synthetic-ui-demo について
Synthetic UI Demos は、製品デモ動画を再現可能な形で作るスキルです。製品のソースコードを読める場合に向いており、画面録画や製品の実行をせず、静的な React 画面と Remotion でカーソル、カメラ、クリックをフレーム単位で制御します。
主な機能
- 01製品ソースから静的画面を再構築
- 02Remotion でフレーム単位のデモを構成
- 03カーソル、クリック、カメラ移動を制御
- 04シーンごとに音声とタイムラインを整理
- 05静止画のコンタクトシートでクリック位置を確認
活用シーン
- 01リリースごとに更新する機能紹介の作成
- 02実際のアプリで使えないデータや画面の提示
- 03デモのクリックとカメラ移動を正確に調整
- 04複数アプリにまたがるリリース動画の制作
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ digitalsamba/ claude-code-video-toolkit --skill synthetic-ui-demoインストール後、Claude Code で/synthetic-ui-demoを入力して読み込まれたか確認します。
詳細
合成 UI デモ
画面録画は行いません。Claude Code が製品のリポジトリを読み取り、シーンに登場する画面だけを静的な React として再構築し、Remotion 上でスクリプト制御のカーソルとカメラを使って一連の動きを実行します。クリックはすべてフレーム番号で指定するため、シーンは毎回同じように再レンダリングされ、シード済みのアカウントや実行中の環境は必要ありません。製品が変更された場合は、コードを編集して更新します。
参考ビルド(このスキルと併せて参照してください。どちらも製品のリポジトリなしでビルドできます):
examples/ds-free-schedule-demo:ナレーターのタイル付きで、電話を予約する26秒のフローを1つ収録しています。最もシンプルな完全版です。src/Demo.tsxは、カーソルのすべてのキーフレーム、クリック、カメラ移動を含む単一のタイムラインです。examples/ds-release-649:3つのアプリにまたがる8つのシーンで構成された、80秒のリリースノート動画です。src/Release.tsxがシーンを順番に並べ、src/scenes/に各リリース項目のファイルが1つずつあり、src/room/とsrc/ac/が画面キットです。
これと Playwright のどちらを使うか
| 合成(このスキル) | Playwright 録画(/record-demo) |
|---|---|
| アプリのソースを読める | 実行中のアプリしか使えない |
| リリースごとに作り直すリリースノートや機能紹介ツアー | 実際の動作や実データを一度だけ収録する場合 |
| 手元にないデータが必要なフロー(通話全体、他のユーザー、管理者画面) | 実際の画面に簡単にアクセスでき、そのままでも見栄えがよい |
| カメラ移動、正確なクリックのタイミング、音声変更後の再レンダリング | 再構築にコストがかかる、長く複雑な操作 |
構成要素(lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // address bar; host darker than path
camera={CAMERA} // [{ f, z, x, y }] in canvas px, eased, clamped to the window
path={PATH} // [{ f, x, y }] cursor keyframes in canvas px
clicks={CLICKS} // frames that squash the cursor and throw a ripple
aiNote // "People shown are AI-generated" (or pass your own text)
canvasWidth={1440} canvasHeight={810} // the defaults
>
<SettingsScreen field={typed(frame, 'Q4 Product Review', 128)} />
</Stage>
Stageは固定サイズのアプリ用キャンバスの周囲にブラウザーの枠を描画し、カメラを適用して、カーソルを最前面(zIndex 100)に配置します。ズーム1では、ウィンドウはフレーム幅に対してfit(0.8625)です。キーフレームで値を繰り返すと、その値を維持できます。CursorとcursorAtは、ブラウザー以外のシーンでも単独で使用できます。near(cursor, target)はホバー状態を制御します。- 製品固有の画面キット(テーマ、アイコン、レイアウト)はプロジェクト内に置きます。ライブラリには含めません。
手順
1. サブエージェントを使って、アプリのリポジトリからビジュアル仕様を抽出する
アプリまたは領域ごとにサブエージェントを1つ割り当て、並行して作業させます。ファイル全体ではなく、簡潔な仕様を返すように指示してください。
- テーマ: 配色、フォントスタック、角丸、間隔、影(テーマファイルまたは CSS 変数を参照)。
- 文字列: シーンに表示されるすべてのラベル、メニュー項目、ダイアログ、トーストについて、英語ロケールの正確な文字列を取得します
(
public/locales/en/*.json、src/i18n/locales/en/*)。UI テキストを言い換えないでください。 - アイコン: アプリがアイコンフォントを使用している場合は、icomoon または fontello の
selection.jsonに各グリフの SVG パスが含まれています。必要なものを小さな JSON に抽出し、<svg><path d=...>として描画します。それ以外の場合は、SVG コンポーネントをコピーします。 - レイアウトと動作: 表示する画面のコンポーネントツリー、ツールバーの順序、デフォルト値、各コントロールの動作。
リモートのデフォルトブランチを読み取ります(git show origin/main:path、または git fetch を実行してから origin/<default> を読み取ります)。
ローカルのチェックアウトは古くなっていることが多く、前四半期の UI が含まれている場合があります。
リリースノートの場合は、まず各項目を、それが最初に出荷されたコミットとコンポーネントまでたどります。そうすることで、表示する正確な文字列とデフォルト値がわかります。
2. シーンに表示する部分だけを、固定キャンバス上に再構築する
- 固定キャンバス(1440x810 が適切なデフォルト)上に、すべての要素を絶対位置で配置します。重要な領域は矩形を定数としてエクスポートします(
SAVE_BTN = { x, y, w, h })。これにより、クリック対象の位置を推測ではなく計算で求められます。 - カメラに映るものだけを構築します。映らないパネルは単色にしてかまいません。
- 静的な React を維持します。state も effects も使いません。視覚的な変化はすべて
frameによって決まります。 - キットは専用のフォルダー(
src/ui/、src/room/)に置き、定数を記述したtheme.tsを含めます。
3. まず音声を作成し、その後タイムラインをフレームで設定する
- シーンごとに1行ずつ書き、最初にシーンごとの音声を生成します(
voiceover.py --scene-dir ... --json。--max-wpm 165を追加します)。計測された長さを確認します。 - 各シーンの長さは、その行の音声に間を加えた長さにします。また、各行の開始位置と長さを記したヘッダーコメントを残します。このコメントが正式な基準です(CLAUDE.md の「音声に合わせたタイムライン」を参照)。
- シーンファイルの先頭にあるフレーム定数ですべてを設定します。カーソルのキーフレーム、
CLICKS、カメラのキーフレーム、UI の状態切り替え(frame >= C.saveでダイアログを表示)などです。クリックのフレームをエクスポートして、親シーンからクリック効果音を配置できるようにします。 - 各クリックは、その名前を表す語に合うタイミングにします。
4. 項目が多い行は、間で分割する
複数の項目を並べた行(「スケジュール、連絡先、録音、文字起こし」など)は、1つのショットには速すぎます。音声を間の位置でフレーズファイル(07a、07b、...)に分割し、各フレーズに個別のショットを割り当て、それぞれのフレーズを別の拍に配置します。ds-release-649 の moreVo フィールドはこの処理を行います。
5. 本番レンダリングの前に、コンタクトシートでクリック対象を計測する
推測した座標は、カメラをズームすると通常20~50pxずれます。各クリックフレームと、その少し前の数フレームの静止画をレンダリングして並べ、カーソルの先端が対象に合っていることを確認します。
i=1; for f in 46 78 122 212; do npx remotion still MyComp out/stills/s$i.png --frame=$f; i=$((i+1)); done
ffmpeg -start_number 1 -i out/stills/s%d.png -vf "scale=640:-1,tile=2x2" -frames:v 1 out/contact.png
定数を修正し、静止画を再レンダリングしてから、動画をレンダリングします。
6. AI 生成の人物には画面上でラベルを付ける
実在しない人物(タイル内の FLUX.2 のポートレート、アバター、ナレーター)には、必ず画面上にラベルを付けます。Stage の aiNote を使うか、ナレーターのタイルに「AI」バッジを付けます。名前、連絡先、通話データもすべて創作なので、明らかに架空だと分かるようにします。
7. レンダリング後に音量を正規化する
レンダリング前に tools/qc.py --project ... を実行し、その後 qc.py --render out/video.mp4 --fix を実行して -14 LUFS にします。
注意点
- モノクロ SVG ロゴは黒くレンダリングされます。 通常、
logo_mono.svgの塗りは黒です。暗い背景ではfilter: brightness(0) invert(1)を使います。 - ローカルブランチが古い場合があります。 ローカルのチェックアウトがどのブランチにあるかにかかわらず、
origin/<default>を読みます。 rsync --exclude srcはnode_modules/*/srcも除外します。 プロジェクトやキットをコピーするときは、除外パターンを先頭のスラッシュで転送元ルートに固定します:--exclude /src。- カメラの移動範囲には制限があります。 キャンバスの端に近いカメラ中心は、ウィンドウの端がフレーム外に出ないよう内側に引き戻されます。ズーム位置が予想外になる場合は、これが原因です。さらに内側の点を狙います。
- メニューとダイアログはキャンバス内に配置する必要があります。 キャンバスによって子要素がクリップされます。実際のアプリと同様に、内側に開くようメニューを配置します。
- ホバー状態はカーソルの軌跡から決まります。 フレーム定数を別に設定するのではなく、
near(cursorAt(frame, PATH), target)を使います。これにより、キーフレームを移動するとホバーも連動します。 - 音声のセリフを変更すると、その後のすべての位置がずれます。 長さを再計測し、ヘッダーコメントとシーンの長さを更新してから、クリック時の静止画をもう一度確認します。リップシンクするナレーターが編集済みの音声トラックに基づいている場合は、ナレーターも再生成します。