$npx -y skills add gooseworks-ai/goose-skills --skill render-song-mvAssemble a song-driven music-video ad from a config — a generated sung track carries the whole narration across N tableaux (one keyframe -> one i2v clip per lyric beat) with NO separate voiceover, captions synced to the song's OWN word timings (script-window, never Whisper) and t
| 1 | # render-song-mv |
| 2 | |
| 3 | Assemble a song-driven music-video ad from a config: a purpose-written, **sung** song is |
| 4 | the entire narration (no separate voiceover), and every visual beat is timed to the lyrics. |
| 5 | The delivered song sets the timeline; N tableaux (one keyframe → one image-to-video clip per |
| 6 | lyric beat, all in a single look pack) are cut to their lyric windows and hard-concatenated |
| 7 | on the beat, captions are built from the song's OWN word timings with the hook line landing |
| 8 | on the chorus drop, and the spot closes on a PIL brand end card. It reads like a tiny animated |
| 9 | music video, not a demo. `scripts/config.example.json` is the worked example (Loóna "Fall In |
| 10 | Love With Sleep Again", 28s paper-craft 9:16); `scripts/PIPELINE.md` maps every config block |
| 11 | to its step and `scripts/README.md` documents the free assembly. |
| 12 | |
| 13 | ## Run |
| 14 | |
| 15 | This is the **FREE, deterministic** assembly stage — it spends nothing. The three paid |
| 16 | inputs are separate capabilities: the sung song (`create-music-elevenlabs`, music_v1, |
| 17 | force_instrumental FALSE — the lyrics ARE the script, returns mp3 + `words.json`), one |
| 18 | keyframe per tableau (`create-image-fal`), and one Kling 3.0 i2v clip per tableau |
| 19 | (`create-video-fal`). Given the delivered song + `words.json` + one clip per beat, |
| 20 | `render-song-mv` cuts each clip to its lyric window, hard-concats on the beat, builds the |
| 21 | lyric-synced captions, composites the PIL end card, and muxes → the master. Re-cuts reuse |
| 22 | the existing song / keyframes / clips and cost **$0**. |
| 23 | |
| 24 | ## Contract (the free assembly) |
| 25 | |
| 26 | - **The sung song carries the narration — no separate VO.** The generated ElevenLabs track |
| 27 | IS the bed and the script (`force_instrumental` false); do not add a spoken voiceover or a |
| 28 | second music bed. |
| 29 | - **Plan the timeline AROUND the delivered song.** The song is generated first and reshapes/ |
| 30 | overshoots length; snap every tableau boundary to the lyric-phrase edges in the returned |
| 31 | word timings (`timeline.json`) — never trim the song to a pre-planned grid. |
| 32 | - **Captions from the song's OWN word timings, not Whisper (script-window).** Chunk |
| 33 | `audio/words.json` (~3 words at lyric boundaries); accent words get the warm-glow color. |
| 34 | Whisper on sung audio returns "🎵 Music Playing 🎵", so it can't caption lyrics. |
| 35 | - **Land the hook on the chorus drop.** Exactly ONE hero tableau (`is_hook`) is timed so the |
| 36 | payoff word (`song.hook_word`) sits on the chorus drop; accent that word in the captions. |
| 37 | - **One look pack for consistency.** A single `style_opener` + `negative_tail` + palette drives |
| 38 | every keyframe so N beats read as one film; no morph within a clip. |
| 39 | - **Hard cuts on the beat.** Cut each clip to its lyric window and hard-concat — no dissolves |
| 40 | (one optional match-cut into the hero reveal). |
| 41 | - **PIL end card from the real app icon — never AI-render brand text.** The lockup is |
| 42 | composited deterministically (brand gradient + circular app icon + wordmark + tagline + CTA) |
| 43 | from the brand's real asset; a diffusion model garbles a wordmark. |
| 44 | - **FFmpeg composite, deterministic, FREE.** Burn the caption ASS, overlay the end-card PNG on |
| 45 | the final window, mux the song, boost the climax beat, loudnorm to −14 LUFS → 1080×1920 |
| 46 | h264+aac. No paid calls, no keys. |