Studio
A small Remotion studio for motion graphics
The Remotion project for personal motion graphics: a small, consistent component library (src/lib/) plus one folder per video (src/videos/<slug>/), rendered frame by frame from React.
- 14components
- 29motion helpers
- 48design tokens
- 6example videos
- 2engines
On this page
What it is
Deterministic frames
Every frame is a pure function of the frame number: useCurrentFrame, interpolate, spring. No CSS transitions, timers or Math.random, so a still and a render always agree.
Reusable components
14 small components in src/lib: titles, staggered text, captions, counters, charts, masks, sound. Prop-driven, frame-local, safe by default.
One folder per video
Each film lives in src/videos/<slug>/ with its scenes and a timeline file, registered in Root.tsx. Copy the template to start.
Design tokens
Palette, type scale, spacing, safe margins, springs, easing and timing live in one tokens file. Change values there, never inline in a scene.
See it
New here? The illustrated guide walks through the whole workflow.
Quick start
Prerequisites: Node 24 (or 20.19+ / 22.13+), ffmpeg on PATH (for stills contact sheets and sfx).
cd studio
npm install
npm run typecheck # tsc --noEmit
npm run stills -- LibraryShowcase 50,130,245,350,449 # PNGs + contact sheet in out/stills/LibraryShowcase/
npm run render -- LibraryShowcase out/LibraryShowcase.mp4
npm run dev # Remotion Studio preview (long-running; stop with Ctrl+C)Scripts
| Script | What it does |
|---|---|
npm run dev | Remotion Studio, the interactive preview |
npm run render -- <CompId> [out.mp4] | remotion render; e.g. npm run render -- Showcase-Vertical out/vertical.mp4 |
npm run stills -- <CompId> <f1,f2,...> | Bundles once, renders each frame with remotion still to out/stills/<CompId>/f<frame>.png, then tiles them into contact-sheet.png with ffmpeg |
npm run typecheck | tsc --noEmit |
npm run lint | ESLint (Remotion rules, e.g. flags non frame-based animation) + typecheck |
npm run sfx | Regenerates the built-in sound cues in public/sfx/ with ffmpeg |
npm run build | remotion bundle |
npm run upgrade | remotion upgrade (keeps all Remotion packages on one version) |
out/ is gitignored.
Start a new video
- Copy
src/videos/_template/tosrc/videos/<slug>/; renameTemplateto your video's name. - One component per scene (copy
Scene.tsx), one focal point per scene, built fromsrc/libcomponents and tokens only. - Chain scenes in a
<TransitionSeries>, withpresets.wipe() / fade() / circle()andtransitionTiming()between them. Duration = sum of scenes - 15 frames per transition. Keep scene lengths in atimeline.tslike the showcase does. - Register the composition in
src/Root.tsxinside a<Folder>, with inlineid,fps={30},width,height,durationInFrames. - Judge rendered frames, not code:
npm run stills -- <CompId> <scene boundaries, text-heavy frames, mid-transition frames, last frame>, open the PNGs, fix, repeat. Then render the MP4 and check it withffprobe.
The paper trail for each video (brief, storyboard, review) lives in ../projects/<yyyy-mm-dd>-<slug>/, see the repo CLAUDE.md.
Example videos
Read these before writing a scene: each one shows the library in use.
Because
The word is already cut. be stays, fore leaves, cause grows from the blade, and that same 3px mark becomes the frame.
LibraryShowcase
15 s tour of every library component. 1920x1080, 30 fps.
SfxcuespresetstransitionTimingBackgroundBarChartCaptionsCharStagger
Showcase-Vertical
15 s tour of every library component. 1920x1080, 30 fps.
SfxcuespresetstransitionTimingBackgroundBarChartCaptionsCharStagger
TechniquesDemo
11.4 s, 1920x1080, 30 fps. One restrained piece that uses each new helper once: FragmentChain + vw ramp (scene 1), velocity-matched cut, buoyant ease + speedCurve camera + logZoom (scene 2), occluder cut, GhostType (scene 3).
BackgroundCameraFragmentChainGhostTypeSafeAreaShapeMaskbuoyantfont
CapabilityReel
60 seconds, 1920×1080. Play this in Remotion Studio; do not encode an MP4 to preview it.
SfxcuespresetstransitionTimingBackgroundBarChartCaptionsCharStagger
Template
One scene = one component = one focal point. Copy this file per scene (Scene1Hook.tsx, Scene2Problem.tsx, ...). Frames inside a scene start at 0, whatever its position in the video.
New helpers from the research
Techniques that earned a place in the library after the Claude motion research. Each links to the finding it came from.
buoyant()
"Buoyant" ease: a cubic ease-out plus a half-sine bob over the WHOLE lifetime, so a rising element keeps drifting through its peak instead of freezing. Returns 0 at t=0 and exactly 1 at t=1; in between it overshoots by about bob and settles back.
velocityCut()
Velocity-matched cut: the outgoing scene accelerates and is cut at its fastest frame, the incoming scene starts at the same speed and brakes to rest, so momentum crosses the edit. Pair with velocityCutTiming(frames) using the SAME frames. Delay the incomin
FragmentChain
Film copy as fragments, not sentences (F-A2). Recipes: the animation is the verb (words before and after the demo, never describing it), stem plus rotating slot instead of a list. One accent per frame: only the newest fragment is accented.
speedCurve()
Camera driven by speed, not by position keys. The table gives speed as a share of cruise at times (0 at 0 s, 0.5 by 0.6 s, 1.0 by 1.6 s, held, easing to 0.12 at 8.3 s), the shares are smoothstepped between keys, integrated into position, and scaled so the came
GhostType
Typewriter text with a locked layout (F-C14). The FULL text is always laid out in the same inline flow; characters not typed yet are just transparent. Lines can never reflow and the box never changes height while typing. No cursor on purpose (see DESIGN_SYSTEM
Two engines
Remotion (this project) is the default: kinetic type, titles, UI demos, diagrams, data, captions, reels, templates. The HTML engine in skills/motion-studio is for narrated product films where every cut lands on a spoken word. Which one, and what each contains.