$npx -y skills add gooseworks-ai/goose-skills --skill render-cosmic-mythology-voiceoverAssemble a cosmic-mythology-voiceover reel from a config — a warm spoken voiceover carries the whole narrative while N curated cosmic stills are weighted beat-synced across the delivered VO duration (cut_dur = VO_dur times weight over the weight sum, so emotional beats hold longe
| 1 | # render-cosmic-mythology-voiceover |
| 2 | |
| 3 | Assemble a **cosmic-mythology-voiceover** reel from a config: a faceless, cinematic storytelling |
| 4 | video where a warm, contemplative spoken **voiceover** carries the whole narrative (a "myth as |
| 5 | teacher" reframe) over a slow, weighted **Ken-Burns zoom** across curated cosmic / mythology stills |
| 6 | in ONE ethereal deep-indigo + gold look, with ONE on-screen hook line and burned captions. This |
| 7 | capability is the **FREE, deterministic assembly** — the weighted beat-sync sequencing, the |
| 8 | Ken-Burns render, the concat, the VO composite, the hook overlay, and the caption burn. |
| 9 | |
| 10 | `scripts/config.example.json` is the worked example (WishAstro "Saturn isn't your villain", ~31s |
| 11 | 1080×1920 9:16, 12 weighted Ken-Burns cuts); `scripts/PIPELINE.md` maps every config block to its |
| 12 | source step and `scripts/README.md` documents the free assembly. |
| 13 | |
| 14 | ## Run |
| 15 | |
| 16 | There is a single runnable script — `scripts/render.py` (config-driven, ffmpeg + Pillow only, NO |
| 17 | API keys and NO `drawtext`/`libass` required): |
| 18 | |
| 19 | ```bash |
| 20 | python3 scripts/render.py --config config.json --vo working/vo2/vo_atempo.mp3 \ |
| 21 | --stills-dir working/stills --out working/final.mp4 \ |
| 22 | [--words working/vo2/words.json] [--endcard working/endcard.png] |
| 23 | ``` |
| 24 | |
| 25 | This is the **FREE, deterministic** assembly stage — it spends nothing. The paid inputs are |
| 26 | separate capabilities — the spoken VO (`create-vo-elevenlabs`, ElevenLabs `eleven_v3` from a |
| 27 | tone-tagged script, atempo time-stretched so the delivered duration sets the timeline) and the 4–6 |
| 28 | hero stills in one look pack (`create-image-fal`, Flux Pro 1.1, reused as repeats to reach the |
| 29 | ~10–12 cuts). Given the VO + the stills + the per-cut weight array + the hook line, `render.py` |
| 30 | distributes the cuts across the VO duration by the weighted formula, Ken-Burns-renders each still, |
| 31 | concats, composites the VO, fades the hook line on over the open, burns the captions, and (if |
| 32 | `--endcard` is passed) appends a brand end card → the master + a poster. Re-cuts reuse the existing |
| 33 | VO / stills and cost **$0**. See `scripts/README.md` §0 for the full arg contract. |
| 34 | |
| 35 | ## Contract (the free assembly) |
| 36 | |
| 37 | - **The spoken VO carries the narrative — no talking head, no sung song.** The generated/supplied |
| 38 | VO IS the audio bed (no music bed by default); do not add a presenter or a second bed. |
| 39 | - **Plan the timeline AROUND the delivered VO duration.** The VO is atempo time-stretched (clamp |
| 40 | the factor ≤ ~1.25 so the voice never chipmunks); its delivered length sets the timeline — never |
| 41 | trim the VO to a pre-planned grid. |
| 42 | - **Weighted beat-sync, not a fixed grid.** For each cut, `cut_dur = VO_dur × weight / Σweights` — |
| 43 | heavier weights hold longer on the emotional beats (the open, the reframe, the close); the setup |
| 44 | cuts run shorter. Every cut stays proportional to the whole VO. |
| 45 | - **Ken-Burns per still.** Render each still with scale 2×, center crop, and a `zoompan` to the |
| 46 | configured `zoom_end` (~1.10); apply `zoom_out` on the flagged cuts; `fade_in` on the FIRST cut |
| 47 | and `fade_out` on the LAST. Stills are reusable — the sequence repeats a few across the cuts. |
| 48 | - **ONE cosmic look, no in-world text.** Every still reads in the single deep-indigo + gold + |
| 49 | volumetric-light look; the reel's only text is the hook + the captions, added in post (the "no |
| 50 | text, no words" descriptor keeps words off the stills). |
| 51 | - **ONE hook line, alpha-faded on over the open.** Burn the single reframe line over the OPEN only |
| 52 | (fade in ~0.5s, hold, fade out ~0.6s) — never a persistent caption, never in-world. `render.py` |
| 53 | does this with a PIL PNG + ffmpeg `fade=…:alpha=1` (no `drawtext` dependency, since stock ffmpeg |
| 54 | often lacks it); an ffmpeg `drawtext` alpha window is an equivalent alternative where available. |
| 55 | - **Captions from Whisper — bottom, white.** VEED/Whisper subtitle burn tracks the spoken VO in the |
| 56 | bottom third, white `#FFFFFF`. If the host ffmpeg lacks libass (no `subtitles`/`ass` filter), |
| 57 | render the cues as timed PIL PNG overlays (ffmpeg `overlay=…:enable='between(t,st,en)'`) a |