Purpose
Render motion graphics from an approved storyboard.html/manifest.json (produced by create-motion-graphics-storyboard) using Remotion. Remotion is React-based programmatic video: text is real DOM, timing is deterministic frame math, and animation primitives map cleanly onto Spring + interpolate. This avoids the text-hallucination and timing-drift failure modes of pure video-gen models.
The atom can scaffold a fresh Remotion project on first run or reuse an existing one in the run folder. It generates one Scene component per storyboard scene, wires them into a root composition with <Series>, and renders to clips/scene-NN.mp4 (per-scene) plus optionally a stitched master.
Inputs
- A storyboard manifest (
scenesarray withid,start,end,copy,elements: [{kind, primitive, easing, ...}],music_cue) and optionalsharedLayersarray describing elements that persist across scene boundaries (see storyboard atom for schema). - A brand-board manifest (palette + Google Fonts + aesthetic direction).
- Output directory (the run folder). The Remotion project lives at
<run>/remotion/. - Optional
--scene <id>to re-render only one scene. - Optional
--fps(default 30) and aspect ratio (default 9:16, 1080x1920).
Workflow
- Read the storyboard and brand-board manifests.
- If
<run>/remotion/does not exist, runscripts/init-remotion-project.sh <run>/remotionto bootstrap the project (npm i remotion @remotion/cli @remotion/google-fonts @remotion/bundlerplus dev deps). - Generate
src/Root.tsxexporting a<Composition>with idMotionGraphic, width/height/fps from inputs. The composition body is an<AbsoluteFill>containing a<Series>of scene components AND, on top, a<SharedLayers>overlay component that interpolates each shared layer's properties across global time using the storyboard'ssharedLayerskeyframes. The shared overlay readsuseCurrentFrame()at the composition level so a single circle can smoothly translate from scene 1 → scene 2. - For each storyboard scene, render
references/scene-template.tsxintosrc/scenes/Scene{NN}.tsx, parameterized with the scene'selementsarray. The template usesuseCurrentFrame,spring, andinterpolateto implement each supported primitive — seereferences/remotion-primitives.md. Multi-element scenes compose elements inside one<AbsoluteFill>, each with its own primitive and absolute or flex layout per the storyboard. - Load brand fonts via
@remotion/google-fontsnamed imports based on the brand-board manifest. - For each scene (or the single
--scene), runscripts/render-composition.sh <run>/remotion <scene-id> <out-path>which wrapsnpx remotion renderwith H.264 defaults. - Optionally render the full composition to
<run>/clips/master-silent.mp4. - Write
manifest.jsonlisting every rendered clip, the Remotion project path, and font/composition settings. Writeverification.md.
Output
<run>/remotion/— the Remotion project (reusable across re-rolls).<run>/clips/scene-NN.mp4— silent per-scene renders.- Optional
<run>/clips/master-silent.mp4— stitched silent master. manifest.jsonandverification.md.
Quality Checks
- Every scene listed in the storyboard has a corresponding rendered clip.
- Clip dimensions and duration match the storyboard (within one frame).
- On-screen text matches storyboard copy exactly (no truncation, no font fallback).
- No raster glyphs — text is real DOM rendered by Remotion.
- All Google Fonts loaded successfully (no system-font fallback).
- Safe-area at animation peak. For every
scale-bounce/pop-inscene, sample the spring peak (~frame 12–18 at 30fps with the default config — overshoot is ~1.15x fordamping: 12). Confirm no text glyph crosses outside the 88% safe area. Springs overshoot; sizing for the static frame is wrong. - Safe-area at scene mid-frame. For every scene, sample the mid-frame and confirm no text or critical element exits the 88% safe area.
- Every scene container in the generated TSX sets
maxWidth: "88%"on the text element (or equivalent padding) so overshoots clip safely rather than pushing offscreen.
Failure Modes
- Node or npm not available — surface and mark blocked; do not silently skip.
- Remotion install fails — surface the npm error.
- Storyboard scene uses a primitive not implemented in
scene-template.tsx— extend the template or fall back to the nearest primitive and surface the substitution. - Font fails to load — fall back to the nearest brand-board font and flag in the manifest.
- Render times out — re-run only the affected scene with
--scene <id>. - Text clips the canvas at the spring peak. Symptom: scene looks fine at frame 0 and frame 30 but text overshoots horizontally around frame 12–18. Fix: reduce the scene's font-size by 15–20% (or set
maxWidth: "82%"on the text element) and re-render only that scene.