Skip to content
Mocky/Docs v0.2
Features

Muse overview

15 min read

The problem Muse solves#

Ask a model for "a modern landing page for a task manager" and you get the same page every time. A purple-to-blue gradient on a dark background, a centred headline, a subheading, two buttons, three identical feature cards with generic icons, and a grey "Trusted by" logo strip.

That is not a rendering fault. It is the centre of gravity of the training data.

Muse moves that centre of gravity. Flip one toggle next to the prompt and Mocky builds an art direction first, then generates the screen from it instead of from the bare prompt.

Without MuseWith Muse
The model invents a paletteA coherent palette, traceable to its references
Generic copy, often in EnglishReal copy, in the language of the request
No images, or flat colour blocksA generated image, served from Mocky's origin
No record of the reasoningA DESIGN-DOSSIER.md citing what inspired what
Nothing prevents clichésA versioned blacklist and a self-critique pass

Muse needs the back end. In pure localStorage mode the toggle is hidden — it must never appear to work while doing nothing.


The four stages#

Muse is a server-side pipeline exposed at POST /api/muse/dossier and orchestrated by server/muse/inspire/engine.js.

#StageModel callsOptional?
1Discover — gather inspirationNoneYes, only when "live inspiration" is ticked
2Distill — turn pages into vocabularyOne per page, at most 6Only runs if Discover found something
3Dossier — write the art directionOne, with one retryNo, but degrades to a deterministic dossier
4Refine — self-critiqueOne score, at most one revisionYes

1. Discover#

The request is classified into tags — landing, saas, restaurant, fintech and so on — by plain keyword matching. No model call, so it is testable and works offline.

Those tags select galleries from a curated registry (sources.json), and any URLs the user pasted are added. The user's URLs come first: their own references always get a slot in the quota.

Pages are fetched through a local, free MCP server — fetcher-mcp, which is Playwright plus Readability.

This stage is optional. It only runs when the user ticks "live inspiration". Otherwise Muse goes straight to the dossier using its offline pattern library.

2. Distill#

Each page becomes a structured InspirationCard: a palette of at most six colours, style adjectives, typographic feel, layout grammar, motion notes, content tone, and clichés to avoid.

The instruction is explicit: extract vocabulary and structural grammar, never copy a specific design, headline or asset. If a field would identify one exact source design, it must be generalised.

3. Dossier#

The design dossier is a strict superset of DESIGN.md. Its ## Tokens section uses the exact DESIGN.md format, so src/lib/design.ts, designTokens.ts and the whole export chain keep working unchanged.

Around that, Muse adds:

SectionContents
## ProductWhat the product is called — the wordmark every screen carries. required in the schema handed to the model, not optional: left optional it was simply omitted, because a model satisfies a schema, it does not volunteer beyond it
## ConceptTwo or three sentences of specific art direction. "Modern, clean, professional" is banned
## ReferencesWhich reference or pattern drove which choice
## TokensPalette of 6 to 8 colours, typography, radius — in DESIGN.md format
## Layout GrammarThe composition rules
## Motion LanguageThe motion vocabulary
## Voice & CopyHeadline, subheadline, three value props, CTA labels, footer — in the language of the request
## Imagery PlanImage slots, each with a ready-to-use generation prompt
## ForbiddenThe clichés to avoid, for this project

Asking the model to cite what drove each choice is not decoration. Traceability is pressure towards originality.

4. Refine#

A cheap model call scores the dossier and revises it at most once. It is optional, silent on failure, and never blocks.

The result is rendered as DESIGN-DOSSIER.md and injected into generation as extraSystem — exactly where DESIGN.md already went (invariant M1).

That slot is shared, but the two documents have opposite lifetimes, and for a long time nothing said so: DESIGN.md is a document the user keeps, while a dossier was written afresh on every generation. A project therefore accumulated one visual language per screen. The dossier is now a candidate direction rather than the authority — resolveDirection keeps the first one and discards the rest, and the later runs exist for their imagery plan, the one part of a dossier that was ever legitimately per-screen. See D11.

Full details are in the inspiration engine page.


How the dossier drives generation#

buildMusePreamble() in src/lib/muse.ts turns the dossier into a preamble. Three additions to the raw Markdown are worth explaining, because each fixes an observed failure.

The palette, restated as classes#

The dossier already lists its colours — as hex values, in prose, inside a long Markdown block. Two things went wrong every time.

The base rules named concrete Tailwind families ("slate, indigo, emerald, amber, rose"), which is a far more actionable instruction than a list of hex values. And nothing said how to apply a hex value with Tailwind.

So the model quietly fell back on indigo-and-slate, and the screens ignored the art direction.

The fix restates each colour as the classes to paste:

- Accent (primary): #cc4b2f → bg-[#cc4b2f] · text-[#cc4b2f] · border-[#cc4b2f]

There is nothing left to translate, and the instruction is now more concrete than the one it has to beat.

The radius, stated without an escape hatch#

RADIUS — use rounded-none as the corner treatment throughout, including when that means square corners. Do not soften it.

Given rounded-none, a model will still round corners "to look more modern" if the sentence leaves any room.

The scroll sequence, stated first#

It comes before the images and in stronger terms, because it decides the shape of the screen rather than filling a slot in it. The hero stops being a block containing a picture and becomes a pinned section the visitor scrolls through.

A model told about it in passing writes a normal hero and drops somewhere below the fold — the one place the effect cannot work.


The three image modes#

The Media library

Every image Muse generates lands in the Media library, shared across projects and searchable by the prompt that made it.

The generated image can serve three different purposes, and it is an explicit choice in the Muse panel.

ModeThe image is…Needs vision?Image profile
contentplaced in the screen as an Nocontent
inspirationshown to the model, never placedYesinspiration
bothshown and placed — one image, one costYescontent

The saved preference is never changed silently. If the active model has no vision, this run degrades to content and the setting stays as it was.

Why inspiration does not generate the same image as content#

It used to, and that is why the mode "often changed nothing".

An inspiration image was generated from the imagery plan's own prompt — the same photographic subject as the hero, merely routed to a different model. That is not an art-direction reference; it is a second hero photo. The model was handed a picture of the product and asked to read its palette and composition from it.

A reference plate is a different object: no subject, no narrative, just palette, material and light. buildInspirationPrompt() builds it from the dossier's own tokens:

An abstract art-direction reference plate. […] Composition: large flat colour fields, generous negative space, one clear focal area, a subtle paper or fabric texture, soft directional light. It is a MOOD BOARD PLATE, not a picture of a product: no people, no objects, no scene, no story.

The canvas records imageRole on the screen — content, inspiration or both. The badge previously said only "Muse image", which made it impossible to verify that inspiration mode had done anything at all.


Designing from your own media#

Selecting an image or a sequence from the library does more than fill a slot. The media is read before the dossier is written, and the dossier is built around it.

There are two channels, because they fail differently.

The palette is measured from the pixels (src/lib/palette.ts). It is exact, and it works on every model.

Asking a vision model to describe the colours fails twice over: half the models people self-host have no vision at all, and the ones that do return names ("warm terracotta") that then have to be guessed back into hex.

The picture itself is attached only when the model can see. It carries what a histogram cannot: subject, composition, density, light.

The measured hex values are declared to override the palettes suggested by patterns and references:

RULES — these override the palettes suggested by any pattern or reference above. […] Do NOT introduce a colour family that is absent from this list. A page whose palette disagrees with its own hero image is the failure this section exists to prevent.

Without that sentence the model politely acknowledges the image and then uses the pattern's indigo anyway — which is exactly the failure the feature exists to fix.

The media block is sanitized before it reaches any prompt or provider. See sanitizeUserMedia() in the inspiration engine page.


Scroll-driven video#

Muse can generate a clip for the hero and let the visitor scrub through it with the scroll wheel. The clip advances frame by frame, pinned full-height, and runs backwards when you scroll up.

It is off by default and asked for explicitly every time. Unlike every other Muse option, this one has a per-use price and adds minutes to a generation. Nobody should discover that by leaving a box ticked.

The clip is never played as a video#

ffmpeg cuts it into a JPEG sequence — 12 fps, 960 px wide, capped at 150 frames — and the screen draws those onto a canvas. Two reasons:

  1. Driving video.currentTime from a scroll handler looks right in a demo and stutters in practice. The browser has to decode from the nearest keyframe on every seek, and a generated clip has very few of them.
  2. Frames are images, so the sandboxed preview needs no media source at all and its CSP does not move an inch to support the feature.

A fixed sample rate, not a fixed count#

Extracting "exactly 60 frames spread over the clip" would require knowing its duration, which means a probe pass. A fixed rate needs nothing.

At 12 fps a 5-second clip gives 60 frames and a 3-second clip gives 36, and both scrub identically because the sequence is driven by progress, not by time. The 150-frame cap is what stops a surprise 30-second clip from writing 400 files.

Two details that matter#

The poster is the first frame copied, not re-encoded. It is what the screen shows before preloading finishes, and it has to be byte-identical to frame 1 so there is no visible jump.

draws the nearest already-loaded frame rather than waiting for all of them. Sixty frames are sixty requests, and blocking the section until the last one lands would leave a hole for a second or two on a cold cache.

Sequences live in data/video-library/, addressed by the SHA-256 of the clip. An identical request reuses the sequence instead of paying for it twice.

A clip chosen from Media can also be steered by the pointer instead of the scroll — the toggle sits under the chosen clip in the Muse panel. The page then gets : the same cut frames, the same loader and painter, but the cursor picks the frame and the drawn position eases towards it, so sixty stills read as a head that turns. It is a box rather than a pinned section, and the prompt tells the model to place it where the request says — a footer whose face follows the mouse cannot be the first thing on the page, which is the one place a scroll sequence insists on. A frame table in the brief (a "gaze map") becomes the map prop, inline, because the sandbox imports no file. The dossier stops calling such a clip the hero for the same reason. A clip Muse generates is always a scroll sequence: it was shot for a hero. Reduced motion, "Sans animation" and a capture hold it on its resting frame.

The clip itself can come from a free stock library. With a Pexels or Pixabay API key saved in Admin (Videos (Muse) → Free stock videos; both keys are free), Media's Videos tab gains a search box. Importing a result is an upload the server downloads: the browser sends the library and an id, the server reads the file's address back from the library's own API, picks the widest mp4 the cutter will keep (1920 px), and cuts it with the same settings as any clip. The author and the source page are stored with it and shown on its card, which both licences ask for. Searches are cached for 24 hours — Pixabay's terms, and both free quotas, want it.

In the Muse panel the clip is a choice, not a checkbox and a link: None, AI-generated or Free. The link to Media was the only way to the free path, and a person who did not think to click it never learned it existed. Free opens Media on its Videos tab, where the search is. Which options an account sees is the administrator's decision (Admin → Videos (Muse) → Video access): one list for generated clips, which cost per use, one for free ones, each "everyone" or named accounts, with Motion Ultra's own control. An account with neither sees no video option, and the server refuses it at /generate, /stock/* and /upload whatever the panel shows — the panel only hides what would be refused.

Photos can be free too. The same keys serve Pexels' and Pixabay's photos, in two places. By hand: Media's Images tab gains the same search, and an imported photo is stored like an upload, with its author's credit shown on its card — pin it and Muse uses it like any picture of yours. Automatically: the composer's Images · AI / Free choice. On Free, the imagery plan's slots are FOUND instead of generated — up to three of them, since a search costs neither a provider's pace nor a price. The dossier writes a searchQuery for each slot (two to four English words), the server returns eight thumbnails, and the model looks at them with the subject and the photos already chosen, and picks one — or none. A first version chose on words alone and put a gas mask on a space-exploration page; a photo that fits nothing is worse than no photo, because the whole page is then built around it. A model without vision takes the search's first result. Nothing found or nothing fitting is a notice, not a failure — and never a quiet fall-back to a paid generation the person turned off. In Inspiration mode, the found photo of the hero is the reference; in the default mode, the thumbnails of the photos found are shown to the model that writes the page, so the page is designed around the pictures it embeds rather than around a dossier written before they were found. Who may use this is its own list, in Admin → Images → Free stock photos.


Anti-slop#

Five mechanisms, all active.

1. A versioned blacklist. server/muse/anti-slop.json names 18 clichés, injected into the dossier prompt and merged with each inspiration card's own avoid list. A sample:

purple-to-blue diagonal gradients on a dark background · three identical feature cards with a generic icon, title and one sentence · a fake logo strip labelled "Trusted by" with grey rectangles · emoji used as UI icons · identical border-radius on every single element

2. Content first. The Voice & Copy section is requested before layout. A page written around its copy does not look like a page with copy poured into it.

3. A post-generation lint. lintSlop() looks for lorem ipsum, "Sample text", "Your text/content here", "Content goes here" and "Placeholder text". The system prompt already forbade them; this makes it enforced. A violation does not discard the screen — it is flagged so you can regenerate.

4. The distinctiveness self-critique. One score, at most one revision.

5. The offline pattern library. 18 hand-written art directions, each with DESIGN.md-compatible token seeds. It is the fallback when live inspiration is unavailable, and blends with the cards otherwise.


Ethics and terms of service#

Muse is built to respect the sites it learns from.

  • No bulk scraping. Only the curated registry pages and the URLs you paste, capped at 6 fetches per run, honouring robots.txt, with an honest User-Agent (Mocky-Muse/…) and a 7-day, text-only cache.
  • No third-party image is ever stored, cached, proxied or displayed. Only Mocky-generated images and text distillations persist — and MuseCache throws if handed anything other than text.
  • Inspiration means vocabulary and structural grammar, never a copy of a specific design.
  • Fetched web content is treated as untrusted data, never as instructions.
  • Every outbound URL passes the SSRF guard, and the default path needs no API key and no account.

These are invariants M2, M4, M5 and M7. See Invariants.


Higgsfield#

Higgsfield.ai has no free API, so it is not integrated. The manual workaround works: generate the image on Higgsfield, download it, drop it into Mocky's media library and pin it.

Muse will use it like any other image — which means measuring its palette and writing the dossier around it.


A note on dependencies#

The MCP SDK pulls a few transitive packages with audit advisories: hono, body-parser, shell-quote and esbuild. All of them are in the SDK's HTTP server transport, which Mocky does not use. Mocky is a stdio client.

CI runs npm audit --omit=dev --audit-level=high, so production dependencies only. Dev-server advisories such as Vite and esbuild do not apply to a deployment, where Express serves the built dist/.

Was this page helpful?
Documentation powered by Lumy llms.txt