The exact pipeline used to make a music-scored, editorial-grade marketing film from architectural renderings — AI motion, frame-by-frame QC, typography, crossfades and generated music, all orchestrated by Claude Code. You mostly watch.
| Thing | Why | Cost |
|---|---|---|
| Claude Code | The orchestrator — writes & runs every step | your existing plan |
| Google AI Studio key — aistudio.google.com/apikey | Veo 3.1 (image→video) + Lyria (music) | pay-per-use, below |
| Node.js ≥ 22, FFmpeg, Python 3 | HyperFrames editor + QC + scripts | free |
| 6–10 landscape stills | your renders or photos | — |
Video generation cost (the only real cost): Veo 3.1 fast ≈ $0.15/sec, standard ≈ $0.40/sec of video. A reel needs six 8-second clips: ≈ $7 fast or $19 standard. Standard is noticeably better with people and animals; fast is fine for pure scenery. Budget 1.5× for a few regenerations.
Paste this into Claude Code in an empty folder, with your images in ./stills. Adjust the brackets:
Read https://ai-video-playbook.pages.dev/ and follow its Recipe exactly. Make a 30-second cinematic marketing reel from the images in ./stills. Subject: [our seaside guesthouse in Crete / whatever it is]. Mood: [calm, warm, editorial luxury]. Brand name for title cards: [NAME]. Put [people / our dog / nothing alive] in the scenes. My Google AI Studio key is in $GOOGLE_API_KEY. Use Veo 3.1 [fast|standard]. QC every clip visually before using it.
Center-crop every image to exactly 1920×1080 (PIL or ImageMagick). Veo uses the image as the literal first frame — off aspect ratios get cropped unpredictably.
One REST call per image — no SDK needed:
apt: POST https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:predictLongRunning
MODEL: veo-3.1-fast-generate-preview | veo-3.1-generate-preview
header: x-goog-api-key: $GOOGLE_API_KEY
{
"instances": [{
"prompt": STYLE + " " + MOTION,
"image": {"bytesBase64Encoded": "<b64 of jpg>", "mimeType": "image/jpeg"}
}],
"parameters": {
"aspectRatio": "16:9",
"resolution": "1080p",
"durationSeconds": 8,
"negativePrompt": "text, watermark, captions, distortion, warping,
morphing architecture, flicker, extra limbs,
deformed faces, cartoon",
"personGeneration": "allow_all"
}
}
→ returns {"name": "models/.../operations/xyz"}
→ poll GET /v1beta/{name} until done:true
→ video uri at response.generateVideoResponse.generatedSamples[0].video.uri
→ download that uri WITH the same x-goog-api-key header
| Gotcha | Fix |
|---|---|
1080p is not supported for a duration of 6 seconds | Always durationSeconds: 8 at 1080p |
| HTTP 429 on submit | Stagger submissions ~5s apart; retry with growing backoff (20s, 40s…) |
| Download URI returns 403 | Send the x-goog-api-key header on the download request too |
| Camera wanders in interior shots | The locked-off tripod phrase above |
| Subjects “materialize” mid-shot (ghost dissolve) | Don’t discard — trim past it in the edit (Step 5, data-media-start) |
For each clip, extract a 3-frame contact strip and actually look at it (Claude: use your vision):
ffmpeg -i clip.mp4 \ -vf "select='eq(n,0)+eq(t,4)+gte(t,7.5)',tile=3x1,scale=1920:-1" \ -frames:v 1 qc.jpg
Reject and regenerate (or trim) anything with warped architecture, morphing subjects, ugly camera drift, or ghosting. For clips where subjects enter over time, also strip t=1,2,3s to confirm the entry looks natural. This QC loop is the difference between “AI slop” and “how did you make this?”
Lyria RealTime with the same Google key (pip install google-genai) — stream ~36s to WAV. Prompt like “serene tropical luxury spa ambient, warm felt piano, soft kalimba, airy pads, sparse organic percussion”, with bpm≈88, scale=MAJOR, brightness≈0.55, density≈0.35, negative “vocals, heavy drums, EDM”. Fallback: any licensed 36s track, or local MusicGen.
npx hyperframes init reel --example blank --non-interactive, then author index.html:
<video> clips as direct children of the composition root, alternating data-track-index 0/1 (DOM order = stacking order); text overlays as <section class="clip"> on track 2; one <audio> music element on track 10. Six ~5.3s scenes → cuts at 5.3 / 10.1 / 14.9 / 19.7 / 25.0.tl.set(old, {opacity:0}, T+1.0) after each fade — the linter requires the hard-kill.data-media-start="3".volume on the timeline (in 0.8s, out over the final 2.4s).npx hyperframes lint → validate → inspect, all clean, before rendering.npx hyperframes render --quality high --output reel.mp4
# then look at a 6-frame contact sheet of the final render (esp. each transition), then:
ffmpeg -i reel.mp4 -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
-c:a aac -b:a 192k -movflags +faststart Final.mp4
ffmpeg -i reel.mp4 -c:v libx264 -crf 26 -vf scale=1280:720 \
-c:a aac -b:a 128k -movflags +faststart Final-social.mp4
| Scene | Content | Overlay |
|---|---|---|
| 1 · 0–5s | Hero exterior, life entering frame | Brand wordmark + one-line promise |
| 2 | Approach / entrance | The headline number (size, location…) |
| 3 | Best interior, daily life | “A home that opens to its garden” |
| 4 | Calm beat — no people | Wellbeing / craft line |
| 5 | Garden, family, animals | The emotional line |
| 6 · 25–30s | Dusk interior, warm lamps | Tagline + wordmark + location = end card |
Total spend for the villa reel this playbook comes from: ~$26 of Veo across two full iterations, one afternoon, zero manual editing.