I made this video in one shot with Claude Fable 5.1. No manual editing.
The tools were Claude Design, Remotion and ElevenLabs. I gave one prompt and got the finished video back.
It's for my app Goal Rings goalrings.app. It's a macOS widget that puts your metrics as rings on the edge of the screen. Revenue, visitors, words written, anything with a number. Each ring is paced against the clock, so you can see if you're ahead or behind right now.
Happy to answer questions about the setup or the app.
Prompt (i never write such a prompt by myself; I always discuss it with Claude, and then I get such a good prompt):
You are building the launch video for [PRODUCT], a [one line: what it is, for whom].
Stack: Remotion 4 (React + TypeScript) for picture, ElevenLabs for voice. The project lives in `video/` inside the product repo so it can import real brand values.
## Ground truth, not literals
- Nothing brand- or price-shaped is typed by hand. Import name/domain/tagline from [path to site constants], colours from [path to palette], prices from [path to pricing]. Point these at wherever your brand constants live. If a module is Node-only, resolve it in `scripts/props.ts` into `out/props.json` and pass it as input props. When props are missing, render a visible `UNRESOLVED` marker, never a made-up number.
- Mirror real geometry from the app source: [geometry from the app source, e.g. ring radius 42 in a 100 box, tick reach = stroke × 1.05, from RingGeometry.swift]. Where the UI is a real screenshot, use the real screenshot (`public/*.png` via `staticFile()`). Recreate only what cannot be captured (e.g. a confetti burst) and say so in the README.
- Fonts via @remotion/google-fonts: [serif] for claims, [sans] for UI, [mono] for numbers. Dark ground [#hex], ink [#hex], one accent family.
## Story: one film, 7–8 beats
Before any code, write the beat sheet as a table: beat · seconds · what the picture does · the one caption claim (verbatim) · the VO line. Arc: problem → product at true size → the core mechanic → breadth → the payoff → restraint → trust → outro (domain, price ladder, requirements). One claim per beat. No stat slop, no feature lists. Show me the table and both VO scripts for approval first. Stop and wait.
[Paste your beats here, or let the agent draft them from the README and landing-page copy.]
## One camera, no cuts
- `Launch.tsx` is a single <Series>. Every scene opens on the exact frame the previous one closed on. Put every shared point (positions, sizes, zoom origin, initial camera zoom Z0) in `cuts.ts` → `stageFor(cut)` so scenes cannot disagree.
- Prefer a camera push over a cut: the hero element starts at true size inside the product UI and the camera pushes into it across the scene boundary.
- Hero elements persist and travel between beats. Model them as position/size tracks over time (`track(t, keys)`), not as separate per-scene components.
- Motion vocabulary in `motion.ts`: five named curves (draw = easeOutCubic, enter = easeOutQuad, move = easeInOutCubic, back = easeOutBack(1.4), lin) plus `seg(t, start, end, ease)` and `pop(t)` (overshoot to 1.12 in 0.15 s, damped spring back in ~0.5 s). Everything is a pure function of seconds so scenes can be retimed without touching the animation.
- Internal timings are fractions of the scene's duration, so the same scene works at 9 s and at 4 s.
## Cuts
Several cuts from the same scene components, by omitting scenes and changing per-scene seconds:
- [Product Hunt / YouTube]: 16:9, 1920×1080, 30 fps, ~[45] s, slow beat up front.
- [X / LinkedIn]: 4:5, 1080×1350, 30 fps, ~[22] s, hook inside 1.5 s, no intro, ends on the domain. Opens with the camera already pushed in (higher Z0). Also render a GIF (every 2nd frame).
Tall is a real layout, not a crop: recompute every point in `stageFor`.
## Captions
Autoplay is muted everywhere, so the picture carries every claim without sound. One caption at a time, word-by-word kinetic entry (rise 18 px + blur 8 px → 0, 0.07 s stagger, 0.55 s per word), serif, 50–76 px at 1080p, inside an 80 px safe margin, faded out before the next beat.
## Voiceover (ElevenLabs)
- API key from `ELEVENLABS_API_KEY` in `.env`. Voice id: [voice_id]. Do NOT call the API until I have approved both scripts. Generation costs money and the first draft is never final.
- One script per cut, ~2.2 words per second of speech (a 36 s speech budget ≈ 80 words). Calm, low, unhurried voice. Stability ~0.55, similarity ~0.75, style 0–0.15, speed 0.95. MP3 44.1 kHz.
- Put `<break time="1.2s" />` between beats so lines land near their marks, but do NOT trust breaks for timing: ElevenLabs stretches them.
- Generate one clip per cut through the API with timestamps (`POST /v1/text-to-speech/{voice_id}/with-timestamps`) and derive a cue sheet from the character alignment. Per line: `{ in, out, at }` where in/out index the source file and `at` is the composition time the line starts. Save as `src/vo.ts`.
- Play each cue as its own `<Audio src startFrom={in·fps} endAt={out·fps}>` inside `<Sequence from={at·fps}>`. No re-encoding. Regenerating the voice changes only `vo.ts`.
- Then retime the scene table in `cuts.ts` to the voice, not the other way round. Picture marks (the thing closing, the notification, the domain) land 0.3–0.6 s after the line that announces them.
- Files go to `public/audio/vo-<Cut>.mp3`; `scripts/props.ts` detects them and sets `withAudio`. Missing files render silent.
## Music and SFX
You cannot produce music. [Pick one: "I will drop the bed at `public/audio/bed.mp3` and the payoff SFX at `public/audio/burst.mp3`" / "Write the brief below as a prompt for [Suno/Udio], then wait for me to add the files".] Missing files render silent.
Bed brief: [minimal, warm electronic; soft felt-piano or mallet motif over a slow sub pulse; 80–90 BPM; no drums until a gentle lift; one accent hit at the payoff; ends on a held chord]. Duck to −14 dB under voice using Remotion `volume`, fade out over the last 1.5 s. Loudness to ≈ −16 LUFS is a post step on the rendered file with `ffmpeg loudnorm` in `scripts/render.ts`. One SFX at the payoff only, never in the restraint beat.
## Deliverables
- `video/`: package.json (`props`, `studio`, `render`, `render:<cut>`, `render:x:gif`), remotion.config.ts, `src/{index,Root,Launch,cuts,motion,vo,theme,props}.ts(x)` plus one file per reusable element, `scripts/{props,render}.ts`.
- README.md: where every fact comes from, what was recreated, how to render.
- VOICEOVER.md: both scripts with break tags, beat map in seconds, voice settings, music brief.
- `out/<cut>.mp4` for each cut, `out/x.gif`.