render-vo-anchored-motion-listicle
Assemble an expert/educator motion-graphic LISTICLE video ad from a config — a spoken authoritative voiceover carries a numbered listicle while N web-animated hyperframe beats (HTML plus the Web Animations API, one branded design system of alternating tiles, big hero numerals, an
Install
npx skills add https://github.com/gooseworks-ai/goose-skills/tree/main/skills/ads/capabilities/render-vo-anchored-motion-listicle
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install gooseworks-ai-goose-skills@llmmart
git clone https://github.com/gooseworks-ai/goose-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole gooseworks-ai/goose-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
render-vo-anchored-motion-listicle
Assemble an expert/educator motion-graphic listicle ad from a config: an authoritative spoken
voiceover carries a numbered listicle (hook + N points + CTA) and every visual beat is anchored to
the VO's word-level timestamps. Each beat is a web-animated hyperframe (an HTML page + the Web
Animations API driven by window.renderAt(t)) rendered to video frame-by-frame with Playwright, all
in ONE branded design system (alternating background tiles, big hero numerals, body type, decorative
SVG accents, glass-pill callouts). Periodic color-graded B-roll windows give visual breath, and
captions burn only on the B-roll windows. The shipped master is pure motion-graphic + VO — there
is NO lipsync (the still expert headshot is kept only for a future lipsync variant). This capability
is the FREE, deterministic assembly — the Playwright beat render, the ffmpeg concat, the
window-masked caption burn, the VO+music mix, and the final composite.
scripts/config.example.json is the worked example (Everself "doctor-educator" listicle, ~66s
1080×1920 9:16 at 25fps); scripts/PIPELINE.md maps every config block to its source step and
scripts/README.md documents the free assembly.
Run
This is the FREE, deterministic assembly stage — it spends nothing. The paid inputs are separate
capabilities — the spoken VO (create-vo-elevenlabs, a cloned or cast expert voice, eleven_v3 +
atempo) whose word-level timestamps (Groq whisper-large-v3 word-level) set the timeline; the low
music bed (create-music-elevenlabs); and the stock B-roll (media-proxy, trimmed + color-graded).
Given the VO + words-flat.json + the N authored hyperframe beats + the color-graded B-roll windows +
the brand wordmark SVG, render-vo-anchored-motion-listicle renders each beat frame-by-frame via
Playwright (all beats at fps 25), concats the beats + B-roll, burns the window-masked captions, mixes
the VO under the low music bed, and composites → the master. Re-cuts reuse the existing VO / beats /
B-roll and cost $0.
Contract (the free assembly)
- The spoken VO carries the listicle — it sets the timeline. The cloned/cast expert VO is the spine; Whisper-transcribe it to word-level timestamps and anchor every beat reveal to those word times. There is no on-camera human and NO lipsync in the shipped master.
- Beats are web-animated hyperframes, rendered deterministically. Each beat is an HTML page + the
Web Animations API driven by
window.renderAt(t); Playwright screenshots it frame-by-frame and ffmpeg encodes it. This is NOT i2v — it is deterministic web motion graphics. - ALL beats share the same fps (25). A mismatched-fps beat stutters at the concat seam. Render every beat and every B-roll window at fps 25.
- ONE design system across every beat. A single
_shared.css(palette + type + alternating tiles + accents + glass-pill) so N beats read as one designed reel; alternate only the background tile, keep numerals / body / accents / pills consistent. - Periodic color-graded B-roll windows for breath. Trim + color-grade each stock/brand clip to the palette (fps 25). These windows are the ONLY captioned windows.
- Captions ONLY on the B-roll windows. On the motion-graphic beats the on-screen type IS the
caption — burning Whisper captions there double-stacks text. Build the caption ASS from the VO word
timings, kept only inside the B-roll windows, 2-word chunks, closing a cue on any >0.4s word gap.
The ASS
Format:header MUST carry aNamefield — without it the leading-comma bug eats the first field and captions silently drop. If the host ffmpeg lacks libass, render the cues as timed PIL PNG overlays (ffmpegoverlay=…:enable='between(t,st,en)') at the same placement. - Low music bed under the VO. The VO is the spine; the ElevenLabs Music bed sits ~0.18 vol under it. No ducking needed at that level.
- Never AI-render the brand lockup. The CTA / end beat composites the brand's real wordmark, never text-in-diffusion.
- FFmpeg composite, deterministic, FREE. Render each beat via Playwright, concat the beats +
B-roll (ffmpeg demuxer), burn the window-masked caption ASS, mix the VO under the music bed,
loudnorm
I=-14→ a 1080×1920 25fps h264 crf18 + aac 192k master. No paid calls, no keys.
Files (goose-skills)
-
scripts
-
config.example.json 8.6 KB
{ "_comment": "Everself 'doctor-educator' listicle — THE WORKED EXAMPLE. Copy to config.json and edit. A ~66s 9:16 (1080x1920, 25fps, h264 crf18 + aac 192k) EXPERT motion-graphic LISTICLE where a CLONED authoritative clinician VO narrates a numbered listicle and every beat reveal is anchored to the VO's word-level timestamps. Pipeline: (1) lock the VO (ElevenLabs clone, eleven_v3, atempo) — it sets the timeline; (2) Groq whisper-large-v3 word-level -> working/whisper/words-flat.json; (3) author N hyperframe beats (HTML + Web Animations window.renderAt(t)) in ONE design system (_shared.css / _shared.js); (4) periodic color-graded B-roll windows for breath; (5) render each beat frame-by-frame via Playwright (render_hf.py, ALL beats fps 25), concat, mix the VO under a low music bed, composite + burn window-masked captions (build_master_v4.py) -> finals/everself-v4-master.mp4. NO lipsync — the still headshot is kept only for a future lipsync variant. See scripts/PIPELINE.md for the source scripts.", "brand_name": "Everself", "campaign": "doctor-educator listicle", "title": "What your dermatologist wishes you knew", "width": 1080, "height": 1920, "fps": 25, "duration_sec": 66.0, "aspect_ratio": "9:16", "video_codec": "h264 crf18", "audio_codec": "aac 192k", "voice": { "_comment": "Locked FIRST — the VO sets the timeline. mode 'clone' here: an ElevenLabs voice cloned from a short reference sample of the clinician (Dr. McGowan), then narrated over the listicle script with eleven_v3, then atempo'd to the target pace. Word-level timestamps come from Groq whisper-large-v3 on the RENDERED VO. The clinician headshot is retained only for a FUTURE lipsync variant — this master has NO lipsync.", "mode": "clone", "provider": "elevenlabs", "model": "eleven_v3", "voice_id": "cloned-mcgowan", "atempo": 1.06, "reference_sample": "voice/dr-mcgowan-all-shorts.mp3", "clone_script": "voice/clone_voice.py", "narrate_script": "voice/render_listicle_v2.py", "outputs": { "audio": "voice/listicle-mcgowan-v2-render.mp3", "raw": "voice/listicle-mcgowan-v2-raw.mp3", "word_timings": "working/whisper/words-flat.json" } }, "design": { "_comment": "ONE branded design system for all N beats. _shared.css holds the tokens (palette, type, alternating background tiles, decorative SVG accents, glass-pill callout); _shared.js holds initRenderer + revealWords + easing. Alternate ONLY the background tile per beat; keep the big hero numerals, body type, accents, and pills CONSISTENT so the reel reads as one designed piece.", "name": "EVERSELF_EDUCATOR", "heading_font": "Editorial serif (heading)", "body_font": "Grotesk sans (body)", "numeral_style": "big hero numeral in the brand accent color, top-left of each point beat", "palette": ["warm cream #F6F1EA", "deep clinical teal", "brand accent coral", "soft charcoal type"], "background_tiles": ["cream-tile", "teal-tile"], "accents": ["hand-drawn arc underlines", "dotted rule", "small SVG plus marks"], "callout_style": "glass-pill (frosted rounded rectangle, coral border, charcoal type)", "shared_css": "working/hyperframes-v4/_shared.css", "shared_js": "working/hyperframes-v4/_shared.js" }, "beats": [ { "id": "B01", "role": "hook", "anchor_word": "wishes", "anchor_t": 0.0, "hero_numeral": "", "headline": "What your dermatologist wishes you knew", "body": "5 things most routines get wrong.", "callout": "", "accent": "cream-tile" }, { "id": "B02", "role": "point", "anchor_word": "first", "anchor_t": 6.4, "hero_numeral": "1", "headline": "More product isn't more results", "body": "Layering actives fights your barrier.", "callout": "Barrier first", "accent": "teal-tile" }, { "id": "BR01", "role": "broll", "anchor_word": "", "anchor_t": 12.2, "broll_ref": "broll[0]", "_comment": "B-roll breath window — captions burn HERE only." }, { "id": "B03", "role": "point", "anchor_word": "second", "anchor_t": 16.8, "hero_numeral": "2", "headline": "Sunscreen is the actual anti-aging step", "body": "Daily SPF beats every serum.", "callout": "SPF > serum", "accent": "cream-tile" }, { "id": "B04", "role": "point", "anchor_word": "third", "anchor_t": 24.5, "hero_numeral": "3", "headline": "Retinol works slowly — on purpose", "body": "Twelve weeks, not twelve days.", "callout": "12 weeks", "accent": "teal-tile" }, { "id": "BR02", "role": "broll", "anchor_word": "", "anchor_t": 32.0, "broll_ref": "broll[1]", "_comment": "Second B-roll breath window." }, { "id": "B05", "role": "point", "anchor_word": "fourth", "anchor_t": 36.6, "hero_numeral": "4", "headline": "Fragrance is the hidden irritant", "body": "It's the first thing to cut.", "callout": "Cut fragrance", "accent": "cream-tile" }, { "id": "B06", "role": "point", "anchor_word": "fifth", "anchor_t": 44.9, "hero_numeral": "5", "headline": "Consistency beats the fancy routine", "body": "Three steps you'll actually do.", "callout": "3 steps", "accent": "teal-tile" }, { "id": "BCTA", "role": "cta", "anchor_word": "start", "anchor_t": 58.0, "hero_numeral": "", "headline": "Build a routine that actually works", "body": "", "callout": "everself.com", "wordmark": "brand-assets/logos/everself-wordmark.svg", "accent": "cream-tile", "_comment": "CTA / end beat — Everself wordmark composited from the real SVG (NEVER AI-rendered text)." } ], "broll": [ { "id": "broll[0]", "source": "media-proxy", "query": "close-up serum drop skincare macro", "brand_clip": "", "t_start": 12.2, "t_end": 16.8, "graded": true, "captioned": true, "_comment": "Stock macro via media-proxy, trimmed + color-graded to the palette. Captions burn ONLY inside this window." }, { "id": "broll[1]", "source": "media-proxy", "query": "woman applying sunscreen morning bathroom", "brand_clip": "", "t_start": 32.0, "t_end": 36.6, "graded": true, "captioned": true, "_comment": "Second stock breath window, graded to the palette; captioned." } ], "captions": { "_comment": "Built from the VO word timings (working/whisper/words-flat.json), kept ONLY inside the B-roll windows (broll[0], broll[1]), 2-word chunks, closing a cue on any >0.4s word gap. The ASS `Format:` header MUST include a `Name` field or the leading-comma bug eats the first field and captions silently drop. The motion-graphic beats are NOT captioned — their on-screen type IS the caption.", "source": "working/whisper/words-flat.json", "mask": "broll_windows_only", "chunk_words": 2, "gap_close_s": 0.4, "ass_format_has_name_field": true, "position": "lower-third", "no_pill": false, "font": "Grotesk sans Bold", "base_color": "#FFFFFF", "outline_px": 4 }, "music": { "_comment": "Low ElevenLabs Music bed UNDER the VO (~0.18 vol). The VO is the spine — the bed must not fight it. No artist names in the prompt (ElevenLabs Music ToS filter).", "provider": "elevenlabs", "model": "music_v1", "prompt": "Warm, minimal, editorial lo-fi bed — soft rhodes, brushed percussion, unobtrusive, clinical-calm. Sits low under a spoken voiceover.", "bed_volume": 0.18 }, "audio_mix": { "_comment": "VO at full, music bed ~0.18 under it. Loudnorm to -14 LUFS. No ducking needed at this bed level.", "vo_volume": 1.0, "music_bed_volume": 0.18, "loudness_lufs": -14 }, "headshot": { "_comment": "The still clinician headshot is kept for a FUTURE lipsync variant. The shipped motion-graphic + VO master does NOT use it.", "asset": "source/mcgowan-headshot.png", "used_in_master": false }, "render_engine": { "_comment": "Each beat is an HTML hyperframe + the Web Animations API driven by window.renderAt(t); Playwright screenshots it frame-by-frame and ffmpeg encodes it. Deterministic web motion graphics, NOT i2v. ALL beats MUST share fps 25 — a mismatched-fps beat stutters at the concat seam.", "engine": "playwright+ffmpeg", "renderer": "window.renderAt(t) per frame -> Playwright screenshot -> ffmpeg encode", "fps": 25, "script": "working/render_hf.py", "hyperframes_dir": "working/hyperframes-v4/", "shared_css": "working/hyperframes-v4/_shared.css", "shared_js": "working/hyperframes-v4/_shared.js" }, "post_production": { "voiceover": { "default": "on", "note": "the cloned/cast expert VO IS the spine and sets the timeline" }, "music": { "default": "on", "note": "low ElevenLabs Music bed (~0.18 vol) under the VO" }, "captions": { "default": "on", "note": "window-masked (B-roll only), 2-word chunks, close on >0.4s gaps, ASS Name-field header" }, "end_card": { "default": "on", "note": "CTA beat uses the brand's real wordmark, never AI-rendered text" } } } -
PIPELINE.md 6 KB
# Pipeline — VO-anchored motion-graphic listicle How `config.example.json` maps to the real production steps. This molecule ships a **config + this map**, not a bundled runner: the worked example (Everself "doctor-educator" listicle) was produced by per-project drivers that live in `clients/everself-hb/working/doctor-christopher-avatar/` (`voice/render_listicle_v2.py`, `working/render_hf.py`, `working/build_pexels_beats.py`, `working/grade_brolls_v4.py`, `working/build_master_v4.py`). Reference those directly, or drive the whole run via `video-orchestrator-with-control-plane`. The steps run **in order** because each depends on the last: the VO sets the timeline (its word-level timestamps), the word times anchor every beat reveal, the beats + B-roll seed the render, the render + concat build the silent master, and assembly mixes the VO under the music bed + burns the window-masked captions. ## Field → source-script map | Config field | Phase | Source step / script (in the run) | Paid? | |---|---|---|---| | `voice.mode`, `voice.voice_id`, `voice.model`, `voice.atempo`, `voice.script_ref` | 1 VO | ElevenLabs clone/cast (`eleven_v3`) + ffmpeg `atempo` -> `voice/vo-render.mp3` | **PAID** | | `voice.outputs.word_timings` | 2 Transcribe | Groq `whisper-large-v3` word-level on the rendered VO -> `working/whisper/words-flat.json` | ~$0.10 | | `design.*`, `beats[].hero_numeral/headline/body/callout/accent` | 3 Beats | author N hyperframes (HTML + Web Animations `window.renderAt(t)`) in ONE design system -> `working/hyperframes-v4/beat-NN.html` + `_shared.css` + `_shared.js` | free | | `beats[].anchor_word`, `beats[].anchor_t` | 3 Beats | reveals anchored to word times in `words-flat.json` | free | | `render_engine`, `fps` | 5 Render | `working/render_hf.py` (Playwright frame-by-frame `renderAt(t)` -> ffmpeg encode; ALL beats share fps 25) | free | | `broll[].query`, `broll[].brand_clip`, `broll[].t_start/t_end`, `broll[].graded` | 4 B-roll | `working/build_pexels_beats.py` (download/trim) + `working/grade_brolls_v4.py` (color-grade to palette) | media-proxy (stock) | | `captions`, `captions.mask`, `captions.chunk_words`, `captions.gap_close_s`, `captions.ass_format_has_name_field` | 5 Captions | Whisper words -> ASS, kept only inside B-roll windows, 2-word chunks, close on >0.4s gaps (`Name`-field header) | free | | `music`, `music.bed_volume`, `audio_mix` | 5 Assembly | ElevenLabs Music bed (~0.18 vol) mixed under the VO | **PAID** (music bed) | | assembly | 5 Assembly | `working/build_master_v4.py` (concat beats + B-roll, mix VO+music, composite silent video + audio + burn window-masked captions -> `finals/everself-v4-master.mp4`) | free | | `headshot` | — | kept for a FUTURE lipsync variant; UNUSED in the shipped master | — | ## 1. VO -> ElevenLabs clone/cast (config: `voice`) [PAID] **Lock the VO FIRST — it sets the timeline.** `voice/render_listicle_v2.py` narrates the listicle script with ElevenLabs `eleven_v3` — either a **cloned** voice (train from a short reference sample of the expert, then narrate) or a **cast** `voice_id` — then `atempo` to the target pace (v3's speed param is weak; use ffmpeg `atempo` for real time-stretch) -> `voice/vo-render.mp3`. The delivered VO and its word-level timings, NOT the plan, set the timeline. ## 2. Transcribe -> Whisper word timings (config: `voice.outputs.word_timings`) [~$0.10] Groq `whisper-large-v3` word-level on the RENDERED VO -> `working/whisper/words-flat.json` (a flat list of `{word, start, end}`). Every beat reveal in step 3 anchors to a word time here. Whisper is the right tool for SPOKEN VO word-level; script-window timing is only for SUNG audio (not this format). ## 3. Beats -> hyperframe authoring (config: `design`, `beats[]`) One HTML hyperframe per beat in ONE design system: `working/hyperframes-v4/beat-NN.html`, each exposing `window.renderAt(t)` driven by the Web Animations API so a renderer can seek any frame. Shared `working/hyperframes-v4/_shared.css` holds the design tokens (palette, type, alternating `background_tiles`, decorative SVG `accents`, the glass-pill callout) and `_shared.js` holds `initRenderer` + `revealWords` + easing. Reveals fire at each beat's `anchor_word`/`anchor_t` from `words-flat.json`. Alternate the background tile per beat for rhythm; keep numerals / body type / accents / pills CONSISTENT so N beats read as one designed reel. This is deterministic web motion graphics — NOT i2v. ## 4. B-roll windows -> download/trim + color-grade (config: `broll[]`) [media-proxy stock] For each `broll[]` window, `working/build_pexels_beats.py` downloads/trims the stock clip (or ingests a brand/procedure clip) and `working/grade_brolls_v4.py` color-grades it to the palette. Render B-roll at fps 25 to match the beats. These are the ONLY windows where captions burn — on the motion-graphic beats the on-screen type IS the caption. ## 5. Render + captions + assembly (config: `render_engine`, `captions`, `music`, `audio_mix`) - **Render (`render_hf.py`):** for each beat, Playwright loads the hyperframe, calls `window.renderAt(t)` per frame, screenshots it, and ffmpeg encodes -> a silent mp4 per beat. **ALL beats MUST share fps 25** — a mismatched-fps beat stutters at the concat seam. - **Captions:** built from `words-flat.json`, kept **ONLY inside the B-roll windows**, 2-word chunks, closing a cue on any >0.4s word gap. The ASS `Format:` header MUST include a `Name` field — without it the leading-comma bug eats the first field and captions silently drop. - **Assembly (`build_master_v4.py`):** concat all beats + B-roll windows (ffmpeg demuxer) -> `master-silent.mp4`, mix the VO (full) under a LOW ElevenLabs Music bed (~0.18 vol), composite the silent video + the mixed audio + burn the window-masked captions, loudnorm to -14 LUFS -> `finals/everself-v4-master.mp4` (1080×1920, 25fps, h264 crf18 + aac 192k, ~66s). Re-cuts (new caption chunking, re-timed beats, swapped B-roll, a different music bed level) reuse the existing VO / beats / B-roll and cost **$0** — only the VO clone/narration and the music bed spend. -
README.md 3.5 KB
# render-vo-anchored-motion-listicle scripts — the FREE assembly `render-vo-anchored-motion-listicle` is the **deterministic, $0 assembly stage** of the VO-anchored motion-graphic listicle format. The paid/metered stages (the spoken expert VO, the low music bed, the stock B-roll) are separate capabilities — `create-vo-elevenlabs`, `create-music-elevenlabs`, `media-proxy`. This capability spends nothing: it takes the VO + `words-flat.json` + the N authored hyperframe beats + the color-graded B-roll windows + the brand wordmark SVG and stitches the finished master. Re-cuts (new caption chunking, re-timed beats, a swapped B-roll window, a different music-bed level) reuse the existing VO / beats / B-roll and cost **$0**. `config.example.json` is the worked example (Everself "doctor-educator" listicle, ~66s 1080×1920 at 25fps). `PIPELINE.md` maps every config block to its source step. This README documents the FREE assembly pieces that `render-vo-anchored-motion-listicle` owns. ## 1. Beat render — Playwright frame-by-frame, all beats at fps 25 Each beat is an HTML hyperframe + the Web Animations API driven by `window.renderAt(t)`. The renderer (`render_hf.py`-style) loads the hyperframe headless in Playwright, calls `window.renderAt(t)` for each frame time, screenshots it, and ffmpeg encodes the frames → a silent mp4 per beat. This is deterministic web motion graphics — NOT i2v, no diffusion, no keys. **ALL beats MUST share fps 25** — a beat rendered at a different fps stutters at the concat seam. The reveals inside each beat are anchored to the VO's word-level timestamps (`words-flat.json`), so authoring order is: lock the VO first, transcribe it, then time each beat's reveals to its anchor words. ## 2. Concat on the timeline The VO sets the timeline. Assembly concats the rendered beats + the color-graded B-roll windows in order (ffmpeg demuxer) → `master-silent.mp4`. Beats and B-roll are all fps 25 so the seams are clean. ## 3. B-roll windows — color-graded, the only captioned windows Each B-roll window is a stock (via `media-proxy`) or brand/procedure clip, trimmed and color-graded to the palette, rendered at fps 25. These give the eye a rest between the dense motion-graphic beats and are the **only** windows where captions burn — on the beats themselves the on-screen type IS the caption. ## 4. Captions — from the VO word timings, masked to the B-roll windows Captions come from the VO's `words-flat.json`, kept **ONLY inside the B-roll windows**, chunked to ~2 words, closing a cue on any >0.4s word gap. Rendered as an ASS overlay in a lower-third. **The ASS `Format:` header MUST include a `Name` field** — without it the leading-comma bug eats the first field and captions silently drop. Never caption the motion-graphic beats (double-stacks text). If the host ffmpeg lacks libass (no `subtitles`/`ass` filter), render the cues as timed PIL PNG overlays composited with ffmpeg `overlay=…:enable='between(t,st,en)'` instead — same placement, no libass dependency. ## 5. VO + music mix The VO is the spine at full level; the ElevenLabs Music bed sits ~0.18 vol under it (no ducking needed at that level). Mix → the master audio. ## 6. FFmpeg composite FFmpeg composites the master: the silent concatenated video + the mixed audio (VO + low bed) + the burned window-masked captions, loudnorm `I=-14` → a 1080×1920 25fps h264 crf18 + aac 192k master (~66s). The CTA / end beat composites the brand's real wordmark — **never** AI-rendered text (a diffusion model garbles a wordmark). Deterministic, no paid calls, no keys.
-
-
tests
-
smoke-test.md 1.4 KB
# Smoke Test Given the spoken expert VO (mp3 + `words-flat.json` word timings), N authored web-animated hyperframe beats in ONE design system, periodic color-graded B-roll windows, a low music bed, and the brand wordmark SVG, `render-vo-anchored-motion-listicle` assembles the master: render each beat frame-by-frame via Playwright (all beats fps 25), concat the beats + B-roll, burn window-masked captions, mix the VO under the low music bed, composite → 1080×1920 h264 crf18 + aac 192k (~66s). Pass when the assembly runs to a valid MP4 and: - every beat reveal is anchored to the VO's word-level timestamps; the ONE design system holds across every beat (tiles alternate; numerals / body / accents / pills consistent); - all beats and B-roll windows share fps 25 (no concat-seam stutter); - captions appear ONLY inside the B-roll windows (2-word chunks, no dropped cues — the ASS `Format:` header carries a `Name` field), never over the motion-graphic beats; - the VO carries the listicle under a low music bed (~0.18); the CTA / end beat shows the real wordmark (never AI-rendered text); - there is NO lipsync / talking head in the master (the still headshot is a future-variant input, not used here); - **no paid call is made** — the VO, the music bed, and the stock B-roll come from the paid/metered capabilities (create-vo-elevenlabs / create-music-elevenlabs / media-proxy); this assembly is $0 and a re-cut reuses the existing VO / beats / B-roll.
-
-
SKILL.md 5.3 KB
--- name: render-vo-anchored-motion-listicle description: Assemble an expert/educator motion-graphic LISTICLE video ad from a config — a spoken authoritative voiceover carries a numbered listicle while N web-animated hyperframe beats (HTML plus the Web Animations API, one branded design system of alternating tiles, big hero numerals, and glass-pill callouts) are rendered frame-by-frame via Playwright and anchored to the VO's word-level timestamps, periodic color-graded B-roll windows give visual breath, and captions burn ONLY inside those B-roll windows (2-word chunks, ASS Format header carrying a Name field so none drop) with the VO mixed under a low music bed. This is the FREE deterministic assembly stage (Playwright beat render plus ffmpeg concat plus window-masked caption burn plus VO-and-music mix plus final composite) — the VO, the music bed, and the stock B-roll come from create-vo-elevenlabs, create-music-elevenlabs, and media-proxy. Use for the vo-anchored-motion-listicle format. status: active --- # render-vo-anchored-motion-listicle Assemble an **expert/educator motion-graphic listicle** ad from a config: an authoritative spoken voiceover carries a numbered listicle (hook + N points + CTA) and every visual beat is anchored to the VO's word-level timestamps. Each beat is a **web-animated hyperframe** (an HTML page + the Web Animations API driven by `window.renderAt(t)`) rendered to video frame-by-frame with Playwright, all in ONE branded design system (alternating background tiles, big hero numerals, body type, decorative SVG accents, glass-pill callouts). Periodic color-graded **B-roll windows** give visual breath, and captions burn **only on the B-roll windows**. The shipped master is pure motion-graphic + VO — there is NO lipsync (the still expert headshot is kept only for a future lipsync variant). This capability is the **FREE, deterministic assembly** — the Playwright beat render, the ffmpeg concat, the window-masked caption burn, the VO+music mix, and the final composite. `scripts/config.example.json` is the worked example (Everself "doctor-educator" listicle, ~66s 1080×1920 9:16 at 25fps); `scripts/PIPELINE.md` maps every config block to its source step and `scripts/README.md` documents the free assembly. ## Run This is the **FREE, deterministic** assembly stage — it spends nothing. The paid inputs are separate capabilities — the spoken VO (`create-vo-elevenlabs`, a cloned or cast expert voice, `eleven_v3` + `atempo`) whose word-level timestamps (Groq `whisper-large-v3` word-level) set the timeline; the low music bed (`create-music-elevenlabs`); and the stock B-roll (`media-proxy`, trimmed + color-graded). Given the VO + `words-flat.json` + the N authored hyperframe beats + the color-graded B-roll windows + the brand wordmark SVG, `render-vo-anchored-motion-listicle` renders each beat frame-by-frame via Playwright (all beats at fps 25), concats the beats + B-roll, burns the window-masked captions, mixes the VO under the low music bed, and composites → the master. Re-cuts reuse the existing VO / beats / B-roll and cost **$0**. ## Contract (the free assembly) - **The spoken VO carries the listicle — it sets the timeline.** The cloned/cast expert VO is the spine; Whisper-transcribe it to word-level timestamps and anchor every beat reveal to those word times. There is no on-camera human and NO lipsync in the shipped master. - **Beats are web-animated hyperframes, rendered deterministically.** Each beat is an HTML page + the Web Animations API driven by `window.renderAt(t)`; Playwright screenshots it frame-by-frame and ffmpeg encodes it. This is NOT i2v — it is deterministic web motion graphics. - **ALL beats share the same fps (25).** A mismatched-fps beat stutters at the concat seam. Render every beat and every B-roll window at fps 25. - **ONE design system across every beat.** A single `_shared.css` (palette + type + alternating tiles + accents + glass-pill) so N beats read as one designed reel; alternate only the background tile, keep numerals / body / accents / pills consistent. - **Periodic color-graded B-roll windows for breath.** Trim + color-grade each stock/brand clip to the palette (fps 25). These windows are the ONLY captioned windows. - **Captions ONLY on the B-roll windows.** On the motion-graphic beats the on-screen type IS the caption — burning Whisper captions there double-stacks text. Build the caption ASS from the VO word timings, kept only inside the B-roll windows, 2-word chunks, closing a cue on any >0.4s word gap. The ASS `Format:` header MUST carry a `Name` field — without it the leading-comma bug eats the first field and captions silently drop. If the host ffmpeg lacks libass, render the cues as timed PIL PNG overlays (ffmpeg `overlay=…:enable='between(t,st,en)'`) at the same placement. - **Low music bed under the VO.** The VO is the spine; the ElevenLabs Music bed sits ~0.18 vol under it. No ducking needed at that level. - **Never AI-render the brand lockup.** The CTA / end beat composites the brand's real wordmark, never text-in-diffusion. - **FFmpeg composite, deterministic, FREE.** Render each beat via Playwright, concat the beats + B-roll (ffmpeg demuxer), burn the window-masked caption ASS, mix the VO under the music bed, loudnorm `I=-14` → a 1080×1920 25fps h264 crf18 + aac 192k master. No paid calls, no keys. -
skill.meta.json 345 B
{ "slug": "render-vo-anchored-motion-listicle", "category": "capabilities", "domain": "ads", "tags": [ "ads" ], "installation": { "base_command": "npx goose-skills install render-vo-anchored-motion-listicle", "supports": [ "claude", "cursor", "codex" ] }, "requires_skills": [ "watch" ] }
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.