motion-studio

Five steps

Make your first film.

Step 1

Install.

You need macOS or Linux, Node 22 or later, ffmpeg with libx264 on your PATH, Python 3.14 and git. Playwright installs its own Chromium.

git clone https://github.com/youneedgreg/motion-studio.git
cd motion-studio
npm ci
npx playwright install chromium
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Python packages go into ./.venv and are always run as .venv/bin/python, so it works even when the venv isn’t activated. Homebrew Python refuses a global pip install.

Step 2

Render your first film.

Florios is a 12 s morph loop. Build its score, render it, then run every check on it:

.venv/bin/python films/florios-morph/score.py
node render.mjs films/florios-morph
.venv/bin/python tools/check.py films/florios-morph

You get out/florios-morph.mp4, a report at out/florios-morph-check.txt and a contact sheet. The score isn’t committed, so run a film’s score.py before its first render. check.py re-renders the film itself.

Other formats, a live preview and the spring tests:

node render.mjs films/safarios-v2 --format 9x16
node render.mjs films/florios-morph --serve
node tools/test_motion.mjs

--serve prints a local address. The preview plays in real time, so it isn’t frame-exact; the render is. What each check proves is on the Tests page.

Step 3

Use /motion-film.

Open the repo in Claude Code and describe the film:

/motion-film a 15 s launch reel for <your product>, 9:16, for an X feed
  1. Brief. It asks for everything missing in one round: goal, audience, brand source, fictional data, references.
  2. Plan. It writes a shot list and waits for your OK.
  3. Build. The score (score.py) and the film (index.html, timeline.json).
  4. Verify. check.py must pass, and it looks at the stills.
  5. Critique. A fresh reviewer subagent scores it, for at most 3 rounds, until every score is 8 or more.
  6. Deliver, with a list of what’s still weak.

The skill lives in .claude/skills/motion-film. Its rule that matters most: never edit a check to make it pass.

Step 4

Write a spec.

For a morph or a UI loop, describe the film as states with numbers, not adjectives. “Grows into a card” can mean anything; “880 × 600, radius 48, at 1.5 s” can’t, and check.py measures it to within ±2 px. Copy the template from spec.md. Florios starts like this:

t (s)Statew × hRadiusFillCursor action
0.00role900 × 200100#0f172aClick "Switch role"
1.50kpi880 × 60048#0f172aClick the role pill
3.00packhouse960 × 26032#10b981Click the KPI card
…
12.00= row 1samesamesame(not rendered)
  • The last row is the first state, at t = D. It is never rendered: frames run 0 … N−1, so the loop has no duplicate frame. Value and velocity must match at the seam.
  • Put states on bar lines, and space them far enough apart for the spring to settle (DEFAULT settles in 0.27 s).
  • Every cursor action is a cue with a sound, and the container responds on the same frame.
  • Radius is at most half the shorter side. Fills are interpolated in OKLab.

Step 5

Run unattended.

Claude can’t wait on a clock, so “carry on if I don’t answer in 10 minutes” doesn’t work. Put this line at the top of the film’s brief instead:

Run mode: unattended

Claude then runs the whole pipeline (plan, build, verify, critique, deliver) without stopping for an OK, and follows two rules:

films/<film>/decisions.md

Ambiguous choices: it makes the choice and writes it down, with when, what it chose, the alternative it rejected and why, and what undoing it would cost.

films/<film>/BLOCKED.md

Run-stoppers it must not guess at: an unclear licence, a need for real customer data, or a check that fails three times. It stops that line of work.

The brief template, with the full unattended rules, is brief.md. Read both files when the run ends.