bueno-demo によるデモ動画の作成
bueno-demo は、Webページ上のスクリプト化された操作を録画し、Screen Studio風のクリップにレンダリングします。2つのステップがあります: capture は実際のChromiumウィンドウを駆動して録画し、render がカメラ、カーソル、字幕を追加します。
セットアップ(マシンごとに一度)
npm i -D bueno-demo # または npx bueno-demo ですべて実行
npx playwright install chromium
brew install ffmpeg # 必須; WebPポスター用に imagemagick を追加推奨
ループ
- スクリプトを書く(JSONファイル、以下参照)。クリップあたり1つのアイデア、10〜15秒に抑える。
- キャプチャ:
npx bueno-demo capture demos/<name>.json out/<name> - オプションでカット: 読み込みが遅い部分を
npx bueno-demo cut out/<name> <from-s> <to-s> - レンダリング:
npx bueno-demo render out/<name> out/<name>/<name> --gif - 使用前に確認する。 コンタクトシートを作成し、すべてのフレームをチェック:
ffmpeg -i out/<name>/<name>.mp4 -vf "fps=1,scale=640:-1,tile=3x4" -frames:v 1 sheet.pngバグやプライベートデータがカメラに映っている場合は、スクリプトを修正するかカットしてから再レンダリングする。
スクリプト
{
"url": "http://localhost:3000/settings",
"viewport": [1440, 900],
"storage": "auth.json",
"keep": "document.querySelectorAll('[data-user-email]').forEach(e => e.textContent = '[email protected]')",
"steps": [
{ "scroll": "[data-testid=billing]" },
{ "click": "[data-testid=plan-toggle]", "after": 1200 },
{ "type": "[data-testid=team-name]", "text": "Northwind", "clear": true },
{ "hover": "[data-testid=save]", "zoom": true },
{ "click": "[data-testid=save]", "zoom": false },
{ "wait": 800 }
]
}
各ステップにはオプションで after(一時停止するミリ秒)を指定できます:
click: 要素まで滑るように移動してクリック。zoom: falseでカメラズームをスキップ(遷移を伴うクリックに使用)。type: クリックしてからtextを入力(delayはキーあたりのミリ秒、デフォルト55;clear: trueで値を置換)。fill: クリックしてからtextを一括設定。タイピングで操作できない入力(日付ピッカーなど)に使用。hover: そこに滑るように移動。zoom: trueでカメラもその位置に向ける。upload: ドロップゾーンに滑るように移動し、file(スクリプトからの相対パス)をinputに添付。press: キー操作(例:"Escape")。scroll: 要素が表示されるまでスムーズスクロール。wait: ミリ秒単位の一時停止。eval: カメラ上でページ内のJSを実行。
オプション:
- サインイン:
storageはPlaywrightのストレージステートファイルを指定(npx playwright codegen --save-storage=auth.json <url>で保存)。または"cookiesFrom": "agent-browser"で既存の agent-browser セッションからクッキーを借用。クッキーが出力されることはありません。 - プライバシー:
keepは録画前および録画中250msごとに実行されるJS。実際の名前やメールアドレスをダミーに置き換え、再レンダリング時に元に戻らないようにします。hideはCSSセレクタを追加して非表示にします(Next.jsやVercelの開発用オーバーレイはデフォルトで非表示;"hide": falseで表示)。 - カメラ外:
setupは録画前に一度だけ実行されるJS(バナーを閉じるなど)。 - 操作感:
travelMs(カーソルの移動時間、デフォルト700)、settle(読み込み後の待機時間、デフォルト1500)、tail(終了時のホールド時間、デフォルト1200)、cursorStart([x, y])、reducedMotion: true。 - 読み込み:
waitUntil(デフォルトnetworkidle)。動画ストリーミングやポーリングを行うページはアイドル状態にならずキャプチャがタイムアウトするため、"load"を設定しsettleで残りをカバーします。 - 別サーバー: キャプチャ時に
--base http://localhost:3001を指定すると、同じパスを別の場所で録画。
セレクタ: 可視テキストよりも data-testid や構造(#title、input[name=amount])を優先。コピー変更でスクリプトが壊れるのを防ぎます。
レンダリングフラグ
--zoom 1.6クリック時のズーム倍率(1.3は2つの要素が同時に関わる場合)。--speed 0.910%遅く再生;--speed 2.5で高速モンタージュ。--end 9.8その秒数でカット;--hold 1.2最終フレームを静止。--matte '#fdfaf1'ページをその色の背景に角丸ウィンドウとして配置(デフォルトは縁なし)。--title 'イベントの移動' --kicker 'イベント'オープニングタイトルを追加;--end-capでエンディングカードを追加。--brand brand.jsonタイトルとカードのルックを設定:ink、paper、accent、displayFont/monoFont({ "family", "file", "weight" })、logo、footer、kicker。パスはブランドファイルからの相対パス。--fitページ本来のアスペクト比でレンダリング(スマートフォンビューポートの場合は縦長)。--gif960px、15fpsのGIFも同時書き出し。
ルール
- 実際の顧客データは決して録画しないこと。 テストアカウントを使用し、個人情報には
keepフックを使う。共有前にコンタクトシートを確認する。 - 短く、単一目的にすること。 クリップ1つにつき1つの機能。待機時間は
cutでカットする。 - 動画はリポジトリ外にホスティングすること。 スクリプト(と、ドキュメントで必要な場合はポスター)をコミット。MP4とGIFは自社ストレージまたはCDNに配置する。