Communitygithub.com

Ameetms/Claude-skill-block-toon-story-video

スクリプトやナレーションから、90×16の一人称視点のストーリーナレーション動画を、丸みを帯びた3Dブロックトゥーン風(角丸ボックスキャラクター、ペイントされた顔、インクの輪郭線、常に動くカメラ、単語ごとのカラーキャプション)で、すべてコードベースで生成します。

Claude-skill-block-toon-story-video とは?

このスキルは、与えられたスクリプトまたは音声ナレーションから、縦長(720×1280、30fps)の一人称視点ストーリー動画を生成するAIエージェント(Claude Code、Codexなど)向けのものです。動画は、角丸ブロックで構成された3Dキャラクター、常に動き続けるカメラワーク、単語ごとに色分けされたキャプションを特徴とし、すべてthree.jsとヘッドレスChromium、ffmpegを用いたコードベースでレンダリングされます。主な用途は「靴磨き男にバレた」のような、短くてテンポの良いナレーション動画の作成です。このスキルは、キャラクターデザインの忠実性とアニメーションの滑らかさを最優先します。具体的な手順として、まずキャラクターと環境のデザインシートを生成し、その後、音声に合わせたリップシンク、ショットリストの作成、ビデオのレンダリング、最終的なオーディオミックスと品質チェックまでを一貫して実行します。サポートするプラットフォームは、three.jsライブラリとNode.js環境(Playwrightを含む)であり、出力はMP4動画ファイルと静止画サムネイルです。このスキルは、特に「すべてコードで完結する」という点が強みで、キャラクター、カメラ、エフェクトのすべてが決定論的な`frame(t)`関数によって制御され、再現性が高く、並列レンダリングも可能です。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Ameetms/Claude-skill-block-toon-story-video/tree/HEAD

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

ブロックトーンストーリー動画(「靴磨き男にバレた」風)

ユーザーが、縦長(720×1280または1080×1920、30fps)の一人称視点ストーリー動画(30〜130秒)を、ナレーション付きで、丸みを帯びたブロック状の3Dキャラクター、滑らかで連続的な動き、そしてパンチの効いたカラーキャプションとともに作成したい場合に使用します。すべてコードベースです:ヘッドレスChromium上のthree.jsシーン、決定論的な frame(t) 関数、そしてffmpegにパイプされるスクリーンショット。

ユーザーの優先事項はキャラクターデザインの忠実性とアニメーションの滑らかさです。ステップ5のキャラクターチェックを省略しないでください。

1. 参考動画の見た目(計測値)

  • 720×1280、30fps、約122秒、AACステレオ、音声の下に静かな連続した背景音がある約-14 LUFS。
  • 約47回のハードカット(シングルフレームカット、クロスフェードなし)、平均ショット約2.6秒(範囲1.2〜5秒)。ショット内ではカメラは常に動いています:イーズインのプッシュイン、プルアウト、クレーン/ハイアングルドリフト、ローアングルプッシュ、天井や窓へのホイップティルト。フレームの1%未満が静止画です。
  • キャプションは画面下部3分の1(上部から約64〜68%の高さ)、1グループあたり2〜3語の小文字、太字の角丸ジオメトリックサンセリフ体(Poppins ExtraBold)、白地にキーワードが色付け(ゴールド #f5c32c、シアン #48c9f0、コーラル #ff5b4d、ピンク #ff7ac0、グリーン #2ecc8f、オレンジ #ffa21f)、太い黒いストローク、各グループに小さなスケールポップ。
  • 環境:暖かみのあるクリーム/ベージュのインテリア(壁 #dcbf96、床 #c7aa7d、石 #d9c39b、腰板 #b99a6f、空 #c7dcec)にアクセントカラーのマルーン #570d0e、ネイビー #1c2a49、ゴールド #e0990d、ティール #1f8f8c、レッド #a3160f。
  • 照明:柔らかく、明るく、やや暖かめ。滑らかなグラデーションシェーディングと柔らかいキャストシャドウ。ハードなセル境界はありません。

2. キャラクターデザインのバイブル(厳守)

シルエットとプロポーション

  • すべての身体パーツは角丸ボックスです。ベベル半径はパーツの最小寸法の約15〜18%です。ボディパーツに球体やカプセルを使用しないでください。
  • 頭部:幅が高さよりわずかに広い直方体(0.44 x 0.41 x 0.39 単位)、クローズアップショットではフィギュアの高さの約1/4〜1/3、ワイドショットではフィギュアは約4頭身。短い首ブロック。胴体 0.46 x 0.56 x 0.26。腕 0.27 + 0.26(上腕 + 前腕)。脚 0.30 + 0.27。全高約1.7単位。
  • 手は分厚いミトンブロック:掌ブロックと、4本の短い2セグメントの指、および独立した親指。開いた状態から握り拳まで曲げられ、広げることもできます。手は決して蜘蛛のように見えてはいけません。
  • 各手首に白いカフバンド。黒い靴、つま先が丸い平らなブロック。
  • 関節の切れ目が見えてはいけません。 2つの角丸ボックスが曲がった関節(股関節、膝、肩、肘、手首、首/胴体、股関節/胴体)で接する場合、ベベルが隙間やノッチを開き、それが切れ目として読まれます。すべての関節を、衣類と同じ色の布の層で隠します:関節のピボットを中心とした小さな球体(ball())、手足の半幅+約0.002のサイズで、手足に沿って伸ばします(sc [1,1.2-1.4,1])。2つの股関節を接続する骨盤ブロック。首/胴体の継ぎ目にかぶせるカラーブロック。カフの下の肌色の手首ピース。ボールのサイズを大きくしすぎないでください(手足の半幅+0.015だと、ゴツゴツした膝や肘になります)。曲げたポーズ(腕を上げる、歩行中のストライド、脚を上げる)でストレステストを行い、拡大して確認してください。

顔(頭部前面にフラットにペイント、キャンバステクスチャに毎フレーム再描画)

  • 大きな丸い目:白い強膜、緑または茶色の虹彩、黒い瞳孔、白いハイライト。太い黒い上まぶたのライン。目は見回す(lookX/lookY)、まばたき、細めることができます。
  • 太く先細りの眉で、傾き(怒り/心配)と上げ下げが可能。黒いリングのメガネは目よりもはるかに大きい(リング半径は目の半径の約2倍)。メガネは接続された1つのフレームである必要があります: 2つのリングは鼻の両側にまたがる湾曲したブリッジアークで結合され、各リングには顔の端まで伸びるテンプルラインがあります。2つの浮いたリングは壊れて見えます(最初のキャストシートでユーザーが指摘しました)。
  • 鼻はなし。口は小さな線/曲線、または話すときはピンクの舌が詰まった塗りつぶされた開口。笑顔の値は-1..1。
  • オプションのソフトな頬紅。

髪、帽子、アクセサリー(積み重ねられた分厚いブロック)

  • 主人公:茶色のクイフ(前に跳ねた髪型)で尖った房、サイドピース、バックブロック。その他:短い、または長い(サイドカーテン+バックブロック)。黒い前髪は薄い斜めのブロック。
  • 警察官の帽子:ネイビーのクラウンブロック+黒いブリム+金のバッジ。フラットキャップ:茶色のブロック+短いつば+明るいボタン。
  • 年配の男性:白いブロックの口ひげ+白いこめかみパッチ。
  • 服装はカラーブロックでシンプル:スーツ(マルーンのジャケット、白いシャツ、濃いネクタイ、ラペル、金のランヤードV+青いストライプの白いIDバッジ)、制服(ネイビー、金のバッジ、黒いベルト+金のバックル)、エプロン(白いシャツ、茶色のエプロンにボタンドット)、その他エキストラ用にグレー/グリーン/タン/ネイビー/ティールのバリエーション。
  • 肌のトーン:ピーチ #efaa71、タン #d89a62、ブラウン #8a5230、ダーク #5a3320、ライト #f4c49a。髪は茶色 #59321a、黒 #14110f。

レンダリング

  • すべてのパーツに、反転ハルシェーダーと画面一定幅の細い暗いインクアウトライン(#1c0f06)。幅はカメラ距離の割合です: w = px / ((H/2) / tan(fov/2)) ここで px は希望するラインの太さ(画面ピクセル単位、720×1280ビデオショットで約0.003、約1.5〜2 px)。遠距離のマルチキャラクターシート(1600×900)では K.setOutlineWidth(.0009) を使用し、バストタイルでは約 .0016;デフォルトは遠方カメラでは太すぎます。顔や小さなトリムピースはアウトラインをスキップします。
  • Lambertマテリアル、半球照明(約2.9)+ 1つのソフトシャドウサン(約1.5)。ソフトPCFシャドウ。
  • 背景の群衆は lod: 1 を使用(指なし、髪ブロック少なめ)。エキストラごとにユニークなデザインを作成しないでください。肌、髪、服装の色と髪型を変更します。

バラエティルール: すべての名前付きキャラクターは、シルエットだけで認識可能でなければなりません(帽子、髪型ブロック、メガネ、口ひげ、肌のトーン、服装の色)。すべてのキャラクターに同じプロポーションを使用します。

リファレンスシート(ユーザーがキャラクターデザインや環境を要求した場合、ビデオを作成する前に行います)

scene.html の隣に designsheet.html と envsheet.html を1つずつ作成します(同じキット、同じ render.cjs、node render.cjs designsheet.html out 0,1,2 --w 1600 --h 900 を実行)。frame(t) は Math.round(t) でモードを選択します。ラベルは、3Dポイントを new THREE.Vector3(x,y,z).project(camera) で投影して配置するDOMオーバーレイ(Poppins)です。ページタイトルとスウォッチもDOMです。

  • キャストファイル cast.js:DEF テーブル(名前、役割、4つのスウォッチ[肌、髪、服装、アクセント])、キャラクターごとに1つのビルダーを持つ BUILD テーブル(makeChar + いくつかの追加の box() アクセサリー)、makeOne(name)、makeCast()、および毎フレーム顔とポーズをリセットし、クリーンなモデルシートポーズを適用する pose(name, C)。各キャラクターをシルエットでユニークにします:異なる scale/headScale、髪型、帽子、メガネ、口ひげブロック、肌のトーン、1つのアクセントカラー。
  • シート0のラインナップ:すべてのキャラクターを一列に並べる(カメラ約[0,1.02,15]、fov 16.5)、各キャラクターの下に名前+役割+スウォッチ正方形。シート1:主人公のヨー角度0、.75、PI/2、PIでのターンアラウンド(正面、3/4、横、背面)。シート2:表情:6つのバスト(ニュートラル、ハッピー、怒り、驚き、心配、自己満足)をシザータイルとして描画。これは、6人のキャラクターを1つの3Dシーンに積み重ねると、列が床の下や重なり合ってしまうためです。タイルパターン:
renderer.autoClear=false; renderer.setScissorTest(true);

その後、タイルごとに

setViewport(x, H-y-h, w, h); setScissor(...); renderer.clear(); camera.aspect = w/h; camera.updateProjectionMatrix(); renderer.render(scene, camera);

そして、frame の終わりにビューポート、シザーテスト、autoClearをリセットします。

  • 眉の符号規則:face.brow = [高さ, 傾き, アーチ]、傾き > 0 は内側が下がる(怒り)、傾き < 0 は内側が上がる(心配)。drawFace では ang = -s * f.brow[1] * .55 です(最初のバージョンでは反転しており、怒った警官が悲しそうに見えました)。
  • 環境シート0:4つの760×318のシザータイル(入り口からのワイド、アーチ型窓のある端壁、キープロップコーナー、hall.ceiling.visible = false にしたハイアングルクレーン)、それぞれにキャプション。スケール感を出すためにキャストをセット内に配置し、カメラまたは互いに向き合うようにします(ヨーを確認:ヨー0は+Z方向のカメラに向かって+Zを向きます。カメラに背を向けているキャラクターはステージングのバグです)。シート1:各プロップを遠く離れたプロップステージ(x = 400 + i*20)に単独で配置。ホールショットに表示されないようにします。タイルは486×300。シート2:俯瞰フロアプラン。up = [-1,0,0](長軸は水平に走る)、カメラは約[-1,120,-13]を真下に見下ろし、狭いfov(約9.3)、ワールドポイントから投影されたピンラベル。ヘッダーには常にパレットストリップ(10スウォッチ)を含めます。
  • ホールの天井と梁は、H.ceiling として公開された1つのグループに存在します(シャドウキャストはオフ、これにより太陽はまだホールを照らします)。クレーンショットとトップダウンショットでは非表示にします。看板のテキストはその平面にサイズを合わせる必要があります:1.9×0.5の看板には 800 56px Poppins が必要で、78px(クリップされる)では不十分です。
  • Three.jsの落とし穴:Object3D.position と .rotation は読み取り専用プロパティです。コンポーネントを設定し(m.rotation.x = ..., m.position.set(...))、Object.assign(mesh, { rotation: ... }) を決して行わないでください。これは例外をスローし、__ready を停止します。
  • PNGと .js ファイルを SendUserFile で配信します。キットを変更した後は、すべてのシートを再レンダリングして再送信し、ユーザーが古いシートを保持しないようにします。

3. アニメーションのスムーズさのルール

  1. アニメーションは時間の純粋関数です:frame(t) は t からすべてを設定します。フレーム間で状態は保持されないため、任意のフレームを任意の順序または並行してレンダリングできます。
  2. すべての動きに kf([[t0,v0],[t1,v1]], t, easeFn) を使用します。プッシュインや到着には easeOut を、クレーン/ウィップの動きには easeIO を、小さなものには ease を使用します。カメラや手足にはリニアを使用しないでください。
  3. カメラは決して静止しません。各ショットには、イーズされた位置+注視点+fov+数度のロールに加え、常時オンになっている手持ちドリフト [sin(.5t)*.03, sin(.37t)*.02] があります。
  4. ショット境界でのみハードカット。単語やビートに合わせます。クロスフェードはなし。平均ショットは2〜3秒。
  5. すべてのキャラクターに常にアイドルレイヤー:呼吸ボブ、揺れ、頭のロール、約3〜4秒ごとのまばたき(0.13秒イーズ)、小さな目の動き。K.idle() がこれを行います。
  6. 口の開き具合は、実際のナレーションのラウドネスから取得します(lipsync.py が30fpsでenv.jsonを書き込みます)。ループするフラップは使用しないでください。
  7. ジェスチャー:予備動作(指差し/叩きつけの前の小さな引き戻し)、オーバーシュート(easeBack)、ホールド、セトリング。頭が先行し、胴体がそれに続きます(twist の前に headYaw を使用)。
  8. 歩行サイクル:脚は逆位相、各腕は同じ側の脚とは逆に振られます。膝は後ろにのみ曲がります(Math.max(0, knee))。1サイクルあたり2回のボブ、小さな胴体のひねりと傾き。位相はサイクル単位:通常の歩行では約1サイクル/秒を使用。
  9. キャラクターは家具と重なってはいけません。明示的なx/zで配置し、静止画で確認します。

4. パイプライン

  1. スクリプト:一人称、最初の3秒でフック、短くパンチの効いた文、エスカレート、ひねり、最後のラインで落ち。約2.5語/秒。
  2. ナレーション:通常、ユーザーがElevenLabsファイルを提供します。そうでない場合は、それを依頼するか、プレースホルダートラックを合成します。voice.wav として保存します。
  3. 単語のタイミング:単語レベルのタイムスタンプを取得します(スクリプトテキストに対する強制アラインメント、例:pocketsphinx または whisper の単語タイムスタンプ)。words.json を [{"w":"my","s":0.20,"e":0.41}, ...] として保存し、スクリプトのスペルと句読点を使用します(キャプションは末尾の句読点を使用してグループを分割します)。
  4. リップシンクエンベロープ:
python3 lipsync.py voice.wav env.json
  1. ショットリスト:ショットごとに1行:開始/終了時間、視聴者が見るもの、カメラの動き、誰が演技するか。約2.6秒ごとにカットすることを目指します。フレーミングを変える:顔のクローズアップ、ミディアムツーショット、ローアングルプッシュイン、群衆の上のハイアングルクレーン、ホイップティルト、オーバーザショルダー、プロップ(電話、バッジ)のインサート。scene.html の SHOTS にショットを書き込みます。
  2. blockkit.js / props.js からキャスト+セットを構築します(コードを参照)。room()、chair()、bench()、crowd() を再利用します。上記のパレットの box() を使用して新しいプロップを構築します。
  3. まず静止画をチェックします(低コスト):
node render.cjs scene.html outstills 1,4,7,...

すべての名前付きキャラクターのクローズアップ、1つの歩行、1つのジェスチャー、1つのワイドを確認します。ビデオをレンダリングする前に修正します。 8. ビデオのレンダリング(720×1280の2コアで約1〜1.7秒/フレーム、つまり40秒で約15〜25分。チャンクを並行してバックグラウンドで実行します):

export NODE_PATH=$(npm root -g)      # playwright はサンドボックス内でグローバルです。それ以外の場合は: npm i playwright && npx playwright install chromium
S=30; for i in 0 1; do (node render.cjs scene.html part$i.mp4 --from $((i*S)) --to $(((i+1)*S)) > r$i.log 2>&1 &); done   # 30秒のチャンクを2つ。両方の終了を待ちます
printf "file 'part0.mp4'\nfile 'part1.mp4'\n" > list.txt && ffmpeg -y -f concat -safe 0 -i list.txt -c copy silent.mp4
  1. オーディオ + ミックス(音声の下に連続した静かな背景音、約-14 LUFS):
D=130   # >= ビデオの長さ
ffmpeg -y -f lavfi -i "sine=f=55:d=$D" -f lavfi -i "sine=f=82.4:d=$D" -f lavfi -i "sine=f=110:d=$D" -filter_complex "[0][1][2]amix=inputs=3:normalize=0,lowpass=f=500,tremolo=f=0.12:d=0.35,aformat=channel_layouts=stereo,afade=t=in:d=1[bed]" -map "[bed]" -ar 44100 bed.wav
ffmpeg -y -i silent.mp4 -i voice.wav -i bed.wav -filter_complex "[2:a]volume=0.12[b];[1:a][b]amix=inputs=2:duration=first:normalize=0,loudnorm=I=-14:TP=-1.5:LRA=7,aformat=channel_layouts=stereo[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k final.mp4
  1. 配信前のQC:(a)コンタクトシート、約2.6秒ごとに1フレーム、キャラクター/キャプションを確認。(b)モーションチェック、ダウンスケールされたグレイストリームの平均フレーム差分、0の静止フレームを期待:
python3 - <<'E'
import subprocess, numpy as np
W,H=90,160
a=np.frombuffer(subprocess.run(['ffmpeg','-v','error','-i','final.mp4','-vf',f'scale={W}:{H},format=gray','-f','rawvideo','-'],capture_output=True).stdout,dtype=np.uint8).reshape(-1,H,W).astype(np.float32)
d=np.abs(np.diff(a,axis=0)).mean(axis=(1,2)); print(len(a),'frames; static:',(d<.02).sum(),'; spikes(cuts):',(d>14).sum())
E

(c)ラウドネス ffmpeg -i final.mp4 -af ebur128 -f null -(約-14 LUFS)、(d)キャプションが音声と一致しているか。 SendUserFile で配信します(mp4 + カバー静止画)。

5. セットアップ

mkdir -p work && cd work && npm init -y >/dev/null && npm i [email protected] @fontsource/poppins
# このフォルダに以下のファイルを作成します: blockkit.js props.js captions.js render.cjs lipsync.py scene.html
# (または、このリポジトリの kit/ フォルダから既製のファイルをコピーします。そこには標準のシーンとその他も含まれています)

関連スキル