code-video:大規模言語モデルにコードを書かせ、フレームごとに解説動画を描画する
画面は動画生成モデルで作られたものではなく、撮影されたものでもありません。モデルがウェブページ(Canvas / SVG / Three.js / WebGL)を作成し、ページが __render(t) を公開し、スクリプトがフレーム番号ごとにスクリーンショットを取得し、ffmpeg で mp4 にエンコードします。
利点は再現可能、修正可能、文字と数字が完全に正確、生成コストゼロであることです。
全体の流れはたったの3ステップ:画風を選ぶ → プロンプトを詳細に書く → まずフレーム抽出して確認し、修正してから全編をレンダリングする。
0. 準備(初回使用時)
cd <スキルディレクトリ>
npm install # playwright-core をインストール
npx playwright-core install chromium-headless-shell # または CHROME=/path/to/chrome を設定
# ffmpeg / ffprobe が PATH 上にある必要があります;日本語フォントは Noto Sans CJK(fonts-noto-cjk / noto-fonts-cjk)を推奨
pip install edge-tts numpy scipy # オプション:ナレーション、字幕、BGM合成
1. 画風を選ぶ:画風はテーマに合わせる
まずテーマがどのカテゴリに属するかを見極め、references/styles/ にある対応する画風ファイルを読み込みます(各ファイルには:どんなテーマに適しているか、簡易プロンプト、完全プロンプト、制作メモ、事実の根拠が含まれ、サンプルソースコードは examples/<name>/ にあります)。
| テーマ | 最初に試す | 代わりに使える |
|---|---|---|
| 故事、歴史物語 | 影絵 piying | ピクセル pixel、漫画 comic |
| 低年齢向け解説、自然の成長 | 絵本 picturebook | フラットMG flatmg |
| 文字、器物、時代 | 漢字の変遷 hanzi | 水墨 ink |
| ルート、遣使、移住 | 古地図 oldmap | 等角投影 isometric |
| 機械構造、製品分解 | 3D分解 explode3d | ホワイトボード whiteboard |
| 工程、生産ライン | 等角投影 isometric | フラットMG flatmg |
| 大きさ、深さ、距離 | 縮尺 scale | データインフォグラフィック infographic |
| 年代、桁数 | データインフォグラフィック infographic | 縮尺 scale |
| 定理、公式 | 数学可視化 math | 線画 lineart |
| 単一の原理、因果関係 | ホワイトボード whiteboard | 線画 lineart |
| スローガン、オープニング/エンディング | キネティック kinetic | — |
全16種類の詳細は references/styles/README.md を参照してください。ユーザーが特定の画風を指定した場合は、そのファイルを直接読み込んでください。
2. プロンプトを詳細に書く:「10秒の影絵劇を作って」という一言だけでは画風しか決まらない
「10秒の影絵劇を作って」のような一言プロンプトでは、モデルは画風しか分からず、何を演じるか、どの数値を使うか、各セクションの長さは何秒かが分かりません。以下の骨格に沿って4つの項目を明確に記述します(画風ファイル内の完全プロンプトはすべてこの構造です):
Canvas を使って、1920×1080、30fps、約12秒の「画風」解説アニメーションをフレームごとに描画します:配色、質感、フォントはそれぞれどのように決めるか。
テーマは「……」:いくつかのシーンに分け、各シーンで何を演じ、画面上にどのテキストを表示し、最後に一言でまとめる。
使用する数値:……、出典は notes.txt に記載;確かでない数値は一桁単位まで正確にしない。
各フレームは時間 t のみで決定される(タイマーは使わず、乱数は固定シードを使用)。
まず4~6フレームを抽出して、字幕が十分大きいか、ラベルの重なりやフレーム外へのはみ出しがないかを確認し、修正後に全フレームをレンダリングする。
さらに、テーマに関して間違えやすい物理的/事実上のルールを補足します。例:曹冲が象の重さを量る話では、船の喫水は積載量に応じて計算する必要があり、そうしないと印と水位線が合いません。このルールを書かないと、モデルが描画したものは間違ったものになります。
3. ページを作成:必ず守るべきページコントラクト
各動画は1つのディレクトリ <work>/<name>/ に格納し、最低でも index.html が必要です。prompt.txt(使用したプロンプト)と notes.txt(数値と事実の出典)も併せて作成することを推奨します。templates/canvas/index.html を出発点として使用できます。
- ページは固定で 1920×1080、
overflow:hiddenを設定。 - 以下の行が必須です(render.sh が正規表現で読み取ります):
const FPS = 30, DUR = 12; window.__info = { fps: FPS, duration: DUR, frames: Math.round(FPS * DUR) };window.__render = t => { ...; return { seg: '<シーン名>', tau: t.toFixed(2) }; }各フレームは t のみで決定される:CSS animation/transition、setTimeout、requestAnimationFrame、Date.now()、Math.random()は使用禁止;乱数は固定シード(例:mulberry32)を使用;パーティクル、振動、物理はすべて t から直接計算可能である必要があり、フレーム間で状態を蓄積しない。- フォントとリソースの読み込みが完了したら
window.__ready = trueを設定する(WebGL ページでは最初のフレームのレンダリング後に true に設定)。 - 外部ネットワークにはアクセスしない(フレーム取得スクリプトがすべての外部リクエストをブロックします)。フォントはシステムにインストール済みのフォントを使用する。
- スマートフォンでも見やすいように:本文ラベルは 36px 以上、タイトルは 64px 以上、左下のフッターの小さなテキストはオプション。
- Three.js ページ:
scripts/fetch_three.sh <name>を実行して<name>/vendor/にダウンロードし、<script type="importmap">{ "imports": { "three": "./vendor/three.module.js", "three/addons/": "./vendor/jsm/" } }</script>を追加; renderer はpreserveDrawingBuffer: trueに設定し、__render(t)内で同期的にrenderer.render()(または composer)を呼び出す。
詳細とよくある落とし穴は references/page-contract.md を参照してください。
4. まずフレーム抽出してモデルに確認させ、修正してから全編をレンダリング
scripts/render.sh <work>/<name> 30,120,210,300 # これらのフレームだけを切り出す -> <work>/_preview/<name>/
画像ビューアで1枚ずつ確認し、以下のチェックリストに沿って問題を洗い出します。修正後、再度抽出して確認します(最低1イテレーション)
- テキスト:十分大きいか、重なり、フレーム外へのはみ出し、遮蔽がないか;中国語がすべて実フォントで表示されているか(豆腐ブロックがないか)。
- 事実:画面上の数字、年代、地名、数式が notes.txt と一致しているか;模式的な画面には「模式図」と明記。
- 構図:主なオブジェクトは十分大きいか(遠景の主役は画面高さの1/4以上)、画面が空すぎないか、配色はテーマに合っているか。
- 動作:重要な動作が実際に発生しているか(隣接する2フレームを確認)、トランジションが不自然でないか。
- WebGL:黒いブロック、斑点、ちらつきがないか。
問題がなければ全編をレンダリング:
scripts/render.sh <work>/<name> # -> <work>/<name>.mp4
N=8 scripts/render.sh <work>/<name> # 並列フレーム取得プロセス数(デフォルト min(6, コア数/2))
GPU_ANGLE=vulkan scripts/render.sh ... # WebGL に GPU を使用(高速だが、連続フレーム取得で黒い斑点が出る可能性があるので、先に抽出確認)
エンコーダは自動選択:NVENC が利用可能なら h264_nvenc、そうでなければ libx264。
フレーム取得は主に CPU を消費します。時間のかかる部分は通常「モデルがページを書き、スクリーンショットを見て、コードを修正する」ことであり、レンダリング自体ではありません。
5. オプション:ナレーション、字幕、BGM、プレビュー画像
# ナレーション + 字幕(デフォルトは無料の edge-tts;ナレーション全体を一度に合成することで音声の一貫性を確保)
python3 scripts/tts.py script.txt -o out/narration --voice ja-JP-NanamiNeural --rate -5%
# BGM:自分で合成する(著作権フリー)、またはロイヤリティフリーの音楽ライブラリを使用(references/narration.md を参照)
python3 scripts/chiptune.py out/bgm.wav 30 # 8ビットチップチューン
python3 scripts/beat.py out/beat.wav --bpm 120 --beats 20 # ビートに合わせたドラム、フレームに同期
# 合成:音楽は自動的に音声を避け、ラウドネスを -16 LUFS に正規化;--burn で字幕を映像に焼き付け
scripts/mux.sh out/<name>.mp4 out/<name>.final.mp4 --voice out/narration.mp3 --music out/bgm.wav --srt out/narration.srt --burn
# プレビュー GIF / カバーフレーム
scripts/gif.sh out/<name>.mp4 2.5 4 # 2.5秒から4秒間
scripts/gif.sh --poster out/<name>.mp4 6.0 # 1フレームをjpgで抽出
字幕を焼き付ける場合、ページ下部の1/5は空けておき、他の字幕バーを描画しない(または SUB_MARGIN=60 で字幕を上げる);
ラウドネス目標は -16 LUFS、音声のピークが高い場合は少し低くなることがあります(真のピーク上限 -1.5 dBTP)。
音声の選択、カスタムTTSの統合、ビート同期の方法は references/narration.md を参照してください。
6. 複数動画を一度に作成(画風比較、シリーズ動画)
各動画は1つのディレクトリと1つのプロンプトを持ち、互いに依存しません。複数のサブエージェントにページ作成と自己チェックを並行して割り当てることができ、 メインエージェントは最後に統一レンダリング、結合、チェックのみを行います。各サブエージェントへの指示には、ページコントラクト、ディレクトリ規則、 フレーム抽出自己チェックリスト、事実は notes.txt に記述すること、完了後に長さと自己評価を報告することを明確に記述します。
作成前に明確にすること
- 事実に基づく数字は検証可能なものでなければなりません;ニュースや新種のテーマを扱う場合、公開済みの情報のみを描画し、隅に「模式図」と表示します。
- サンプル内のテーマに関する数字を汎用的な事実として再利用しないでください;テーマを変更した場合は再検証してください。
- モデルが生成したBGM(例:MusicGenの重み)は非商用ライセンスであることが多いです;商用公開の場合は、自作または明示的にライセンスされた音楽を使用してください。