Back to Home

Muse

A visual editor for running React apps. Point at an element, shape it, and Muse rewrites the real source. This page runs a live copy, and on a desktop nearly everything on it is editable.

Role
Design Engineer
Team
Solo
Timeline
May 2026 – Present
Tools
TypeScriptReactBabel

Every visual change in code costs the same detour.

You can see the fix, but you have to make it in a file. Design tools let you shape the thing in front of you. Code makes you describe it from memory.

Muse is an overlay for your dev server. Select an element, adjust it, and Muse writes the change to the source that produced it. Anything it can’t write gets flagged for your coding agent.

Try it before you read how it works.

Edit anything belowThe demo cardThe demo cardLiveLive on desktopDemo offline

New release

Make this card your own.

Press R to turn on Canvas Mode, then double-click to rewrite this text. Select the heading and scrub its size. Recolor a button, or drag the two to swap them. Each gesture is one Canvas transform, and edits come back on refresh.On a desktop each of these is one gesture: rewrite this text, scrub the heading’s size, recolor a button, swap the two buttons.The live demo is offline in this browser; the clips below show the same gestures.

A click resolves to the line of source that rendered it.

React 19 removed the fiber field that carried source locations, so I stamp them in at build time. A Babel plugin writes data-muse-loc="file:line:col" on every JSX element, and Muse reads it straight off the DOM.

The plugin never touches React internals, so it works on React 18 and 19 in any bundler that runs Babel. Turbopack can’t load SWC plugins, so Babel is the way in.

Elements · data-muse-locexample
This is a representative stamp; the live read was not available in this view. A click normally resolves to that file:line:column, which is how the edit lands on the right JSX.

Every gesture is one fixed transform.

Canvas probes before it writes. The offer and the write come from the same analysis, so they can’t disagree. Each edit is a deterministic Babel AST rewrite.

The same gesture lands differently depending on how an element is styled: a Tailwind class, an inline style object, a CSS Module rule, or a styled-components template. If the value comes from a design token, Muse edits the CSS variable where it’s defined instead of stamping a literal over it.

Reorder
Reorder · the source diffMuseSampleBlock.tsx
Dragging the second button above the first is one node swap in the parent’s JSX children (the diff is simplified here for clarity). Canvas re-emits with the original indentation, so the change is one moved block instead of a reformat.

The hardest design call was teaching Canvas when to refuse.

A list rendered from a .map() can’t become one provable transform, because moving the JSX moves nothing. Canvas could preview the attempt or show a confidence score, but a tool you have to re-check after every edit defeats the point of direct manipulation. So it refuses and tells you why. The actual message, word for word: “these are generated from data — reorder the list instead.”

The refusal
Canvas names the owning utility, stops, and offers the flag.

Take the offer on a refusal, or Shift-click any element, and you get a work-order. A small MCP server routes it to your own coding agent.

Flag work-orderexample
{
  "id": "f_1",
  "comment": "make this card a two-column layout on desktop",
  "status": "open",
  "file": "src/components/case-study/MuseSampleBlock.tsx",
  "line": 56,
  "column": 6,
  "tag": "div",
  "className": "p-8 md:p-10",
  "text": "Make this card your own."
}
This is the payload muse-mcp hands to your coding agent. It gets a precise work-order rather than a prompt. Shift-click any element in the card above and your own flag appears here. On a desktop, Shift-clicking any element in the card above drops your own flag here.

A design pass comes out as a reviewable pull request.

One click turns a session of hand edits into a branch and a pull request. The diff is built with git plumbing against a temporary index, so your working tree is never touched.

Share
Here’s a real PR Muse opened against its own repo, a text edit on its docs site: github.com/thientran01/Muse/pull/117

Muse is shipped, and it helped build this page.

npm i -D @thientran01/muse

Muse installs from npm, and the flag server runs with npx muse-mcp. If you’d rather have the engine in your own tree to customize, npx skills add thientran01/Muse vendors it in. The code is public at github.com/thientran01/Muse.

I kept Muse installed while I built this case study and made a stretch of its edits using it. The page’s styling patterns also surfaced engine bugs Muse’s own demo never hit, like a reorder that landed a beat late until I measured the geometry mid-drag.

Where Muse goes next.

Canvas handles the edits it can prove, and everything else becomes a flag. Making Muse more useful means moving that line outward: teaching the work-order to carry edits Canvas still refuses, like extracting a component into its own file. Real work is what keeps showing me where the line should go next.

Next Case Study

SETlib