REMOTION STARTER KIT FOR CLAUDE Motion graphics and explainer videos made with code, by Claude. Written November 2026. The setup and helper code were tested with Remotion 4.0.535 on Windows 11. Everything here came from real videos: the rules, the craft numbers, and the traps that each cost a render. ===================================================================================================== FOR THE HUMAN (read this part, then give the whole file to Claude) ===================================================================================================== What you need - Node.js 20 or newer (the LTS from nodejs.org). - Claude Code (desktop app or terminal). - About 600 MB of disk for one project (Remotion's packages plus its own headless Chrome). - Optional, recommended: a full FFmpeg on your PATH, used for CHECKING videos (black frames, loudness, contact sheets). Windows: winget install Gyan.FFmpeg. Mac: brew install ffmpeg. Remotion carries its own small FFmpeg for rendering, so rendering works without it. - Licence: Remotion is free for individuals and for companies of up to 3 people. Bigger companies need a paid company licence (see remotion.pro). Check which one fits you before you use it for client work. How to start 1. Make an EMPTY folder with a SHORT path, for example C:\videos\remotion (on Windows a deep folder path breaks rendering, see Part H, trap 1). 2. Put this file in it. 3. Open Claude Code in that folder and send: Read REMOTION_STARTER_FOR_CLAUDE.txt completely. Do PART A (setup), then fill MY SETUP with me. From now on follow KNOWLEDGE.md for every video. 4. Answer Claude's MY SETUP questions (your fps, sizes, editor, fonts, colours). You do not need any other file. Things that help if you have them: your fonts (.ttf / .woff2), your logo as an SVG, music / voice files, and a few stills of videos whose look you like (Claude can look at pictures). Tips for working with it - Ask for STILLS first ("show me 6 stills of the key moments"), video only after you like the stills. Stills take seconds, a full render takes minutes, and changes are cheap at the still stage. - Say what you dislike in plain words ("too bouncy", "text leaves too fast", "too busy behind the title"). Claude writes it into KNOWLEDGE.md so the next video already knows. That file growing is the whole trick. - Give it the exact words for every title, name and link. It must never invent them. ===================================================================================================== PART A. SETUP (Claude: do these steps in order, report each result) ===================================================================================================== A1. Check the tools: `node -v` (must be 20 or newer). Check the folder path is short (under ~60 characters). A2. Create the project in this folder (do not use a template; the files below are all that is needed): npm init -y npm install --save-exact remotion @remotion/cli @remotion/renderer @remotion/bundler @remotion/media @remotion/transitions @remotion/paths @remotion/zod-types zod react react-dom npm install --save-exact --save-dev typescript @types/react Then edit package.json: keep "private": true, remove "type" and "main" if npm added them, and set "scripts": { "studio": "remotion studio src/index.ts", "typecheck": "tsc -p ." } RULE: every `remotion` and `@remotion/*` package must be on the SAME exact version. Mixing versions breaks things. To upgrade later, use `npx remotion upgrade` (moves them together), never one package alone. Add more @remotion packages only when a video needs them, always with --save-exact and the same version. A3. Download Remotion's own headless Chrome once (~270 MB, goes into node_modules/.remotion): npx remotion browser ensure A4. Create these files with the exact content from PART I: tsconfig.json, src/index.ts, src/Root.tsx, src/TitleCard.tsx, src/voice.ts, render.mjs, stills.mjs, temp_clean.mjs and the empty folders public/ (assets: fonts, pictures, audio) and out/ (renders). A5. Smoke test (all must pass): npm run typecheck -> prints nothing node render.mjs TitleCard out/f45.png --still 45 -> a PNG; open it and LOOK at it node render.mjs TitleCard out/tc.mp4 -> a 4 s mp4, ffprobe line shows 1920x1080, 30/1, 4.0 s node stills.mjs TitleCard out/st 0.3 1.0 3.55 -> three PNGs If a render fails with "spawn ... chrome-headless-shell.exe ENOENT", the folder path is too long (Part H, 1). A6. Write the knowledge into the project: - KNOWLEDGE.md = Parts B to H of this file, copied as they are. It is a LIVING file: add lessons to it. - CLAUDE.md = the text in A7 below (Claude Code loads it in every chat in this folder). A7. CLAUDE.md for the project (fill the MY SETUP lines WITH the human, ask, do not guess): # Remotion videos - Before writing or changing any composition, read KNOWLEDGE.md (rules, craft, routine, traps). - Render with render.mjs. Before showing anything, render stills with stills.mjs and LOOK at them. - Never invent words, names, links, numbers or claims for a video. They come from me. - Each delivered version is kept (v1, v2, ...). Never overwrite a file my editor may link to. - A lesson (a trap, a fix, something I liked or disliked and why) goes into KNOWLEDGE.md THE MOMENT it is learned, one or two lines in the right part, with the reason. Not at the end of the session. ## MY SETUP - Frame rate: __ fps (30 is the common choice for screen and explainer videos) - Long-form timeline size: __ (1920x1080 / 2560x1440 / 3840x2160). Designs stay in 1920x1080 units and render with --1440 or --4k to match. - Vertical Shorts / Reels: yes / no (1080x1920, a SEPARATE composition, never a crop) - Editor: __ (CapCut / Premiere / Resolve / Final Cut / none) - Transparent overlays wanted: yes / no (full-frame by default; alpha only when asked) - Fonts: titles __, text __ (files in public/fonts) - Colours: background __, accent __, text __ - Where finished videos go: __ - Taste notes: (filled in as we go: what I liked, what I rejected) ===================================================================================================== PART B. THE RULES OF REMOTION (code that renders right) ===================================================================================================== The idea: a video is a React component. Every value is a function of the frame number (`useCurrentFrame()`), so any frame can be rendered on its own, in any order, on many threads. Anything that is not a function of the frame (CSS animations, timers, the clock, random numbers, network calls) breaks the render. B1. Animation - Every animation comes from `useCurrentFrame()` + `interpolate()` / `spring()`. - NO CSS transitions, NO CSS @keyframes animations, NO Tailwind `animate-*`. They do not render. - `interpolate` ALWAYS clamped: `{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }`, or the value keeps going past the range. An easing array has (number of points - 1) items. - The input range must be strictly increasing: a 0-length ramp (a == b) THROWS. Guard `fade > 0`. - `Math.random()` is forbidden: use `random("some-seed")` from remotion (same value on every render). - Write all timings in SECONDS (`t = frame / fps`) so a piece can render at another fps. - Prefer the separate CSS properties `scale`, `translate`, `rotate` over one long `transform` string. - A spring drives TRANSFORMS only (position, scale). Opacity gets its own clamped ramp, because a spring can overshoot past 1. B2. Time and sequencing - ``: inside it, `useCurrentFrame()` starts at 0 when the Sequence starts. Always set `premountFor` (loads media a second early, avoids a flash). Order of operations: from -> trimBefore -> durationInFrames -> playbackRate -> loop. - `` + ``: scenes back to back; a negative offset overlaps. Never set `from` inside a Series. - ``, `` to hold a frame. - Transitions (`@remotion/transitions`): TOTAL LENGTH = sum(sequences) - sum(transitions) (90 + 90 with a 20-frame transition = 160). A transition may not be longer than its neighbours. Presentations: fade, slide, wipe, flip, clockWipe, iris, none; a custom one is a component that gets `presentationDirection` ("entering" / "exiting") and `presentationProgress` (0..1). Some newer transitions draw through HTML-in-canvas: they render fine but need a Chrome flag to PREVIEW in the Studio (the docs say which). - One component and one file per scene; chain scenes with Series / TransitionSeries. B3. One template, many videos (props) - A zod schema on the Composition = an edit form in the Studio's right panel + checked `--props` on the CLI. Top level must be `z.object()`. `@remotion/zod-types` adds `zColor()` (a colour picker), `zTextarea()`. - `defaultProps` inline and JSON-serializable. - `calculateMetadata` = duration / size from the props or from data (async allowed): const calc: CalculateMetadataFunction = async ({ props }) => ({ durationInFrames: props.steps.length * 90, }); It can also return defaultCodec / defaultPixelFormat / defaultOutName. This is how one composition makes a video per data file (read a JSON, count the items, set the length). - CLI: `--props=file.json` is the safe way on Windows (inline JSON quoting through cmd is fragile; render.mjs runs without a shell, so inline JSON works there too). B4. Text and fonts - Fonts must be loaded BEFORE the first frame renders, or frame 0 shows a fallback font: `npm i --save-exact @remotion/fonts` (same version as remotion), then in a file imported once by Root.tsx: import { loadFont } from "@remotion/fonts"; import { staticFile } from "remotion"; loadFont({ family: "Title", url: staticFile("fonts/Title-ExtraBold.ttf"), weight: "800" }); Without the package: `new FontFace(...)`, `await face.load()`, `document.fonts.add(face)` inside a `delayRender()` / `continueRender()` pair. Google Fonts: `@remotion/google-fonts/` with `loadFont("normal", { weights: ["400", "700"], subsets: ["latin"] })` (always pass weights + subsets). - Load all fonts in ONE shared module, imported once, not per composition. - Measure text instead of guessing: `@remotion/layout-utils` (`fitText`, `measureText`, `fitTextOnNLines`), or `canvas.measureText` after the fonts are loaded. DOM measurements: divide `getBoundingClientRect()` by `useCurrentScale()`. - Useful packages: `@remotion/rounded-text-box` (TikTok-style caption boxes), `@remotion/rough-notation` (hand-drawn circle / box / underline driven by a 0..1 progress, great for "look here"). B5. Media - Use `