| name | remotion-composition-builder |
| description | Authors or extends a Remotion composition (.tsx) wired to BrandConfig + Storyboard + layout spec + motion. The actual builder skill — runs as a Sonnet-tier worker. Triggered after brand, storyboard, designer, and motion-language outputs are ready. Produces the file under remotion-studio/src/compositions/ and registers it in src/Root.tsx. |
| automation | automatic |
| intents | build-composition, write-composition, edit-composition |
remotion-composition-builder
The hands. Reads everything else, writes the TSX.
Triggers
- Wave 4 of a
remotion-orchestrator plan.
- User asks to "build a composition for {topic}" with brand + storyboard already known.
Inputs
All consumed from prior skill outputs in the same job:
BrandConfig (from src/brands/{slug}.ts) — runtime + behaviour: voice, voiceover, motion, audience, channel, forbiddenWords
{slug}.design.md (loaded via loadDesign(slug) from src/brands/loadDesign.ts) — visual tokens: colour, typography, spacing, components, layout, elevation
- Storyboard JSON (from
remotion-screen-storyteller)
- Layout spec (from
remotion-designer) — references design.md tokens by name
- Motion block (part of
BrandConfig)
- Channel spec / aspect ratio (from
remotion-marketing-strategist)
See src/brands/CONTRACT.md for the full ownership boundary between BrandConfig and design.md.
Method
- Locate composition — does
src/compositions/{Name}.tsx exist?
- If yes → patch in place (preserve existing API).
- If no → create new file using
Explainer.tsx as the template.
- Wire imports —
import { brands } from '../brands' for runtime, import { loadDesign, colour, spacing, componentRecipe, typography } from '../design' for visual tokens, motion helpers from ../motion.
- Define schema —
z.object for input props matching the Storyboard shape.
- Compose scenes — one
<Sequence> per storyboard scene. Each scene component reads cfg = brands[props.brand] (for motion/voice/voiceover) AND tokens = loadDesign(props.brand) (for colour/typography/spacing/component recipes), then composes them. Use componentRecipe(tokens, 'cta-primary') to get a fully-resolved CSS-ready object instead of inlining hexes.
- Audio — wire
<Audio src={staticFile(scene.voiceoverAudioPath)} /> per scene if path is set.
- Register in Root — add or update
<Composition id="{Name}" ... /> in src/Root.tsx. durationInFrames = sum(scene.durationSec) * fps.
- File hygiene — composition file ≤500 lines. Extract scenes to
src/compositions/{Name}/scenes/*.tsx if it grows past that.
- Type check — run
npx tsc --noEmit. On red, fix and retry once.
Output
Edited files:
src/compositions/{Name}.tsx (or scene sub-files)
src/Root.tsx (registry entry)
Status report to remotion-orchestrator:
{ "composition": "Explainer", "filesChanged": [...], "tscPassed": true, "ready": true }
Boundaries
- Never inline brand values (hex codes, font names, spacing literals) — always read from
tokens (visual) or cfg (runtime). See CONTRACT.md for which side owns what.
- Never set
durationInFrames without summing storyboard scene durations × fps.
- Never
staticFile() an asset path that doesn't exist — fail loud at build, not silent at render.
- Never bypass
signatureEntry() for entry motion — keeps motion-language consistent.
- Never duplicate a token across
BrandConfig.ts and {slug}.design.md. If the value lives in .design.md, the .ts must not redeclare it.
Reused
Hands off to
remotion-render-pipeline to actually render.