ビデオ解説生成
あなたはモーションデザイナーが行う方法で動画を作成しますが、それはコードで行われます。各シーンは小さなJSモジュールで、DOM/SVGを構築し、一時停止状態のGSAPタイムラインにトゥイーンを追加します。レンダラーはそのタイムラインをフレームごとにシークし(t = frame / fps)、各フレームをスクリーンショットとして取得し、ffmpegにパイプします。出力は決定論的です。同じプロジェクトは常に同じフレームをレンダリングします。
SKILL_DIR は、このスキルのベースディレクトリ(このファイルを含むフォルダ)を意味します。
セットアップ(各マシンで一度)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
ffmpeg がPATHに含まれている必要もあります。すべてのスクリプトは、依存関係が不足している場合にこのコマンドを出力します。
ワークフロー
- 最初にストーリーボード。トピック、対象者、目標の長さ、ナレーションの有無について合意し、シーンリストを
video.jsonに記述します。各シーンにはnarration(ナレーションの内容)とvisual(画面に表示される内容)を設定します。reference/storyboard.mdを参照してください。ストーリーボードが単独で意味をなすまで、シーンコードを書かないでください。 - スキャフォールド:
node SKILL_DIR/scripts/new.mjs <project-dir> --title "..."を実行し、video.jsonを編集して、scenes/<id>.jsにシーンごとに1つのモジュールを作成します。レシピについてはreference/animation-patterns.mdを、レイアウト、タイポグラフィ、色についてはreference/design.mdを参照してください。ナレーションがある場合は、シーン長を"auto"に設定し、cue('phrase')でアニメーションのタイミングを調整します。両方の例は完全なナレーション付き動画です:examples/kubernetes-overview/(ElevenLabs)とexamples/how-dns-works/(Gemini)。 - ナレーション(必要な場合):
reference/voiceover.mdに従ってください。簡単に言うと、video.jsonに"voice"ブロックを追加し、node SKILL_DIR/scripts/narration.mjs status <project-dir>を実行し、リストされたシーンごとにOpenRouter MCPのgenerate-speechツールを、出力される引数とまったく同じように1回呼び出し、各保存ファイルをnarration.mjs addで登録します。これはチェック(ステップ4)の前に行ってください。実際のオーディオはシーン長とcue()のタイミングを変更するためです。 - チェック:
node SKILL_DIR/scripts/lint.mjs <project-dir>を実行します。Chromiumですべてのシーンをロードし、ロードエラー、シーンより長いアニメーション、tlの外部で作成されたアニメーション、フレーム外の要素、クリップされたテキスト、シーンに対して長すぎるナレーションを報告します。意図的でない限り、すべての警告を修正してください。 - 確認:
node SKILL_DIR/scripts/render.mjs <project-dir> --contact-sheetを実行し、PNGを読んでください(<project-dir>/out/contact-sheet.png)。階層、重なり、可読性、空のフレーム、タイミングを確認します。詳細については、特定の瞬間をレンダリングします:--stills 12.5,30。見た目が良くなるまで繰り返し、最後の編集後にもう一度コンタクトシートのパスを実行します。 - レンダリング:
node SKILL_DIR/scripts/render.mjs <project-dir>→<project-dir>/out/video.mp4。ナレーションクリップは自動的にミックスされます。ユーザーにパス、長さ、シーンリストを伝えてください。ユーザーはnode SKILL_DIR/scripts/serve.mjs <project-dir>でインタラクティブにプレビューできます(スクラバー、再生/一時停止、ナレーション再生、cでキャプション切り替え、mでミュート)。
便利なレンダリングフラグ:--out file.mp4、--from 10 --to 20(部分レンダリング)、--scale 0.5(高速ドラフト)、--workers N、--crf 18、--audio track.mp3(ナレーションの代わりに独自のオーディオトラックを使用)、--no-voice(無音)、--audio-codec aac(デフォルトはMP3。VS Codeでも再生可能)。
プロジェクトレイアウト
my-video/
video.json # タイトル、幅、高さ、fps、シード、トランジション、テーマ、シーン[]
scenes/<id>.js # シーンごとに1つのモジュール
assets/ # 画像/SVG、'/assets/x.svg' として参照
audio/ # ナレーションクリップ + manifest.json(narration.mjs から;コストがかかるため保持)
style.css # オプション、存在すれば自動ロード
out/ # レンダリング出力(contact-sheet.png、stills/、video.mp4)
video.json のシーンフィールド:id(必須、デフォルトのファイル名 scenes/<id>.js でもある)、duration(秒、または "auto":ナレーションオーディオに合わせる、またはオーディオが存在しない場合は読み上げ速度の推定値、アニメーションより短くなることはない)、narration、visual、オプションの file、オプションの transition("fade"、デフォルト、または "cut")。トップレベルの "voice" ブロックについては reference/voiceover.md で説明されています。
シーンの契約
import { h, reveal, pop } from '/__runtime/helpers.js';
export default function ({ el, tl, gsap, theme, width, height, random, onFrame, cue, voice }) {
const title = h('div', { class: 'h1' }, 'Hello');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tl time 0 = このシーンの開始
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('the key idea')); // そのフレーズが発話されたとき
}
el:このシーンのコンテナ。フル1920×1080のステージをカバーし、表示/非表示は自動で行われます。tl:このシーンのタイムライン。すべてのモーションはtlに含める必要があります(またはそれに追加された別のタイムライン)。gsap.to(...)を直接使用したり、CSSのanimation/transition、setTimeout、setInterval、requestAnimationFrame、<video>、またはウォールクロック時間に依存するものを使用しないでください。レンダラーはタイムラインの再生ヘッドのみを移動するため、それ以外のものはMP4でフリーズするか、同期がずれます。cue(phrase, offset = -0.15)は、phrase(大文字小文字を区別しない、このシーンのナレーションに含まれている必要があります)が発話されるシーン時間を返します。オーディオが存在する場合、クリップの測定されたポーズに合わせられるため、節を開始するフレーズは話された単語に一致します。フレーズが見つからない場合はエラーをスローします。ナレーションと同期するすべてのatで使用してください。voiceは、このシーンの発話に関する{ start, duration, end, estimated }を提供します(例:ループ効果をvoice.endまで実行し続けるため)。- フレームごとの描画(キャンバス、プロシージャルモーション)には、
onFrame((sceneTime) => draw(sceneTime))を使用します。シーンが表示されている間、すべてのフレームで呼び出されます。 - オーバーレイ(オプション):
video.jsonの"overlay": "scenes/_overlay.js"は、動画全体のすべてのシーンの上に描画されるモジュールを追加します。タイムコード、プログレスバー、ロゴ、フレームボーダーなどに使用します。{ el, tl, onFrame, scenes, duration, ... }を受け取り、onFrame((t) => ...)はグローバル時間を受け取り、scenesは各シーンのstart/endをリストします。 random()はシーンごとにシードされ、Math.randomもシードされるため、ランダムなレイアウトは再現可能です。- 関数は
asyncにすることができます(例:fetch('/assets/data.json')など)。フォントはシーン実行前にロードされるため、テキストの測定が機能します。 - テーマカラーはCSS変数です:
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger、さらに--fontと--mono。video.json→"theme": { "accent": "#ff3366" }で上書きできます。 - GSAPプラグインはグローバルに登録されています:DrawSVG、MotionPath、MorphSVG、SplitText、Text、ScrambleText、CustomEase。
品質基準
- シーンごとに1つのアイデア。画面上の何かは少なくとも2〜3秒ごとに動くか変化する必要があり、すべてのアニメーションはそれが示すナレーションのラインと同期して着地する必要があります(
cue()を使用;ナレーションは約2.5語/秒です)。 - テキストは大きく:本文は40px以上、ラベルは26px以上(1920×1080ステージ上)。セーフエリア(
.safe、96/128pxのマージン)内に収めてください。 - 完成した図を一度に表示するのではなく、図をピースごとに構築してください。
- 各シーンの終わり、フェードアウト前に短いホールドを残して、最終状態を読み取れるようにしてください。
ナレーション
オーディオはOpenRouter MCPサーバー(generate-speech)から取得されます。このスキルはそれを計画し記録するだけです。それらのツールが利用できない場合は、ナレーションにはOpenRouter MCPサーバーの接続が必要であることをユーザーに伝え、その間はナレーションなしでレンダリングしてください。reference/voiceover.md に完全な手順、モデル、音声の選択肢、費用が記載されています。