ナレーション付きデモ
1つのエージェント、1つの午後、VPがスマートフォンで見られる1つの .mov。このレシピは、5つのクリップ(各69~188秒)で実証済みです。
- ユーザーにインタビュー — 5つの質問のみ(下記参照)。すでに持っているコンテキストからストーリーをドラフトします。ユーザーは編集を行い、一から書き起こす必要はありません。
- シーン仕様を記述(
clips/<name>.yaml):4~8シーン、各シーン約25~45語のナレーション(約10~18秒の音声)。すべてのシーンにペイオフ(見せ場)があります — その瞬間に何かが表示される — 動画はペイオフが早く表示され、最後にならないようにカットされています。 - レンダリング:
scripts/render_clip.py clips/<name>.yaml。シーンごとに:TTS → キャプチャ → 動画を音声に合わせる → テロップ → 多重化。その後連結。 - スチル画像を確認:レンダラーが書き出すスチル画像(
out/_<clip>_<scene>.png)をreadツールで確認してから完了とします。--only s2で単一シーンのみ再レンダリングします。 - 投稿(オプション):
scripts/slack_post.py <channel_id> out/<clip>.mov "<要約>"。
すべて再実行可能でキャッシュされます(TTSはテキストハッシュ、動画はシーンごと)。そのため、1文を修正するのに数秒しかかかりません。
1. インタビュー(以下の質問をしてからドラフト)
5つの質問すべてを1つのメッセージで尋ねます。部分的な回答を受け入れ、残りはコンテキストから補完し、仮定した部分にはラベルを付けます。
- 誰に向けた動画で、どれくらいの時間がありますか?(同僚のエンジニア:3分 · 役員:90秒 · Slackスクロール:60秒)→ シーン数とペースを決定します。
- 視聴者に見てもらいたいことは何ですか? → シーン1の最初の文と最後のシーンの最後の文になります。
- 画面に何を表示する必要がありますか?(UI、ターミナル、ドキュメント、チャート、ファイル)→ キャプチャの種類を決定します。URL/コマンド/パスを尋ねます。
- 「ライブ」な瞬間は何ですか? 視覚的に何かが起こる瞬間(クエリの結果が返る、ビルドが完了する、数値が表示されるなど)。1クリップあたり最低1つ。
- 声色とトーンは? デフォルト:
onyx、「落ち着いていて、クリアで、自信に満ちた技術デモナレーション、中程度のペース、数字をわずかに強調」。より軽いトーンを希望する場合はalloy/novaを提案します。
その後、ストーリーボードをテーブルとして提案します(シーン · 画面に表示するもの · 1行ナレーションの要点 · ペイオフ)。レンダリング前に承認を得ます。5シーンのクリップのレンダリングには約3~6分かかります。承認前にレンダリングしないでください。
2. シーン仕様
clip: 1-setup # 出力名 → out/1-setup.mov
size: 1440x900 # オプション:すべてのシーンの共通フレーム(フィット+パディング)— 画面とブラウザシーンを混在させる場合に設定
voice: onyx
instructions: "落ち着いていて、クリアで、自信に満ちた技術デモナレーション。中程度のペース、句点で自然な間。数字をわずかに強調。"
scenes:
- id: s1 # 短く安定したID → キャッシュキー
caption: "テーゼ · 1つのグラフ、4つのエビデンス" # ロワーサード;60文字以内に抑える
narration: "…25~45語…" # TTSがうまく発音しない場合は数字を単語で記述("eighteen thousand")
# シーンごとにキャプチャソースは1つだけ:
url: "http://127.0.0.1:4300/#mode=purchase" # (a) Playwright経由のブラウザ — さらにオプションのアクション:
actions: # ロード後に順に実行;`ready` はペイオフを示す
- fill: ["#prompt", "Acne Studios"]
- press: ["#prompt", "Enter"]
- wait: ".layer-chip" # セレクタ(45秒タイムアウト)→ ready を設定
- click: "button:has-text('fit')"
- select: ["#graph-layout", "brand"]
- scroll_to: "#join" # scrollIntoView(長いドキュメント用)→ ready を設定
- hover: ".card"
# video: "cache/cr_s3" # (b) 事前録画ディレクトリ(page.mov + marks.json)またはファイル — record_screen.sh を参照
# still: "docs/figure.png" # (c) 静止画像(チャート、スライド)— ケンブランスなし、ナレーションの間表示
hold: 2 # 最後のアクション後の追加秒数
speed: 1 # ソースを事前に速度調整(8 = ゆっくりしたスクロールを8倍速で表示)
scale: 2560 # シーンごとのダウンスケール幅(画面録画);font: 28 = このシーンのテロップのピクセルサイズ
良質なクリップを作るためのルール:
- ペイオフを先に。 レンダラーは各シーンを
readyの約2.5秒前から開始し、ナレーションが長い場合は最後のフレームをパディングし、短い場合はトリミングします。ペイオフが45秒の録画の40秒目にある場合、そのシーンは12秒のシーンとなり、ペイオフから始まります。 - ナレーションと画面が一致する。 ナレーション内の大文字の名前や数字はすべて、画面上に表示されているか、表示されようとしている必要があります。そうでない文は削除します。
- 1シーン1アイデア。 文が「また」で始まる場合、それは新しいシーンか削除対象です。
- テロップがアウトライン。 テロップだけを読めばストーリー全体がわかるようにします。
- ナレーションのために数字をでっち上げない。 結果ファイルや、作成したばかりのライブレスポンスから取得し、四捨五入する場合は「約」と言います。
3. キャプチャソース
- ブラウザ(
url+actions) — Playwright Chromium、1440×900、ヘッドレス、webmで録画。@playwright/testと Chromium がインストールされたNodeプロジェクトが必要;PW_REPO=/path/to/that/projectを設定(デフォルト:スキル自身のpw/—scripts/setup.shを一度実行して作成)。 - 画面 / ターミナル(
video) —scripts/record_screen.sh <scene> <seconds> [--rect x,y,w,h] -- <command…>:screencapture -Vを開始し、コマンドを実行し、コマンドがMARKを出力してペイオフを知らせた時点でstarted(ms)を含むmarks.jsonを書き込みます。注意点:screencapture -Vはピクセルが変化したときのみ進行します。静的ターミナルペインでは録画が停止します。何か動くもの(1Hzの時計表示など)を維持するか、show_file.shを使用して時計付きでファイルをページングします。広いキャプチャはDEMO_SCALE=2560でダウンスケールします。プライバシー: 矩形を指定して録画(--rect、ポイント単位;screencapture -R)し、目的のペインのみをキャプチャします — ウィンドウタイトル、タブバー、サイドバー、ドック、通知、バックグラウンドウィンドウは含めません。投稿前に、すべてのレビュースチル画像を確認し、「画面上に雇用主、ホスト、内部URL、他の人物を特定できるものはないか?」と問いかけます。ページングするテキスト(show_file.sh)は匿名化します — デモ用であり、実際のファイルではありません。 - 静止画像(
still) — ナレーションの間表示するPNG/JPG。チャート、テーブル、スライドに使用。 - 事前録画(
video: file.mov) — シーン内でready: <seconds>とともに使用。
4. レンダリング、確認、投稿
export OPENAI_API_KEY=… # TTS(gpt-4o-mini-tts);または互換プロキシ用に TTS_URL=https://<proxy>/v1/audio/speech TTS_API_KEY=…
scripts/render_clip.py clips/1-setup.yaml # フルクリップ → out/1-setup.mov(+ out/_1-setup_<scene>.png スチル画像)
scripts/render_clip.py clips/1-setup.yaml --only s2 # 1つのシーンのみ再レンダリングし、再連結
DEMO_FONT=26 scripts/render_clip.py … # テロップサイズ(1440pxの場合は22、2560pxソースの場合は26~30)
確認:read ツールで out/_*.png のスチル画像を2~3枚確認します。ブラウザシーンで wait failed: … Timeout と表示された場合、アプリが遅いかセレクタが変更されています。シーンはt=0からレンダリングされます(ペイオフカットなし)— セレクタを修正するか、アプリがアイドル状態のときにそのシーンのみ --only <id> で再実行します(各シーンの開始フレーム)。テロップが読めるか、ペイオフが画面に表示されているか、機密情報(トークン、購入者レベルの行、他人のメッセージなど)が表示されていないかを確認します。可能であれば .mov の最初と最後の5秒を視聴します。
Slackに3行以内の要約とともに投稿します:何を示しているか · 長さ · 1つの数字。scripts/slack_post.py C0123… out/1-setup.mov "…" は ~/.config/slack-mcp/credentials.json の slack-mcp ブラウザセッション認証情報を使用します。存在しない場合は、ユーザーにファイルと要約を渡します。
5. 規約(プロジェクトを整理整頓)
clips/ 仕様書 · cache/ TTS + シーンごとの動画(gitignore対象) · out/ 最終ファイル+スチル画像 · blurbs/ Slackテキスト。クリップは再生順に名前を付けます(0-…、1-…、2a-…)。インタビューのテーブルを含む storyboard.md を維持します。これは動画の後に人々が求める成果物です。
依存関係:ffmpeg/ffprobe、node ≥ 18、Playwright Chromium(ブラウザシーン用)、macOS screencapture(画面シーン用)、TTSキー。scripts/setup.sh で確認および可能な限りインストールします。