Purpose
The production line for the proof-then-recipe format: show an impressive result, then reveal the AI pipeline that produced it.
Structure (always two acts):
- The flex (proof hook, ~3–5s): a fast beat-cut montage of the most scroll-stopping moments from a finished brand film, riding the film's own music, with a small context caption ("we made this ad."). No VO.
- The recipe (explainer, ~22–30s): a hard record-scratch cut to the felt-Goose mascot (lip-synced), who names the stack one tool per beat — orchestration → video model → voice → editor → "one afternoon, one person" — over brand-colored tool cards, with a pop SFX on every beat and word-level karaoke captions. Closes on a follow CTA.
The reusable abstraction: point it at (a) a finished deliverable to use as proof and (b) a tool-stack to credit, and it returns the reel. First shipped as gooseworks-ads/how-we-made-this-touchland/.
NOT the right molecule for:
- A plain mascot explainer with no "proof" footage — use
talking-goose-creator-video. - A straight creator-grammar recut of a single source video — use
ugc-ad/recut-video-to-creator-grammar. - A long-form (>45s) walkthrough — this format lives or dies on pace; keep it under ~35s.
Inputs
| Input | Required | Notes |
|---|---|---|
proof_film |
yes | Path to the finished brand film used as the proof hook. Must be visually strong — the "wow" has to land in under 5s. Any aspect; it is scaled/cropped to 1080×1920. |
tool_stack |
yes | Ordered list of {role, name, brand_color, badge} (e.g. orchestration=Claude Code, video=Seedance, voice=ElevenLabs, editor=FFmpeg). Drives the tool cards + the VO rundown. |
voice |
yes | ElevenLabs voice id + model. Default Goose character voice QzTKubutNn9TjrB7Xb2Q (eleven_v3). Must be listed in VOICES.md or explicitly approved. |
character |
no | Lip-sync anchor still (neutral closed-mouth, head-and-shoulders). Default felt Goose; reuse a materialized anchor PNG (LFS pointers will not lip-sync). |
script |
no | Override the auto-generated VO. If omitted, the molecule writes the pivot → tool-rundown → CTA script from tool_stack. |
hook_caption |
no | Lower-third context caption over the proof montage. Default "we made this ad." Keep it from spoiling the explainer's reveal. |
output_dir |
yes | Project folder (canonical 5-folder layout). |
auto |
no | bool, default false. Pass through the script + composite gates with prior preferences. |
Composed Atoms
atoms/voiceover/create-voiceover-elevenlabs—eleven_v3VO with-timestamps for the recipe narration (approved voice only).atoms/source/transcribe-audio-fal— word-level timestamps on the final (speed-tuned) VO; drives beat boundaries + caption cues.atoms/lipsync/create-lipsync-veed-fal— one Veed Fabric 1.0 call on the full VO to lip-sync the mascot (the PIP source).atoms/captions/burn-in-captions— word-level karaoke reference (ASS + libass); here captions are dark-ink + brand-color karaoke placed in the band above the circular cam.atoms/overlays/render-video-overlay-track— Playwright/Chromium HTML→PNG renderer used for the premium overlay cards (real brand logos, glass cards, step bars).atoms/assembly/stitch-videos-ffmpeg— concat the proof hook + the explainer into the master.atoms/review/watch— mandatory frame + transcript self-QC on the final master (VIDEO_GEN_RULES.mdrule 3).molecules/talking-goose-creator-video— the mascot-explainer lineage (VO + lip-sync + pop-SFX + brand-name caption patching). This molecule supersedes its flat overlay/rectangle-PIP look with premium HTML cards + a circular Loom-style cam.
Workflow
Step 1 — Intake + project setup
Validate proof_film exists and voice is in VOICES.md. Create the 5-folder layout under output_dir; copy proof_film to source/. Write script/script-locked.md.
Step 2 — Cut the proof hook (conform, local)
Sample frames across proof_film; pick the 4–6 most arresting ~0.7–0.9s moments. Trim each (scale to 1080:1920:force_original_aspect_ratio=increase,crop=1080:1920, fps=30), hard-concat, and lay a single continuous music slice from the film (its climax) as the bed with a short fade. Overlay the hook_caption (looped image + alpha fade) only over a clean cut and remove it before any text-bearing end card.
Step 3 — Write + lock the VO script (GATE)
Author the recipe VO: pivot line ("Want to make ads like these? Here's how.") → one line per tool in tool_stack → "one afternoon, one person" → CTA. Save clean + eleven_v3-tagged versions. Human-approve before spending on VO/lip-sync.
Step 4 — Generate + pace the VO
Generate eleven_v3 with-timestamps. eleven_v3 reads slow/dramatic — measure the result and apply atempo (~1.4–1.5) to hit a snappy ~30s and to cut lip-sync cost. Re-run transcribe-audio-fal on the paced VO for accurate word timings. Verify the transcript matches the script with no audio-tag leakage.
Step 5 — SFX
Generate a pop (card snap), a record-scratch (the turn), and an optional whoosh via ElevenLabs sound-generation.
Step 6 — Overlays (premium HTML/CSS via Playwright)
Render each beat as two layers at 1080×1920 so content can animate over a stable bg:
- bg layer (opaque): light/studio cream gradient + a faint brand-tint glow + step bars (01→04) +
@handlewatermark. - content layer (transparent,
omitBackground): the card/hero only.
Tool cards use real brand logos — fetch official SVG marks from cdn.simpleicons.org/<slug> (claude, elevenlabs, ffmpeg, anthropic, bytedance) and CSS-filter them white on a brand-color app-tile. For a tool with no simpleicons entry (e.g. Seedance), use a clean inline glyph (play triangle). Beats: kinetic title, "no camera/studio/crew" strikes, one tool card per tool_stack entry (logo tile + role eyebrow + name + descriptor), "one afternoon / one person", and a centered-Follow CTA. Type: Space Grotesk + Inter (Google Fonts). Render via a small Playwright script (render.js) at 2× then downscale for crispness.
Step 7 — Lip-sync the mascot
One create-lipsync-veed-fal call (image=character, audio=paced VO, 720p). Reuse a materialized anchor; never feed a 130-byte LFS pointer.
Step 8 — Composite the explainer (with motion)
Per beat (timings from the word timestamps): show the static bg for its duration and overlay the content layer with a subtle entrance — fade-in ~0.24s + slide-up ~18px. Concat beats → bg-track. Then composite the circular Loom cam: crop the mascot's face from the lip-sync clip, mask to a circle, and overlay it as a unit with a gentle float (y = base + 8*sin(2πt/3.6)). Layer order is critical: shadow UNDER the cam, white ring OVER it — never a fill or glow over the face, or the mascot reads as a brown tint / dull. Lift the masked face with a small eq (brightness +0.05, saturation ~1.15) so it pops on the light bg. Mix audio = VO + pop at each beat + record-scratch at t=0 — or, when only visuals change, re-mux a previously approved audio track of the same timeline.
Step 9 — Stitch
Concat proof hook + explainer (normalize fps/SAR/sample-rate via the concat filter) → edits/master-nocaption.mp4.
Step 10 — Captions
Generate word-level karaoke ASS from the paced-VO timestamps: short 1–3 word cues, dark-ink base + brand-color (coral) active word + soft white halo for the light bg, placed in the band between the card and the circular cam (MarginV ≈ 930 at 1080×1920) so they never cover the mascot or the cards. Offset by the proof-hook duration; leave the CTA sentence uncaptioned. Pre-merge split brand names in the word stream (e.g. "Eleven"+"Labs" → "ElevenLabs"). Burn with ffmpeg-full (libass) → edits/master-final.mp4.
Step 11 — QC + deliver
Run atoms/review/watch on the final. Copy to finals/, build storyboard.html, print the absolute master path.
Decision Rules
- Captions: ASS karaoke, not Klap. Compound tool names (Claude Code, Seedance, FFmpeg, ElevenLabs) reliably break Klap's homophone handling, AND captions must be precisely placed in the narrow band above the PIP — Klap's auto-placement would land on the mascot's mouth. Both conditions trigger the
VIDEO_GEN_RULES.mdASS fallback, and thetalking-goose-creator-videolineage uses ASS anyway. - Suppress captions on the proof hook and the CTA end card (LEARNINGS #7 — never stack two text layers in the same spot). The hook caption and the CTA card already carry their own text.
- Pace the VO with
atempo. eleven_v3 with expressive tags runs ~1.8 words/sec; the proven Goose pace is ~2.7. Speed-tuning 1.4–1.5× hits the snappy target and lowers Veed cost proportionally. Lip-sync the paced audio. - Premium HTML/CSS overlays, not flat Pillow. Render cards via Playwright (gradients, glass, real type, layered shadows). Split each beat into a static bg layer + an animated content layer.
- Use real brand logos via
cdn.simpleicons.org. The repo's logo PNGs are Git-LFS pointers (~130 bytes) and won't composite. Fetch official SVG marks and CSS-filter them white on a brand-color tile. For a tool with no simpleicons entry (Seedance), use a clean inline glyph — intentional, not hand-drawn. - Circular Loom-style cam, not a rectangle PIP. Mask the mascot face into a circle with a white ring + soft shadow. Shadow UNDER, ring OVER; never a fill/glow over the face (it reads as a brown tint / dull mascot). A small
eqlift makes the face pop on the light bg. - Subtle motion sells "premium." Content slides up + fades in per beat; the cam floats gently; step-bars advance; captions karaoke. Keep it subtle, never busy.
- Light/studio premium background. Cream gradient + a faint brand-tint glow; the brand color lives in the tile, bars, and accent — not a heavy wash.
- Reuse the proof film on the video-model beat. Showing a clip from the result while crediting the video model ties the recipe back to the flex and adds motion mid-explainer.
- Voice gate. If
voiceis not inVOICES.mdand not explicitly approved, stop at Step 1. - Proof film must be strong. If the film is music-only, use its music as the hook bed; if it carries VO, duck or drop it under the montage. If nothing in the first scenes is scroll-stopping, the format will not land — flag it.
Output
<output_dir>/
├── script/script-locked.md # clean + eleven_v3-tagged VO, per-scene map
├── source/ # the proof film (read-only)
├── audio/
│ ├── voiceover.mp3 # paced (atempo) — canonical
│ ├── voiceover_raw.mp3 # pre-atempo
│ ├── words.json # fal word timestamps on the paced VO
│ └── sfx-{pop,scratch,whoosh}.mp3
├── generated/overlays/*.png # title, word flashes, tool cards, afternoon, CTA, hook caption
├── clips/goose-talking.mp4 # one Veed Fabric lip-sync of the full VO
├── edits/
│ ├── explainer.mp4
│ ├── master-nocaption.mp4
│ ├── captions.ass
│ └── master-final.mp4 # burned captions
├── working/ # build_*.py scripts + QC frames (regeneratable)
├── finals/<slug>.mp4 # shippable 9:16 master
└── storyboard.html
Quality Checks
finals/*.mp4exists, is 1080×1920, h264 + AAC, total runtime ~30–36s.- Proof hook is ≤5s and the strongest moment lands in the first ~2s.
- Captions: word-level, gold karaoke sweep, no phantom leading comma (correct ASS Dialogue field count), no two cues stacked at once, every tool name spelled correctly, none covering the mascot's mouth.
- Captions suppressed during the proof hook and the CTA end card.
- Audio: continuous,
max_volume< 0 dB (no clipping); the music→VO transition is marked by the record-scratch. - One tool card per
tool_stackentry, each with the correct brand color + a one-line descriptor. atoms/review/watchpass finds no subtitle/overlay collisions on any frame.storyboard.htmlembeds the final and lists the stack.
Failure Modes
| Symptom | Likely cause | Fix |
|---|---|---|
| VO is 40s+ and feels slow | eleven_v3 expressive pacing | atempo 1.4–1.5 on the VO before lip-sync; rescale/re-transcribe timestamps |
| Phantom leading comma on every caption | extra comma in the ASS Dialogue: line → empty field pushes a comma into Text |
Use exactly Dialogue: 0,{start},{end},Style,0,0,0,,{text} (MarginL,MarginR,MarginV,Effect,Text) |
| Two caption lines stacked on screen | adjacent cues overlap (hold time runs past the next cue's start) | Clamp each cue's end to next_cue_start − 0.02s |
| Captions cover the mascot's mouth | MarginV too low | Place in the band above the PIP head (MarginV ≈ 848 at 1080×1920) |
| Tool card shows a blank/garbled logo | logo PNG is a Git-LFS pointer (~130 bytes) | Use a drawn Pillow wordmark + brand-colored badge instead |
| Lip-sync clip is silent/static or 403s | LFS-pointer anchor, or Veed storage auth under parallel load | Use a materialized anchor PNG; keep Veed calls sequential |
fal_client cannot authenticate |
.env has FAL_API_KEY but fal reads FAL_KEY |
export FAL_KEY="$FAL_API_KEY" before fal scripts (LEARNINGS #10) |
| ASS burn does nothing | default brew ffmpeg lacks libass | Use /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg for the ass= filter |
| Hook caption invisible / fades wrong | image overlaid without -loop 1 is a single frame at t=0 so fades never animate |
Add -loop 1 to the caption image input |
| Proof hook feels random/choppy | cuts picked without checking content, or music jumps between cuts | Confirm each cut frame; use one continuous music slice as the bed |
| Mascot in the cam looks brown / dull | a glow disc or the drop shadow is composited OVER the face | Compose shadow UNDER the cam and the white ring OVER it — nothing tinted over the face; add a small eq lift |
| Tool card shows a blank/garbled logo | logo is an LFS-pointer PNG, or no real mark exists | Fetch the SVG from cdn.simpleicons.org/<slug>; for tools without one use a clean inline glyph |
| HTML overlays render without fonts/styles | Playwright not installed, or fonts not loaded | npm install && npx playwright install chromium; wait for document.fonts.ready before screenshot |
| Stitch reads a half-written file ("moov atom not found") | a background render hadn't finished when the next step started | Run dependent steps after the render completes (don't race a background job) |