/bw-song-ad — 白黒カートゥーンの歌広告を完パケまで
2026-10-18、おひさまパンツ H-OHI-023a で作った手順。参考は X の WhiteHat MRR(骨密度・白黒カートゥーン・約4分)。 本人の反応は「歌がfireすぎる、神だ」。参考動画の正体は短いジングルではなく、ロングフォームVSLの台本をほぼそのまま歌っているもの。
順番(崩さない)
歌詞 → 歌(Suno) → 1文字単位の時刻合わせ → 場面割り → キャラ表 → キーフレーム → 動画化 → 背景の書き出し → Remotion → 検品。 歌が時計。絵を先に作ると、歌の間と合わずに全部やり直しになる。
S=~/.agents/skills/bw-song-ad
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. 先に在庫を見る
同じブランドで前に作った絵・動画があれば、まずそれを使う(ブランドごとの素材在庫表 ASSETS.md を見る・新しく作ったら追記する)。 生成が要る時は、作る前に「画像◯枚・動画◯本・約◯クレジット」を一言伝える。今回の実績:画像61枚で約15、Grok動画61本で約265。
1. 歌詞(lyrics.md)— reference/lyrics.md の型
- VSLの流れを段落ごとに歌う。韻は踏まない。参考と1対1の運び: 専門職の語り出し → 定番を「ちがいます」×3 → もっと大事なこと → 原因は思われている所にない → 体の中の味方と悪役 → 昔はうまくいっていた → 今は悪役が勝っている → 困るのはここから(放置の連鎖)→ 売り場は同じ物ばかり → でも変わった(解決)→ 記事へ(登録不要)→ 在庫 →「もう〜しなくていい」×3 →「だいじょうぶ。下をタップ」
- 全部ひらがな。数字もひらがな(Sunoは漢字と数字で崩れる)。
- 商品名は歌わない(画面のラベルで出す)。金額なし。約束は商品ページの範囲。
- 見せてから作曲する。
2. 歌(Suno・Playwright)
- ログインだけ本人。あとは
suno.com/create?mode=CUSTOMに歌詞・Style・Title を流し込んで Create。 - Style(当たり):
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 - 1回で2曲出る。両方 whisper で全文起こして歌詞と照合し、崩れていない方を採る。
- ダウンロードは曲ページの More options → Download → MP3 →「Unlock & Download」(無料枠を1回使う)。
cdn1.suno.aiは403、再生用のm4aは暗号化。ブラウザ内のblobを抜いて回数を回避するのはやらない。 song/song.mp3に置く。
3. テロップ文(captions.txt)
- 歌詞の1段落=1行。行の中を
/で区切る(1かたまり=画面に1回出る文字。12字前後まで)。 - 漢字かな混じりの読みやすい日本語に直す(歌詞はひらがなでもテロップは普通の表記)。
- 2行で出したい所は
|(例:下のボタンから|記事を読む)。
4. 1文字単位の時刻合わせ
uv run --python 3.12 --with openai-whisper python $S/scripts/align.py $AD
→ $AD/cap_events.json(かたまりごとの 出る秒・消える秒)と $AD/line_starts.json(各行の歌い出し)。
- whisper-cli の行単位の時刻は使わない。イントロ(歌のない頭の数秒)を0秒から歌っている扱いにし、行の中も均等割りになるので、テロップが「歌ってないのに進む」。
- 歌の止まっている間(0.8秒以上)はテロップを消す。
- 一致率が80%を切ったら、歌が崩れているか captions.txt が歌詞とずれている。
5. 場面割り(shots.py)
STYLE(全カット共通の絵柄の文)とSHOTS = [(行番号, その行の何カット目, 場面, 動き), ...]。- 1カット2〜5秒。長い行は2〜3カットに割る。3分半で約60カット。
- 長尺は全部を動画にしない:静止画をゆっくり寄せるカットを半分混ぜると、動画生成が半分で済む。
- キャラ:先生(ボブ・エプロン)/ママ(低いお団子・ストライプ)/子ども(おかっぱ)/体の中の伝令2人(縞のつなぎ・帽子)/悪役(黒い丸・角・スポンジとストロー)/商品(実物の柄)。
6. キャラ表とキーフレーム(Higgsfield gpt_image_2_5・9:16・1枚0.25)
- まずキャラ表を1枚作り、以後の全カットに キャラ表+参考の1コマ の2枚を
image_referencesで渡す。 - 文頭に
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。 python3 $S/scripts/make_reqs.py $AD image→ 12件ずつgenerate_image_batch。- 子どもは服を着せる(下着・おむつ姿は nsfw で落ちる)。参考の1コマに下着が写っているとそれだけで落ちる。
- 商品は実物の柄をプロンプトに書く(存在しない柄が出たら作り直し)。
- 全部をコンタクトシートで見る:絵柄・文字なし・キャラ表どおり。
7. 動画化(Grok Imagine grok_video_v15_lite・720p・start_image のみ・1秒≒1クレジット)
python3 $S/scripts/make_reqs.py $AD video→ 12件ずつgenerate_video_batch。長さは担当区間+0.5秒(3〜8秒)。- プロンプトは「この絵を動かす:<動き>。同じ絵柄・構図・キャラのまま、弾むラバーホースの動き、フィルムのちらつき、カメラ固定、カットなし、新しい文字なし」。
- 落ちた時に preset が返ってきたら
declined_preset_idを params に入れて出し直す。 - 終わりのコマを全部並べて見る。 商品が横を向く・回ると柄が崩れる → 崩れる前の秒までだけ使う(
SRC_MAX)か、正面の部分を往復させる。
8. 背景の書き出し(文字なし)
python3 $S/scripts/bg.py $AD # → $AD/bg.mp4(1080x1920・30fps)と shot_starts.json
- カットの切り替えは各行の歌い出しに合わせる(line_starts.json)。最初のカットは0秒から。
- 歌のない頭のイントロは、強い場面(悪役・大洪水・ハンコで潰す等)を速く切って埋める案もある。
9. Remotion($AD/remotion)
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 "トイトレ中のママへ" --hook-main "オムツが外れない" --hook-mark "本当の理由" --brand-name "<商品名>" --keys "<黄色にする決め言葉,カンマ区切り>"- 載せる物(
src/SongAd.tsx):- 上のフック:紙のカード(黒い太枠・ずらした影・少し傾き)で「誰向けか+何の話か」を最初の1秒で分からせる。例「トイトレ中のママへ/オムツが外れない本当の理由」。
- テロップ:黒い角丸に白い太字、ポンと弾んで出る。決め言葉だけ黄色(白黒の中の唯一の色)。
- 商品名ラベル:商品が出るカットだけ上に小さく。
- CTA:最後の「下をタップ」から終わりまで、黄色い矢印3段が下へ流れて弾む+「下のボタンから|記事を読む」。
- 書き出し(このMacはヘッドレスChromeが単体で落ちる →
chrome-wrap.sh、サンドボックス外):
cd $AD/remotion && NODE_USE_ENV_PROXY=1 npx remotion render src/index.ts SongAd out/<広告ID>.mp4 --browser-executable=./chrome-wrap.sh --concurrency=6 --crf=20
- 納品は
-crf 23 -movflags +faststartで再エンコード(白黒の粒子で重い。3分半で約200MB)。
10. 検品(openの前に必ず)
- 約1秒おき24コマのコンタクトシートを読む:テロップが歌とずれていない/歌のない所で出ていない/商品の柄が崩れていない/CTAの矢印が画面内に収まっている/余計な文字がない。
- 最初の3秒だけ別に見る(フックが一目で分かるか)。
- HTMLのプレビューページを作って open。広告名は AdLab の発行名(H-XXX-000a)をファイル名にそのまま使う。
ハマった所(今回の実測)
- テロップが冒頭からずれる:歌い出しは4.9秒だったのに whisper-cli は0秒からとした →
align.py(openai-whisper の word_timestamps+difflib で文字合わせ)で解決。 - キーフレームの nsfw:下着姿の子ども・参考画像の下着 → 服を着せる/参考は絵柄の文で言う。
- 商品が回って崩れる:Grok はよく横を向く。正面の秒だけ使う。
- 作りすぎ:画像61枚+動画61本を1本で使った。次は在庫と静止画の寄りで半分にする。
- 長い最後の文字が1字だけ次の行に落ちる →
|で行を指定する。
関連:reference/lyrics.md(歌詞の型と実例)、/zackd-ad(3Dアニメ型)、AdLab(作る前に仮説を登録)。