/pitch-deck-video
実行中のローカルWebアプリからナレーション付きのプロダクトデモ動画を生成します。編集は導出ベースで、手動でタイミングを調整することはありません。そのため、フロントエンドが変更されたら再録画するだけで、カットは自動的に追従します — フレーム番号を修正する必要はありません。
現実に起こるケース、つまり「動画が必要なのに、UIがまだ動いている」という状況のために作られています。
使用方法
/pitch-deck-video # カレントディレクトリのアプリを録画
/pitch-deck-video <path-to-app> # 特定のアプリを録画
/pitch-deck-video --rebuild # 既存の動画プロジェクトを再録画・再編集
/pitch-deck-video --script # ナレーションのみ変更。最後のテイクを再利用
/pitch-deck-video --render # カード/キャプションのみ変更。再レンダリング
構成
4つの生成ファイルがすべてを動かします。人間が書き込むのは最初のファイルだけです。
| ファイル | 保持するもの | 書き込み元 |
|---|---|---|
shots.json | 何を言うか、声、音楽、テーマ、ブラウザが何をするか | あなた |
src/vo-durations.json | 各行が実際にかかる時間 | scripts/tts.mjs |
capture/marks.json | 各ショットがテイク内でいつ始まったか、どこを見るか | scripts/record.mjs |
src/frames.json | テイクから何フレーム出力されたか | scripts/frames.mjs |
src/timeline.ts はこれらから編集全体を導出します。シーンの長さは測定されたナレーション音声から決まります。映像はそれに合わせて引き伸ばされます。カメラ移動は録画中にライブで測定された要素のバウンディングボックスから来るため、移動したボタンはショットを壊すのではなく、ショットを移動させます。
ワークフロー
1 · スキャフォールド
このスキルから template/ をアプリの隣にある <app-name>-video/ にコピーし、npm install を実行します。テンプレートにはシードJSONが同梱されているため、最初の録画前でも型チェックとレンダリングが可能です。
1つのプロジェクトのために ~/.claude/skills/pitch-deck-video/template/ 以下のファイルを編集しないでください — これは共有テンプレートです。コピーした方を編集してください。
2 · 最初に保存済みログインを確認
アプリに認証がある場合、リポジトリが自身のe2eテスト用に既に保持しているPlaywrightストレージステート(.auth/*.json、storageState、playwright/.auth/)を探します。app.storageState をそれに指定すると、録画はログイン済みの状態で開始され、カメラにログインフローが写りません。そのようなファイルがない場合にのみログインショットを作成します — その場合、パスワードが決して shots.json に書き込まれないように {"type": {"sel": "…", "env": "DEMO_PASSWORD"}} を使用します。
3 · 文章を書く前にアプリを把握する
アプリの開発サーバーを起動し、すべてのルートを1920×1080でスクリーンショットし、画像を読み取ります。ソースコードからナレーションを書かないでください。審査員が見るものについて書いているのであり、それを教えてくれるのはスクリーンショットだけです。
// 一回限りのスクリプト。Playwrightはテンプレートに付属
for (const r of routes) {
await p.goto(base + r, { waitUntil: 'networkidle' });
await p.waitForTimeout(700);
await p.screenshot({ path: `/tmp/shots/${r}.png` });
console.log(r, await p.evaluate(() => document.documentElement.scrollHeight));
}
スクロール高さに注意: 1080を超えるものは scroll アクションが必要です。そうしないとカメラに写りません。
4 · shots.json を書く
template/shots.example.json から始めてください。すべてのフィールドとアクションについては references/shots-reference.md を参照してください。
カードと映像が一体となるよう、theme.colors をアプリ自身のデザイントークンから取得します。cards.arch.nodes には実際のアーキテクチャ — どのボックスがモデル呼び出しか、どのボックスが決定論的コードか — を記入します。このビートがデモとラッパーを区別するものであり、審査員はそれを求めます。
重要なナレーションルール:
- 12~16ビート、合計1分30秒~1分50秒。 2分の制限下で、10秒の余裕を残します。
- 1ビートに1つのアイデア。 アイデアが終わったらビートも終わります。
- 数字は言う。 「6人がスコアをつけ、Priyaが勝つ」は「誰かを選ぶ」よりも優れています。
- UIを説明しない。 「ここで承認をクリック」ではなく、それが何を意味するかを言います。
- プロダクト名が出てくる前に、ユーザーの言葉で問題を提示します。
- 画面に残しておきたい一言で締めくくります。
5 · ビルド
APP_DIR=/path/to/app npm run all
ナレーション → 録画 → フレーム → レンダリング。app.url に何もサービスされていない場合は開発サーバーを起動し、アプリ自身の dev スクリプトを読み取って、そのフレームワークがどのようにポートを受け取るかを判断します。特別なものが必要な場合は app.devCommand を設定します。通常のフルリビルド: 約2分。
shots.json は最初に検証されるため、重複したIDや不明なアクションは、2分後に混乱を招く動画が生成されるのではなく、2秒で失敗します。
6 · 実際に見てQA — これは必須です
npm run qa # ショットごとに1枚の静止画をコンタクトシートにまとめる
npm run qa 3 # ショットごとに3枚。トランジションを確認したい場合
その後、シートを画像として読み取ります。動画をスクラブするのは時間がかかり、見逃しが発生します。コンタクトシートなら数秒で済み、重要な失敗を正確に捉えます。
確認項目: capture/marks.json → warnings が空であること(セレクタの欠落は警告であってクラッシュではありません — 動画は生成され、何が壊れたかのリストも得られます)。次に: 各キャプションはそれをサポートするフレームに配置されていますか?空白や白フレームはありませんか?小さなテキストは判読可能ですか、それとも focus が必要ですか?
references/troubleshooting.md には、このパイプラインが実際に遭遇したすべての障害とその修正方法が記載されています。ゼロからデバッグする前に読んでください。
UI変更後の再カット
これがこのツールのポイントです。
APP_DIR=/path/to/app npm run all
ほとんどの変更は何も必要ありません — 要素の移動、ページのスタイル変更、新しいコピー。それでも判断が必要なもの:
- ページが削除またはリネームされた → そのショットを再指定または置き換えます。
- 機能が書き直された → そのビートのナレーションは、もはや存在しないものに対する主張になります。その行を書き直します。
- セレクタが本当に消えた → 警告リストを確認し、そこで修正します。
ナレーションは行ごとにキャッシュされるため、編集された行のみが再合成されます。
声
声は完成した動画の最大の品質差をもたらし、フォールバックは本当に貧弱です。 最初のビルドの前に、後からではなく、はっきりとそう伝えてください。
tts.provider: "minimax"— 良好。ユーザーにMiniMaxキーの保存場所を尋ね、tts.envFileをそのファイルに指定します。音声IDはMiniMaxのシステム音声リストから取得します。音声の言語タグはヒントであり制限ではありません(ウクライナ語タグの音声でも、languageBoost: "English"を指定すれば英語をうまく読めます)。tts.provider: "say"— macOSの組み込みシンセサイザーで、通常はレガシー音声のみがインストールされています。ロボット的で、製品名を誤って発音し、見る人にはすぐにわかります。タイミング確認には使用できますが、審査員や顧客が見るものには弱いです。- 人間の録音はどちらよりも優れています。
<id>.wavファイルをpublic/vo/にドロップし、npm run scriptを実行します。すると、持続時間が再測定され、動画全体が他の編集なしに自動的に再タイミングされます。
キーがない場合は、とにかく動画をビルドしてください — ただし、声が弱い部分であり、キーを入手するか10分間マイクを使えば修正できることを、ユーザーに事前に伝えてください。say のナレーションを完成品であるかのようにこっそり出荷しないでください。
APIが到達不能な場合、MiniMaxは行ごとに say にフォールバックするため、実行が完全に失敗することはありません。
APIキーをプロジェクト内にコピーしないでください。 scripts/tts.mjs は環境変数 MINIMAX_API_KEY / MINIMAX_GROUP_ID を読み取り、それ以外の場合は tts.envFile で指定されたファイルから解析します。ユーザーにキーの保存場所を尋ね、envFile をそのファイルに指定します。
人間の録音はどのシンセサイザーよりも優れています: <id>.wav ファイルを public/vo/ にドロップし、npm run script を実行します。すると、それらが再測定され、動画全体が再タイミングされます。
音楽
scripts/music.mjs は、music.url で指定されたトラックをyt-dlpで取得し、現在の動画の長さに合わせてBGMをカットします。公開前にトラックのライセンスを確認するようユーザーに伝えてください — ライブラリトラックは通常、説明にクレジットを記載すれば無料で、記載しなければ有料です。タイトルとURLは public/music/SOURCE.txt に保存されるため、クレジットのコピーが簡単です。
禁止事項
- タイミングを手動で調整しないでください。ビートが急いでいると感じるなら、ショットに対してナレーションが長すぎます — タイムラインではなく
shots.jsonを修正してください。 <Screen />を<Sequence>の内側に配置しないでください。<Screen />は設計上絶対時間を読み取ります。Sequence内のuseCurrentFrame()は Sequenceローカルのフレームを返します。これにより以前動画が壊れたことがあり、その症状(映像が微妙に音声と同期しない)は見つけにくいです。- セットアップ時間がカメラに映り込まないようにしてください。ナビゲーション、データ読み込み、バナーを閉じる操作はすべてショットのキャプチャウィンドウ内で発生し、ナレーションの下で引き伸ばされます。セットアップステップの後に
{"cut": true}を配置します。すると、映像はそこから始まります。 - スタブされたものを撮影しないでください。ボタンが「バックエンドに接続してください」を返す場合、そのボタンは除外し、どのショットを追加すべきかをユーザーに伝えてください(配線が完了したら)。
- 警告リストを確認せずに出荷しないでください。
- 実行ごとに変動する可能性のある数字をナレーションしないでください。また、このアカウントのバージョンが空であるのに、画面を「いっぱい」と説明しないでください。1つの虚偽の主張を見つけた審査員は、残りの主張も割り引いて見ます。