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.txtPython 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-morphYou 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- Brief. It asks for everything missing in one round: goal, audience, brand source, fictional data, references.
- Plan. It writes a shot list and waits for your OK.
- Build. The score (
score.py) and the film (index.html,timeline.json). - Verify.
check.pymust pass, and it looks at the stills. - Critique. A fresh reviewer subagent scores it, for at most 3 rounds, until every score is 8 or more.
- 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) | State | w × h | Radius | Fill | Cursor action |
|---|---|---|---|---|---|
| 0.00 | role | 900 × 200 | 100 | #0f172a | Click "Switch role" |
| 1.50 | kpi | 880 × 600 | 48 | #0f172a | Click the role pill |
| 3.00 | packhouse | 960 × 260 | 32 | #10b981 | Click the KPI card |
| … | |||||
| 12.00 | = row 1 | same | same | same | (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: unattendedClaude 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.