Site Reels: サイト → MP4 → デッキ
必要条件
| 必要なもの | 内容 | 確認方法 |
|---|---|---|
| すべて | GPUと可視デスクトップを備えたWindows、macOS、またはLinux(Chromeはヘッドフルで実行) | n/a |
| すべて | Node.js 18+ | node -v |
| すべて | PATH上のffmpeg + ffprobe(またはFFMPEG=<フルパス>) | ffmpeg -version |
| すべて | Chrome、Chromium、またはEdge(自動検出、以下を参照) | node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))" |
record.mjs | puppeteer-core + puppeteer-screen-recorder(npm、プロジェクト内) | npm ls puppeteer-core puppeteer-screen-recorder |
snap.mjs | puppeteer-core + timesnap(timewebを含む) | npm ls timesnap |
| ショーリール | Remotionプロジェクト | npx remotion --version |
| 監査(オプション) | ImageMagick | magick -version |
| Figmaアップロード | Figma MCP(use_figma、upload_assets)+ スキル figma-use、figma-use-slides | MCPツールリスト |
Chromeの検出(scripts/find-chrome.mjs、record.mjsおよびsnap.mjsで使用):CHROME環境変数が設定されている場合はそれを使用。そうでなければインストール済みのChrome/Edge(Windows:Program FilesとLocalAppData;macOS:/Applicationsと~/Applications;Linux:PATH上のgoogle-chrome、chromium、microsoft-edge)。それもなければ最新のms-playwright/chromium-<N>(またはPLAYWRIGHT_BROWSERS_PATH)。それでも見つからなければ停止し、CHROMEの設定方法またはnpx playwright install chromiumの実行方法を表示します。Windowsでは--use-angle=d3d11を追加。その他のOSではChromeはデフォルトのGPUバックエンドを維持します。
機能概要
ローカルで実行中のサイトのスクリプト化されたシーンを録画し、Figmaに適したMP4(1920×1080、30fps、x264、<100MB)を提供します。検証済みエンジン:puppeteer-core + puppeteer-screen-recorder(CDPスクリーンキャスト + ffmpeg)。画面録画は行いません。
実行方法
mkdir <proj>-reels && cd <proj>-reels && npm init -y && npm i puppeteer-core puppeteer-screen-recorder
mkdir -p scripts && cp <skill-dir>/scripts/{record,find-chrome}.mjs scripts/
cp <skill-dir>/scripts/scenes.example.mjs scenes.mjs # シーンを編集
# サイトリポジトリ内:npx next build && npx next start -p 3100 (プロダクションビルド:WebGLがよりスムーズ)
OUT="<出力フォルダ>/Videos" node scripts/record.mjs scenes.mjs [scene ...]
スクリプトをプロジェクトにコピーして実行します:ESMはスクリプトのフォルダからpuppeteer-coreを解決し、cwdからは解決しません。
環境変数:BASE(デフォルトhttp://localhost:3100)、OUT、CHROME、FFMPEG、W、H、DPR、MOBILE=1(タッチエミュレーション)、KEEP_UI=1(ナビゲーションを維持)、MIN_FPS(24)。電話4:5:W=360 H=450 DPR=2 MOBILE=1で720x900になります。
各シーン = { name, path, theme?, trim?, crf?, css?, prepare(page, ctx), act(page, ctx) }。
prepareは録画前に実行されます(セクションまでスクロール、3Dの読み込みを待機)。actは動画に含まれる操作です。ctxにはwait、scrollTo(page, y|selector, ms)、scrollThrough(page, selector|null, ms, from, to)(イージング付き)があります。読み込み時に実行されるイントロ:act内でpage.reload()を使用し、trimで読み込み部分を切り取ります。
効果的なブランドスクリプト:ロゴ/ヒーローイントロ(ライトとダーク)、ヒーローメディアの切り替え、スクロールでカメラが入る3Dセクション、イマーシブルート(ウォーク)、フッターワードマーク、および1ページ全体のパス。5~7シーン、各12~55秒。
納品前の監査(終了コードは信頼しないでください)
- 動画ごとのコンタクトシート:
ffmpeg -i x.mp4 -vf "fps=6/<dur>,scale=480:-1,tile=3x2" -frames:v 1 sheet.pngで確認。 - 19秒で1MB = 空白シーン。>100MB = Figmaに入りません(シーン
crfで修正。24はグレインとトレイルのあるシーン向け)。 - 上部ストリップ(
crop=1920:120:0:0)を数回確認し、ナビゲーションのリークを検出。 LOW-FPSで最初/最後に長い一時停止があるのは誤警報です(カウンターは静止フレームをカウントします)。コンタクトシートを確認してください。
既知の落とし穴
- 非表示ウィンドウがrAFをフリーズさせる → フリーズフレーム。起動時にはすでに
--disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusionを渡しています。headless: falseは意図的です:ヘッドレスモードはSwiftShaderにフォールバックし、WebGLがカクつきます。 - UI非表示CSSがリロード時およびReactハイドレーションが
<head>をクリアするときに消える → スクリプトは100msごとに<style id="reels-hide">をbodyに再アタッチします。 - 1つの無効なセレクターがルール全体を無効にする:JSテンプレートリテラル内の
\[は[になり、.z-[60]はheader, nav, .z-[60]を無効にします。リスクのあるセレクターごとに1つのルール。 [data-cursor]を非表示にしない:サイトは3Dキャンバスとカルーセルにこれを使用します。カスタムカーソル要素(固定+ mix-blend + pointer-events-none)をターゲットにします。- 遅延読み込みされる3D(IntersectionObserver + 動的インポート):セクションに到達した後5~7秒待機します。
preserveDrawingBufferがないとWebGLキャンバスの読み取りは空で返ります。固定待機またはスクリーンショットを使用します。 - スクリーンキャストは約32ユニークフレーム/秒を提供 → 30fpsで録画(60fpsでは不規則な繰り返しフレームが発生)。デフォルトの
trim0.3秒は最初のキャッシュフレームを削除します。 - 負荷の高いマシンでのスクリーンキャストのスタッター(Photoshop起動中、並列レンダリング):MP4は「30fps」と表示されますが、9~18のユニークフレーム/秒しかありません。
record.mjsは測定し(mpdecimate=hi=64:lo=24:frac=0.05)、MIN_FPS(24)を下回ると最大2回再録画します。WebGL/3Dプレートにはsnap.mjsを推奨します。 - Cookieバナー:録画前に
prepareで同意します(「Accept/Aceitar/OK」を汎用的にクリック)。 - スクロールのないツールページ(ジェネレーター、エフェクトエディター)は静止画を録画します:シーンはインタラクションする必要があります(
input[type=file]へのファイルアップロード、ボタンテキストによるプリセットクリック)。カスタムスライダーは合成入力に応答しません。
60fps固定プレート:snap.mjs(決定論的)
scripts/snap.mjsはtimeweb(timesnapの仮想クロック:rAF、performance.now、setTimeoutおよびvideo)を注入し、1/60秒ごとにフレームを進めてスクリーンショットを撮ります。マシンの負荷に依存しません。
シーン:{ name, path, theme, css, dur, wait(3D読み込みの実ミリ秒), pre(スクロール前の仮想ミリ秒、2000;**読み込み時に実行されるイントロの場合は0**), start(スキップされる仮想秒数), scroll: { sel, from, to, at, len } | { bottom: [from, to](画面高さ単位)}, frame(page, t)(例:マウス) }。スクロールは仮想時間の関数です。
npm i puppeteer-core timesnap && mkdir -p scripts
cp <skill-dir>/scripts/{snap,find-chrome,patch-timeweb}.mjs scripts/
npm pkg set scripts.postinstall="node scripts/patch-timeweb.mjs" && npm run postinstall
FPS=60 OUT=... node scripts/snap.mjs scenes-snap.mjs [scene]
timesnap()自体は使用しないでください:Next + R3F + Lenisでは空白ページを録画し、WebGLヒーローにフリーズしたストレッチを残しました(rAFが実行されない)。独自のループ(goTo + scrollTo + screenshot)はどこでも機能しました:60/60ユニークフレーム。- timewebパッチは必須です:
video.play()をPromiseなし・playingイベントなしのバージョンに置き換えます →video.play().catch()がスローし、ページが空白になります。 preserveDrawingBuffer: trueがgetContextに強制されます(preserve: falseでオフにできます)。- フレームはOSの一時ディレクトリに保存され、同期ドライブには保存されません(数千のjpgが同期するのを防ぐため)。
- GPU作業の並列実行は行わない(Remotionレンダリング、Photoshop):WebGLはコンテキストを失い、背景のみの秒間を録画します。スクリプトは
FLASH警告を表示します(輝度によるフレームフラッシュ)。長い空白ストレッチはコンタクトシートでのみ表示されます。確認してください。 - 実際のスムーズさ監査:
magick compare -metric MAEを隣接フレーム間でフル解像度で実行。0 = 繰り返しフレーム。 - 納品:
scripts/deliver.example.sh(プレートをコピーして名前を変更。V=が必要)は、リビール、ループ(loop.sh)およびクリーンな3Dを構築し、95MB未満になるまでcrfを3ずつ上げます。
Figma Slidesへのアップロード(非表示、チームに迷惑をかけずに)
MP4はMCP経由では入りません:use_figmaにはネットワークがなく、Plugin APIはバイトから動画を作成せず、upload_assetsは画像のみを受け付けます(≤10MB)。したがって:
- 各動画のカバー:
ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg。 use_figma(スキルfigma-use+figma-use-slides):最終スライドの前にcreateSlideRow(idx)、row.name = 'WIP · Motion (hidden)'、動画ごとに1つのcreateSlide(idx, i)でisSkippedSlide = true、黒背景、VIDEO · x.mp4という名前の1920×1080の長方形、およびspeakerNotes内の指示。upload_assetsを長方形のnodeIdsとともに使用 → カバーのマルチパートPOST。- ユーザーがMP4を長方形にドラッグします(カバーが動画に置き換わります)。これを明確に伝え、動画の挿入を約束しないでください。
getSlideGrid()はスキップされたスライドを省略します:SLIDE_GRID.children[i].childrenで構造を確認してください。
ブランドショーリール(Remotion):プレート + デザインシーン
ブランディングショーリール(ロゴ、カラー、タイポグラフィ、レイアウト、マニフェスト、モックアップ)の場合、サイトからクリーンなプレートのみを録画し(テキストなし:シーンごとのCSSでタイトル/ナビゲーションを非表示)、残りをRemotionで構成します(フレームごとのレンダリング、ジッターなし)。src/fx.tsxに持つ価値のある再利用可能なビルディングブロック:Mist、MistWords、Grain、KenBurns、Plate、nevoa()(ミスト)トランジション。ブランドデータはbrand.tsに。
- ビジュアルアイデンティティはデッキから取得し、サイトからは取得しない:スライドで公式の書体とパレットを確認してください(サイトはブランドブックから逸脱することがあります)。
- 効果的なシーン:ミスト内のオープニングフレーズ、録画されたリビール、ロゴ構築(
evolvePath+ ロゴラスター上で測定されたガイド)、フレーズごとのマニフェスト、可変wght軸を持つピラー、バンド内のパレット、タイポグラフィ、レイヤードグラフィック、mix-blend-differenceのタイトル付き3D、スクロールレイアウトカラム、1/2/3-upモックアップモンタージュ、名前に同期したプロダクト、ワードマークでクロージング。 - スチルでレビュー(
npx remotion still ... --frame=N+ モンタージュ)してからレンダリング。 --concurrency=3でレンダリング:8では、Photoshopを開いた状態で32GBマシンがメモリ不足になりました。- ループ:
scripts/loop.sh in out [start] [dur] [crossfade]。magick compare -metric RMSEで最初と最後のフレーム間の継ぎ目を確認(< 0.5% = 不可視)。 - 悪いプレートは破棄:回転するソリッド・オブ・レボリューションは動きを示さず;ポートレートキャンバス用にフレーミングされたカメラは16:9で小さく見えます。
- 熱狂的なリール(承認されたリズム):25~35秒、60fps、120 BPMグリッド(30フレーム)でのハードカットの
Series、3~6フレームごとの画像バースト、グリッド上の巨大ロゴクロップ、フォントウェイトのフラッシュ、再配置されるパレットブロック、円形マスク内のポートレート、グラフィックバースト、キネティックフレーズ、ワンビートモックアップ、クロージングロゴ約2.5秒保持。ラベルゼロ:テキストはグラフィックとしてのみ。スローな1分40秒のショーリールは却下されました。 - ソフト/スモーキーなリール:ヒーロースモークをロゴや写真なしで録画、純白に黒(
storage= localStorageのパラメータとblock: ['assets/logo.png']のシーン。空白のPNGは透過である必要があります。動画をブロックしないでください:壊れた動画はtimewebをフリーズさせます)。プレートをmix-blend-mode: multiplyオーバーレイとして使用:シーンの開始時に開き、反転版(ffmpeg -vf reverse)で閉じます。カットは黒に着地します。トランジションは約1.1秒(レート2)、またはシーンの半分が黒になります。 - Figma Slidesレイアウトの組み立て:スライドを複製(スキップ済み)、各カードを3倍に複製し、背景/ロゴ/テキストのみ表示、各ノードを
download_assets(スケール2)でダウンロードし、クローンを削除。Slidesは透過をエクスポートしません:ロゴ/テキストをスライド黒と白でエクスポートし、差分でアルファを抽出(ImageMagick)。use_figmaを通じてbase64を返さないでください(コンテキストを壊します)。
プロダクトショーリール(実際のアプリを撮影)
画面は実際に撮影されます(Playwright + CDPスクリーンキャストを2倍速で、ステップgoto/move/click/type/scroll/wait、ポインタートレイルは<id>.cursor.jsonとして保存)。カーソルはRemotionで上に再描画されます。
却下、繰り返さないでください:
- デザインされたリールを実際の画面に接着する(イラスト入りオープニング + 撮影中盤 + 描画CTA):2つの言語、キャラクターが消えて戻り、幕間の黒いカードがリズムを止める。ビデオごとに1つの言語。
- 16:9ウィンドウ内の小さく高速化されたプロダクト画面:フィードとランキングが読めない壁になります。
- 画面をトリミングするズームと、ビデオ上にスタンプされたテキスト(巨大動詞、01/07カウンター、グラデーション):押し付けがましい。
- 画面上での空の待機(「waiting…」):アクションに直接カット。
- 曖昧なコピー:明白で、観客の平易な話し言葉である必要があります。
機能する形式: アクションごとに、まずテキストのみ、中央揃えで短く(2ビート)、次に画面全体、トリミングなし、キャプチャアスペクトの中央揃えウィンドウ、クリーンな背景上。控えめなテキスト(ウェイト700、1語ごとのエントリーで軽いブラー)、カウンターなし、グラデーションなし。トラックグリッドでカット(14フレーム = 128.57 BPM)。最大40秒。
効果のあったテクニック:
- キャプチャの高速化:
<OffthreadVideo trimBefore={from} playbackRate={rate}>でクリックフレームを/ rateで再計算(サウンドとカーソルリング)。 - 別のコンポジションを途中から開始:
<Sequence from={X}><Sequence from={-start}><Comp/></Sequence></Sequence>。ストレッチの最初のフレームを確認:共有要素が「飛び込んで」くると、緩く表示されます。数フレームスキップし、前に落ちたインパクトサウンドを再配置。 - コンポジションごとにモジュールレベルのタイムラインで言語を設定:Rootの前にインポートされた独自のエントリファイルによって設定される
globalThisフラグ。 - カットをまたぐ連続トラック:ベッドに
trimBeforeを設定し、前のストレッチが停止したところから続くようにします。 - ラウドネス:レンダリング、測定、2パス
loudnorm(-14 LUFS / -1 dBTP)で正規化、ビデオをコピー。最後にのみ測定:Remotionの生ミックスは0 dBFSを超えます。 - 監査:すべてのレンダリングのタイムスタンプ付きコンタクトシート(
fps=2,drawtext=%{pts\:hms},tile=8x6)、および各ストレッチ接合部の密集シート(fps=6)。 - gitなしのプロジェクト、別のセッションで同時に編集:新しいファイルに新しいカット、共有ファイルを編集する前に
*.bak-*バックアップ、そしてユーザーに伝えます。
ポートフォリオカードとスタジオリール
record.mjsをKEEP_UI=1(カードはサイトをそのまま表示)と上記の電話プリセットで使用。- インベントリファイルから生成されたシーン(公開ライブURLを持つアイテムのみ)。ライブURLなし = スクリーンショットのKen Burns。
- カードパッケージ:終了から開始へのクロスフェードループ、KB上限への2パスビットレート、VP9 WebM、フレーム1のWebPポスター。
- ウェブ用のアルファ付きロゴ:
-
...
[翻訳のため省略]