Communitygithub.com

yuyurun73-dev/bw-song-ad

Creates a complete vertical MP4 "song ad" in a 1930s black-and-white rubber-hose cartoon (Fleischer-style) from lyrics, including composition, subtitle timing, keyframe animation, and final packaging.

What is bw-song-ad?

This skill automates the creation of a full black-and-white cartoon song advertisement from a long-form VSL script. It takes the script, turns it into a song via Suno, aligns each character's timing with OpenAI Whisper, generates consistent rubber-hose cartoon visuals using Higgsfield and Grok Imagine, then composites everything in Remotion with hooks, subtitles, and a CTA. Designed specifically for /bw-song-ad, it avoids live-action, avatar VSLs, or Zack D-style animations. The entire pipeline produces a ready-to-deliver 3.5-minute vertical MP4, optimized for the 1930s Fleischer aesthetic with strict quality checks.

Works with~Claude Code✓Codex CLI~Cursor
npx skills add yuyurun73-dev/bw-song-ad

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

/bw-song-ad — Complete Black-and-White Cartoon Song Ad

2026-10-18, created with the procedure from H-OHI-023a (Ohisama Pants). Reference: X's WhiteHat MRR (bone density, black-and-white cartoon, ~4 minutes). The response was "the song is fire, it's godly." The reference video is not a short jingle but a long-form VSL script sung almost verbatim.

Order (do not change)

Lyrics → Song (Suno) → Character-level timing → Scene division → Character sheet → Keyframes → Animation → Background export → Remotion → Quality check. The song is the clock. Creating visuals first will cause rework because they won't align with the song.

S=~/.agents/skills/bw-song-ad
AD=<project folder>/<ad ID>        # lyrics.md / captions.txt / song/ / shots.py / keys/ / clips/ / remotion/
cp -R $S/remotion $AD/remotion && cd $AD/remotion && NODE_USE_ENV_PROXY=1 npm install

0. Check Existing Assets First

If there are previously created images/videos for the same brand, use them first (check the brand asset inventory table ASSETS.md; update it when new ones are made). When generation is needed, report upfront: "Approximately X images, Y videos, ~Z credits." This run: 61 images (~15 credits), 61 Grok videos (~265 credits).

1. Lyrics (lyrics.md) — Template in reference/lyrics.md

  • Sing the VSL flow in paragraphs. Do not rhyme. Structure mirrors the reference 1:1: Expert opening → "it's not that" ×3 → Something more important → Cause is not where you think → Internal allies vs. villains → Used to work → Now villains are winning → The problem chain → Store has the same stuff → But things changed (solution) → Article (no registration needed) → Inventory → "No need to..." ×3 → "It's okay. Tap below."
  • All in hiragana. Numbers also in hiragana (Suno breaks on kanji and digits).
  • Do not sing product names (display via on-screen labels). No prices. Promises stay within the product page scope.
  • Show before composing.

2. Song (Suno / Playwright)

  • Only login is manual. Then inject lyrics, Style, Title into suno.com/create?mode=CUSTOM and press Create.
  • Style (best): 1930s vintage upbeat jazz, old cartoon soundtrack, swing, clarinet, muted trumpet, ragtime piano, brushed snare, upright bass, warm female vocal, Japanese, clear diction, storytelling sung melody
  • Generates 2 tracks. Transcribe both with whisper and compare to lyrics; pick the one with fewer errors.
  • Download: track page → More options → Download → MP3 → "Unlock & Download" (uses one free credit). cdn1.suno.ai returns 403; encrypted playback m4a. Do not extract browser blob to bypass limits.
  • Place at song/song.mp3.

3. Caption Text (captions.txt)

  • One line per lyrics paragraph. Split lines with / (one segment = one subtitle on screen, ~12 characters max).
  • Convert to readable mixed kanji/kana Japanese (lyrics are hiragana; captions use standard notation).
  • For two-line segments, use | (e.g., Tap the button below|to read the article ).

4. Character-Level Timing

uv run --python 3.12 --with openai-whisper python $S/scripts/align.py $AD

→ Produces $AD/cap_events.json (segment start/end times) and $AD/line_starts.json (each line's start).

  • Do not use whisper-cli's line-level timing. It treats the intro (silent first seconds) as starting at 0 seconds and distributes timing evenly within lines, causing subtitles to advance during silence.
  • Hide subtitles during pauses longer than 0.8 seconds.
  • If alignment accuracy <80%, the song or captions.txt is misaligned.

5. Scene Division (shots.py)

  • Define STYLE (common visual description for all cuts) and SHOTS = [(line number, cut index within line, scene description, motion), ...].
  • Each cut: 2–5 seconds. Long lines split into 2–3 cuts. ~60 cuts for 3.5 minutes.
  • Do not animate all long shots: mix static images with slow zooms to halve video generation needs.
  • Characters: Teacher (Bob, apron) / Mom (low bun, stripes) / Child (bob cut) / Two internal messengers (striped overalls, hats) / Villain (black sphere, horns, sponge & straw) / Product (actual pattern).

6. Character Sheet & Keyframes (Higgsfield gpt_image_2_5 / 9:16 / 0.25 credits each)

  • First, create one character sheet image. For all subsequent cuts, pass both the character sheet + one reference frame as image_references.
  • Prepend STYLE: black and white 1930s rubber-hose cartoon, thick clean ink lines, cross-hatching, grayscale only, vintage Fleischer-era look, slightly grainy film, soft dark round vignette, no text.
  • Run: python3 $S/scripts/make_reqs.py $AD image → batch 12 at a time with generate_image_batch.
  • Dress the child (underwear/diaper view triggers NSFW rejection). One reference frame showing underwear alone can cause rejection.
  • Write the actual product pattern in the prompt (re-create if different pattern appears).
  • Review all frames via contact sheet: check style consistency, no text, character sheet conformity.

7. Video Generation (Grok Imagine grok_video_v15_lite / 720p / start_image only / ~1 credit per second)

  • Run: python3 $S/scripts/make_reqs.py $AD video → batch 12 at a time with generate_video_batch. Duration = segment length + 0.5s (3–8s total).
  • Prompt: "Animate this image: . Keep the same style, composition, and characters; bouncy rubber-hose motion, film flicker, fixed camera, no cuts, no new text."
  • If rejected and a preset is returned, re-submit with declined_preset_id in params.
  • Review all final frames side by side. If the product turns sideways and its pattern breaks, use only frames before the break (SRC_MAX) or loop the front section.

8. Export Background (No Text)

python3 $S/scripts/bg.py $AD          # → $AD/bg.mp4 (1080x1920 / 30fps) and shot_starts.json
  • Cut transitions align with line start times (line_starts.json). First cut starts at 0s.
  • To fill an instrumental intro, consider fast cuts of strong scenes (villain, flood, stamping, etc.).

9. Remotion (at $AD/remotion)

  • Place public/bg.mp4, public/song.mp3, src/data.json: python3 $S/scripts/make_data.py $AD --hook-end 29.3 --cta 192.9 --brand 44,46,47,50 --hook-top "For moms potty training" --hook-main "Diapers won't come off" --hook-mark "The real reason" --brand-name "<product name>" --keys "<key phrases in yellow, comma-separated>"
  • Components (src/SongAd.tsx):
    • Top hook: Paper card (thick black border, offset shadow, slight rotation) showing "who it's for + what it's about" in the first second. Example: "For moms potty training / Diapers won't come off The real reason".
    • Subtitles: Black rounded rectangle, white bold text, pops in with bounce. Key phrases only in yellow (the sole color in black-and-white).
    • Product label: Small text above product-only cuts.
    • CTA: From the final "Tap below" to the end, three yellow arrows bounce down + "Tap the button below|to read the article".
  • Export (this Mac's headless Chrome crashes standalone → use chrome-wrap.sh, outside sandbox):
cd $AD/remotion && NODE_USE_ENV_PROXY=1 npx remotion render src/index.ts SongAd out/<ad ID>.mp4 --browser-executable=./chrome-wrap.sh --concurrency=6 --crf=20
  • Delivery: re-encode with -crf 23 -movflags +faststart (heavy due to grain; ~200MB for 3.5 min).

10. Quality Check (mandatory before opening)

  • Read a contact sheet of ~24 frames (one per second): check subtitles align with song / not appearing during silence / product pattern intact / CTA arrow stays on screen / no stray text.
  • Check first 3 seconds separately (hook immediately readable).
  • Create an HTML preview page and open it. Use the AdLab issued name (H-XXX-000a) as the filename.

Pain Points (measured this run)

  • Subtitles drift from start: song starts at 4.9s but whisper-cli assumed 0s → solved by align.py (openai-whisper word_timestamps + difflib).
  • NSFW keyframes: child in underwear / reference frame with underwear → dress the child / describe only the style in the reference.
  • Product rotates and breaks pattern: Grok often turns sideways. Use only front-facing seconds.
  • Over-production: 61 images + 61 videos for one ad. Next time: reuse inventory and static images with zooms to halve.
  • Last character of a long line drops to next line → specify line breaks with |.

Related: reference/lyrics.md (lyrics template and examples), /zackd-ad (3D animation type), AdLab (register hypothesis before creation).

Related Skills