a Claude Code plugin · v1.1.0

Sixteen elements.
Eight compositions.
Zero keyframes.

Cinemorph composes Vite + React decks where every persistent element carries a stable id and morphs cleanly between stages via shared-layout (FLIP) transitions. Watch the same sixteen primitives rearrange themselves below — no per-element animations defined, just eight different layouts of the same source.

01/ 08 seed
00:00 / 00:30

Each stage is one entry in a stages.ts file. Sixteen element ids carry across all eight — no per-element animation defined anywhere. Framer Motion's shared-layout algorithm computes every transition automatically.

three production targets, one source of truth

What Cinemorph makes

Live deck

Vite + React + motion/react

Hot reload. Keyboard nav. ?dev=1 for the timeline scrubber. The deck is the artefact — open it in Chrome on the projector and click through with arrow keys.

bun install && bun run dev
# → localhost:5173
MP4

Playwright + ffmpeg

30-second to 5-minute cinematic film. Audio bus mixes a music bed, voiceover, and SFX cues. Smear cuts on real scene transitions. aeBounce on every element that lands with weight.

/cinemorph video --out launch.mp4
PPTX

Native Microsoft Morph

Editable PowerPoint with native Morph transitions between slides. For stakeholders that don't ship from a browser. Compatible with Office 2019+ and Microsoft 365.

/cinemorph pptx --out pitch.pptx

the pipeline

How it works

1

Token merger

--theme < --tokens < --reference < --prompt. Later layers win on fields they provide. Missing fields trigger a clarifying question.

2

Composer

Spawns the claude CLI on PATH. System prompt is agents/morph-composer.md. Emits one JSON object — stagesTs + dataTs.

3

Scaffold renderer

Vite + React 18 + Tailwind + motion/react. <LayoutGroup> wraps the persistent element layer. Every primitive carries a stable layoutId.

4

Three exports

Live deck, MP4 (Playwright + ffmpeg), or PPTX with native Microsoft Morph. One source of truth.

The morph engine, in a screenful of code

stages.ts · composer-generated
export const STAGES: Stage[] = [
  {
    id: 'hook',
    elements: {
      wordmark: { pos: { left: '50%', top: '50%', width: '20%' }, shape: 'wordmark' },
      kpi:      { pos: { left: '12%', top: '24%' }, shape: 'kpi' },
      pillar:   { pos: { left: '88%', top: '24%' }, shape: 'pillar' },
    },
  },
  {
    id: 'reveal',
    elements: {
      wordmark: { pos: { left: '50%', top: '14%', width: '12%' }, shape: 'wordmark' },
      kpi:      { pos: { left: '20%', top: '60%' }, shape: 'kpi' },
      pillar:   { pos: { left: '80%', top: '60%' }, shape: 'pillar' },
      pipeline: { pos: { left: '50%', top: '50%' }, shape: 'pipeline' },
    },
  },
];

Hook → reveal: the wordmark shrinks and slides up; the kpi and pillar drift outward; the pipeline fades in. One synchronised 700 ms transition. No code per element.

earned the hard way

Cinematic-launch-video lessons baked in

For 30-second-style films the composer applies eight rules below. Every one was earned by shipping a real video and getting each detail wrong before getting it right.

One master RAF clock

Writes a single elapsedMs. Phases are non-overlapping windows. SMEAR_MS = 350 cross-fades at boundaries.

aeBounce easing

For elements that land with weight. Dan Ebberts' AE bounce ported to TypeScript.

Whooshes only on real cuts

Never on internal animation events. They lead the visual by 300–500 ms so the sweep + the new scene's snap reads as one cinematic cut.

Audio-bus race-condition fix

A stoppedSfxRef: Set<number> ensures each cue's stopAtMs runs exactly once. The single most painful bug in the build.

Browser autoplay gate

Minimal transparent click area + 64 px play icon. Pre-warm the SFX pool on first interaction.

ffmpeg -ss seek-mode trap

Input-seek (-ss before -i) when trimming with afade. Output-seek can produce silent files that pass duration checks.

Volume hierarchy

Music 0.20–0.25 baseline, ducks to 0.05–0.08 under VO. Whooshes 0.30–0.40, POPs 0.70–0.80, BLIPs 0.30–0.45 tapered, VO 0.85–0.95.

Dev scrubber

?dev=1 appends a frame-accurate timeline so a reviewer can give precise timestamp feedback during iteration.

starter decks

Twelve bundled examples

Generate any of these with /cinemorph new --from-example <name>. Each ships its own brief.md as documentation by example.

install

Three commands

  1. Clone & install the plugin.
    # into your Claude Code plugin directory
    git clone https://github.com/LucasDuys/cinemorph.git
    cp -r cinemorph/plugin ~/.claude/plugins/cinemorph
  2. Restart Claude Code, then generate a deck.
    # In a Claude Code chat:
    /cinemorph new --from-example cinemorph-about-30s --out my-cinemorph-film
  3. Run it.
    cd my-cinemorph-film
    bun install && bun run dev   # → http://localhost:5173
    #                              ?dev=1 for the timeline scrubber

Auth is your Claude Code subscription via the claude CLI on PATH. No ANTHROPIC_API_KEY required.