synthetic-screen-recording について
Remotion の TerminalScene でターミナルや CLI のデモを合成するか、実画面を録画するかを判断するスキルです。コマンドと出力が事前に決まり、ナレーションとの同期が必要な制作に向いています。
主な機能
- 01合成デモと実画面録画の判断ガイド
- 02TerminalScene のコマンド・出力タイムライン
- 03ナレーションに合わせたペース調整
- 04ProviderChip の切り替えバッジ表示
- 05合成 UI コンポーネント追加のルール
活用シーン
- 01台本に沿ったターミナルや CLI のチュートリアル制作
- 02コマンドとナレーションの同期
- 03デモ映像へのデスクトップ上の個人情報混入防止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ calesthio/ openmontage --skill synthetic-screen-recordingインストール後、Claude Code で/synthetic-screen-recordingを入力して読み込まれたか確認します。
詳細
合成画面録画(Remotion TerminalScene)
このスキルが解決する判断: ユーザーがターミナル、CLI ツール、またはコーディングのワークフローを画面録画風に見せたい場合、デスクトップを実際にキャプチャする(screen_recorder、Windows-MCP、Cap、Playwright による OS の画面録画)のか、それとも TerminalScene コンポーネントを使って Remotion で合成するのかを判断します。
目安: エージェントがコマンドと出力の正確なシーケンスを事前に作成できるなら、合成します。実際の動作が予測できない場合、実際のアプリ UI が必要な場合、またはユーザーが実際の録画を明示的に依頼した場合に限り、ライブキャプチャを使用します。
これが必要な理由
OpenMontage ショーケースの v3 では、インストール手順のウォークスルー用に、Windows-MCP と screen_recorder を使って Git-Bash ウィンドウを操作しようとしました。しかし、ウィンドウの配置、フォーカスの競合、タスクバーのプライバシーに関する懸念で行き詰まりました。そこで、Remotion による純粋なレンダリングに切り替えました。TerminalScene という名前の React コンポーネントが、偽のターミナルを描画し、コマンドを一文字ずつ入力します。出力は実際の画面録画と見分けがつかないほど視覚的に同じ(同じ信号機風のウィンドウ装飾、点滅するカーソル、スクロールする出力)でありながら、決定論的で、プライバシーに配慮し、1080p でピクセル単位まで正確で、フレーム単位でペースを制御できます。
このスキルで見つけやすくなる機能は、このコンポーネントとパターンです。
合成(TerminalScene)を使用する場合
合成するケース:
- コマンドと出力が予測可能なターミナル/CLI/コーディングセッションのデモ
- 洗練されたチュートリアルのような雰囲気(整ったタイポグラフィ、フローティングピル、カーソルの点滅)をユーザーが求めている
- インストール手順のウォークスルー、セットアップのデモ、API キーの設定、
makeターゲット、git cloneの手順 - ナレーションとの厳密な同期が必要で、各コマンドを特定のタイミングに合わせる必要がある
- 結果を再現可能にしたい(再レンダリングしても同じピクセルが得られる)
- 実際のデスクトップに、通常なら切り抜く必要があるプライベートなアプリやウィンドウが表示されている
実際の画面をキャプチャするケース:
- 偽の画面では再現できない実際のアプリ UIのデモ(Figma、Photoshop、ライブ状態のウェブアプリ、ブラウザーの操作手順)
- ユーザーが実際に使っている画面の録画を明示的に依頼した
- スクリプト化できないタイミングに動作が依存する(ストリーミングされる LLM 出力、実際のネットワーク遅延)
- ライブ環境でしか表示されない視覚的な特徴(カーソルのエフェクト、プラグインのポップアップ)がある
ブラウザーのデモの場合 → このスキルではなく、playwright-recording スキルを使用します。
実際のデスクトップの場合 → screen_recorder ツール、または cap_recorder による Cap を使用します。
コンポーネント — TerminalScene
配置場所:remotion-composer/src/components/TerminalScene.tsx
エクスポート元:remotion-composer/src/components/index.ts
ディスパッチでの接続先:remotion-composer/src/Explainer.tsx(if (cut.type === "terminal_scene"))
Props:
interface TerminalSceneProps {
title?: string; // shown in the window title bar
steps: TerminalStep[]; // the timeline
prompt?: string; // "$", ">", etc.
accentColor?: string; // pill + prompt glow
backgroundColor?: string;
}
ステップの種類:
{ kind: "cmd", text: string, typeSpeed?: number, holdSeconds?: number }
{ kind: "out", text: string, holdSeconds?: number }
{ kind: "pause", seconds: number }
{ kind: "pill", text: string, color?: string, durationSeconds?: number }
cmd— プロンプトを表示し、テキストを一文字ずつ入力します(typeSpeedは一文字あたりの秒数で、デフォルトは 0.035)。その後、holdSeconds(デフォルトは 0.3)の間、表示を保持します。out— プログラム出力の行を短いフェードインで即座に表示します。pause— 何も表示されない時間です。ターミナルは最後に表示された状態を保持します。ナレーションとの同期にはこれを使用してください。pill— ブロックしないフローティングバッジ(右上)。スプリングインして保持された後、スプリングアウトします。カーソルは進めず、次のステップは並行して実行されます。
作成パターン
新しいシーンを作成するには、build_composition.py(または同等の props ビルダー)にカットを追加します。
install_steps = [
{"kind": "pause", "seconds": 7.0}, # wait for intro narration
{"kind": "cmd", "text": "git clone https://github.com/calesthio/OpenMontage.git",
"typeSpeed": 0.045, "holdSeconds": 0.3},
{"kind": "out", "text": "Cloning into 'OpenMontage'..."},
{"kind": "out", "text": "remote: Enumerating objects: 2847, done."},
{"kind": "pill", "text": "repo cloned", "color": "#34D399", "durationSeconds": 2.6},
{"kind": "pause", "seconds": 3.8}, # bridge to next narration cue
# ...
]
cuts.append({
"id": "install-terminal",
"type": "terminal_scene",
"terminalTitle": "bash — OpenMontage setup",
"prompt": "$",
"accentColor": "#22D3EE",
"steps": install_steps,
"in_seconds": 50.0,
"out_seconds": 110.0,
})
ルール:ナレーションに合わせて進め、先走らない
最大の失敗パターン: ステップが連続して実行され、シーンの最初の40%でコンテンツをすべて使い切り、残りの60%はターミナルがフリーズしたままになります。これが v3 の初回作業を失敗させた原因です。機能メニューは t=80s に表示されましたが、ナレーションで告知されたのは t=92s でした。
代わりに、次のようにします:
- ナレーションの合図を把握する — 各シーンについて、各ナレーション区間が始まる動画上の正確な時刻を書き出します。
- 最初のナレーションの合図まで届くポーズから始める — コマンドを入力する前に、最初の合図まで間を持たせます。
- 各コマンドをナレーションに合わせる —
cmdは、ナレーションがその内容を話し始めた瞬間に入力を開始するようにし、先行させないでください。 - コマンド群の間にポーズを入れる — 次のナレーションの合図までつなぎます。
- 最後は余裕を持たせて止める — ナレーション終了後も最終状態を読めるよう、十分な長さのポーズを入れます。
レンダリング前にステップを確認します — Remotion のレンダリングにかかる時間は貴重です。ステップの所要時間を合計し、シーンの長さと一致することを確認します:
import math
def trace(steps, scene_start, fps=30):
t = 0.0
for s in steps:
k = s["kind"]
if k == "cmd":
tf = math.ceil(len(s["text"]) * s.get("typeSpeed", 0.035) * fps)
t += tf / fps + s.get("holdSeconds", 0.3)
elif k == "out":
t += max(2, math.ceil(0.08 * fps)) / fps + s.get("holdSeconds", 0.15)
elif k == "pause":
t += s["seconds"]
# "pill" is non-blocking — does NOT advance cursor
print(f" {t + scene_start:6.2f}s {k}: {s.get('text', '')[:40]}")
trace(install_steps, 50)
出力列を確認してください。各ナレーションの合図の動画上の時刻が、その内容を伝えるコマンド/出力のすぐ隣に表示されている必要があります。コマンドが合図より10秒早い、または遅い場合は、ポーズを調整してください。
このスクリプトの再利用可能なバージョンについては、lib/verify_scene_pacing.py を参照してください。
デザインルール(v3 の再調整から継承)
- 冒頭のポーズ — すべてのターミナルシーンは、文字入力が始まる前に、カーソルが点滅する空のターミナルを少なくとも2秒間表示して始めます。視聴者がウィンドウを認識するために必要です。
- ピルのタイミング — ピルは、名前が示すイベントが画面上で完了する正確なタイミングで表示します(例:最後の
Receiving objects行の直後にrepo cloned)。ピルは、実際の UI 通知の代わりとして使います。 - コマンド入力後のホールド — 視聴者が最初の出力がスクロールインする前に入力済みのコマンドを認識できるよう、すべての
cmdでholdSecondsを0.3以上にします。 - 出力のテンポ — 出力行の
holdSecondsは0.4~1.0の間隔を空けます。出力が速すぎるとバグのように感じられ、遅すぎると退屈に感じられます。 - 自動スクロールは機能します — ターミナルには直近の18行が表示されます。画面外のコンテンツは気にしないでください。
- カーソルが点滅するのは、入力中の最新コマンド行と、入力完了後約0.2秒の間だけです。
ProviderChip(関連コンポーネント)
.agents/skills/synthetic-screen-recording パターンでは、ProviderChip も管理します。これは、固定の間隔でプロバイダー名のリストを切り替えて表示するバッジオーバーレイです。v3 のショーケースで、「generated motion」セクション中に11の AI 動画生成プロバイダーすべてを切り替えて表示するために使用されています。
overlays.append({
"type": "provider_chip",
"providers": ["Veo 3.1", "Seedance 2.0", "Kling 2.5", ...],
"cycleSeconds": 2.5,
"position": "bottom-right",
"accentColor": "#22D3EE",
"label": "generated with",
"in_seconds": 195.0,
"out_seconds": 222.5,
})
ディスパッチへの接続箇所:remotion-composer/src/Explainer.tsx のオーバーレイレンダラー(overlay.type === "provider_chip")。
新しい合成 UI コンポーネントの追加
このパターンは一般化できます。別の UI 画面を再現する必要がある場合(Claude Code のチャットバブル、Jira のチケットビュー、GitHub PR の差分、Slack のメッセージ、VS Code のステータスバーなど)は、次の手順に従います:
TerminalScene.tsxをテンプレートとしてコピーします。- 関連するタイムラインのプリミティブ用に
stepsインターフェースを定義します。 - 累積した開始時刻と終了時刻に対する
frameの補間によって、各ステップをレンダリングします。 - 新しい
cut.typeを指定して、Explainer.tsxのSceneRendererディスパッチに接続します。 Explainer.tsxのCutインターフェースとcomponents/index.tsに型を追加します。- このスキルに、その内容を説明するセクションを追加します。
- 新しいカットタイプを
remotion-composer/SCENE_TYPES.mdに追加します。
関連スキル
.agents/skills/remotion— Remotion の一般的な作成方法(フック、スプリング、シーケンス).agents/skills/playwright-recording— ウェブアプリの実際のブラウザ操作フローをキャプチャtools/capture/screen_recorder— ffmpeg ベースのデスクトップキャプチャtools/capture/cap_recorder— Cap.so による高品質なデスクトップキャプチャskills/pipelines/screen-demo/asset-director.md— 画面デモプロジェクトで合成と実写のどちらを使うか選択
来歴
導入:OpenMontage showcase v3 のレンダリング(2026-04-16)。当初の目的:v3 のセットアップ手順セクションで、すべてのコマンドが Chirp 3 HD のナレーションの合図と一致する 60 秒のインストールデモが必要でしたが、Windows-MCP を使った実際のキャプチャは実用上不安定でした。参考実装については projects/openmontage-showcase/build_composition.py を参照してください。