Communitygithub.com

brewed-ops/spatial-film

Spatial Filmは、talking-explainerの背後にあるフィルムエンジンです。実際のプロダクトUI、プラットフォームに忠実なサードパーティ製キャンバス(GHL、n8n、Zapier)、ネイティブテンポのミュージックベッドとSFXを使用し、CSS-3Dカメラによるプッシュスルー、ハードカットなし、最後のフレームが最初にループする45~90秒のナレーションなし空間3Dプロダクトモーショングラフィックを制作します。HyperFramesを介して1080p60でレンダリングされ、5つの並列シーンワーカーが1つのコントラクト(frame.md + シームテーブル + シーン別ハーネス)に対して動作します。ユーザーが「spatial film」や「3Dモーショングラフィック」と発言したり、MOTION-PROMPT.mdを渡したり、6シーン以上/3Dカメラのプロダクトビデオ(ナレーションなし)を依頼した場合に使用します。ナレーション付きの説明動画(ユーザーの声やマスコットがプレゼン)はexplainer-kitであり、このスキルの基盤上で動作します。

spatial-film とは?

Spatial Filmは、製品UIとプラットフォームに忠実なサードパーティキャンバス(GHL、n8n、Zapier)を使用して、プロフェッショナルなナレーションなしの空間3D製品モーショングラフィックを作成するための高度なAIエージェントスキルです。60秒のデモフィルムで実証済みであり、7シーン、5人のワーカー、フレーム精度のシーム、1080p60の出力を4分19秒で実現しています。このスキルは、複数のフェーズ(スキャフォールディング、ブリーフィング、ストーリーボード、フレームデザイン、サーフェスキャプチャ、ワーカーハーネス、音楽生成と分析、アセンブリ、シームチェック、レンダリング)を定義しており、各フェーズは明確なゲートで区切られています。Claude CodeやCoderなどのプラットフォーム向けに設計されており、スクリプトは実績のあるコードとして提供され、コンテキストを節約するためにフェーズごとにリファレンスを読み込むことを推奨しています。ファイルの内容全体を貼り付けるのではなく、ファイルパスをワーカーに渡し、コンタクトシートでシーンを判断し、正確なパッチで修正するという効率的なワークフローを採用しています。また、ナレーション付きのフィルム(ボイスオーバーと画面上のプレゼンター)もサポートしており、声優、台詞タイミング、プレゼンターオーバーレイ、オーディオ調整のための追加スクリプトが含まれています。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/brewed-ops/talking-explainer/tree/main/skills/spatial-film

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Spatial Film

60秒のプロダクトフィルム(7シーン、5ワーカー、1ウェーブ、すべてのシームがフレーム精度、チェッククリーン、1080p60、4分19秒)でエンドツーエンドに実証済みです。テンプレートの film.json には、そのフィルムの実際のシーンバーとキューシートが実例として保持されています。

<skill> = このスキルのフォルダ(このSKILL.mdがあるディレクトリ)。すべてのスクリプトはフィルムプロジェクトルートから実行します。 python 以下は、explainer-kitのセットアップチェックによって ~/.talking-explainer/config.json に保存されたPythonコマンド(Windowsでは py -3.12、macOSでは python3)です。

必要なもの

  • Node 20+、PATHに ffmpeg + ffprobe、Python 3.10+(numpy と Pillow が必要)。
  • HyperFramesスキル:npx hyperframes skills update general-video hyperframes-animation media-use(パケットビルダー、ブループリント+アニメーションマップ、音楽ヘルパー)。CLI自体はプロジェクトごとに固定され、npx 経由で実行されます。
  • サーフェスのプリレンダリング用Playwright:フィルムフォルダ内で npm i -D playwright(または PLAYWRIGHT_PATH を設定)。
  • オプション、ソース音楽用:HeyGenアカウント - 自分のターミナルで npx hyperframes auth login を一度実行します。 なければ、自分でトラックを用意して、フェーズ4を music-analyze.py から開始します。
  • 最終レンダリング用のGPU搭載マシン(ソフトウェアパスでもレンダリングは可能ですが、約15倍遅いです)。

コンテキストを無駄にせずにこのスキルを使う方法

  • フェーズが開始したときに、そのフェーズのリファレンスを1つだけロードします。決して事前にすべてをロードしないでください。
  • スクリプトは実績のあるコードです。実行してください。そのロジックをインラインで再生成しないでください。
  • メインセッションは、シーンファイル全体(20-60KB)を読み込むことはありません。コンタクトシートでシーンを判断し、grep -n と正確なパッチで修正します。
  • ワーカーには、ペーストしたコンテンツではなく、ファイルパスを渡します。storyboard.html(60KB以上)は、セルIDでgreppingし、全体を読むことはありません。
  • 最初にコンタクトシートを読みます。詳細を確認する場合のみ、単一のスナップショットPNGを開きます。

フェーズ(各フェーズはゲートで終了します - ゲートを通過するまで先に進まないでください)

#フェーズロードゲート
0スキャフォールド:node <skill>/scripts/new-film.mjs <dir> --name <slug>-フォルダにfilm.json、frame.md、kit、テンプレートドキュメントが存在する
1インテーク、BRIEF、ストーリーボード、スケッチシートreferences/storyboard.mdユーザーの明示的な「go」が ## Locked の下に記録されている
2frame.md、kit、プリレンダリングされたサーフェスreferences/surfaces.md(+ GHL/n8n/Zapierが表示される場合のみ platform-canvases.md)frame.mdに {{ がないこと、すべてのキャプチャPNGを確認したこと
3ハーネススモークテスト、パケット、パラレルワーカーreferences/workers.mdすべてのシーン:lintエラー0、コンタクトシートがそのブロックと一致する
4音楽+SFX(ワーカー構築中に実行)references/audio.mdベッドがネイティブBPM、ダウンビートでバー0。キューがfilm.jsonにある
5アセンブル、チェック、シーム、レンダリング、出荷references/verify-ship.mdチェック合格、シームチェックすべてPASS、ハードウェアgpuレンダリング確認済み

インテントとスケッチレビューのステップについては、/hyperframes -> /general-video を経由します(このスキルはプロジェクトの形状、コントラクト、スクリプト、ゲートを提供します。general-videoはパケットビルダーとワーカーの役割を提供します)。

ファイル

パス内容
template/プロジェクトスケルトン:film.json(シーンバー、BPM、bgm、SFX、キュー - タイミングの唯一のソース)、frame.md(記入項目付きデザイン真実コントラクト)、BRIEF.md、STORYBOARD.md、assets/kit.css(基本フィルムキット)、assets/fonts/(Poppins 500/700 + OFL)、references/worker-brief.md、references/scene-starter.html(lintクリーンなシーンスケルトン)、references/snippets.html、package.json(pinned hyperframes 0.8.117)。
surfaces/GHL、n8n、Zapierキャンバスの実用的なHTMLリビルド+長いマーケティングページ(架空のNorthwind Dentalデータ)、platform.css、sprite.svg、jobs.json。
scripts/new-film.mjsテンプレートからプロジェクトをスキャフォールドします。
scripts/capture-surfaces.cjssrc/captures/*.html を assets/captures/*.png に2倍でレンダリングします(jobs.json)。
scripts/measure-surface.cjsサーフェス上の要素の1倍のボックス。正確なアノテーション/クロップターゲット用。
scripts/make-harness.py、scripts/test-scene.mjsシーン別の分離テストプロジェクト。1つのシーンをlint + スナップショットします。
scripts/music-search.mjs、scripts/sfx-search.mjsmedia-useのRESTヘルパーを介したHeyGenカタログ(Windows対応)。
scripts/music-analyze.py、scripts/music-bars.pynumpyのみのテンポ+バーごとのエネルギー(librosa不要)。
scripts/bgm-cut.pyバーラインでベッドをカットし、トラック自身のアウトロをスプライスし、フェードします。--b は長いフィルムの下でベッドをループするために繰り返します。
scripts/assemble.pyfilm.json -> index.html(バーグリッド上のシーンスロット、bgm、持続時間とレーン付きのSFX)。
scripts/seam-check.pyすべてのシーム(カットの前/丁度/後)とループ(フレーム0対最後)をピクセル単位で証明します。
scripts/render-final.mjsCLIレンダリング。--browser-gpu を使用し、ログをファイルに出力し、gpuモードとffprobeを検証し、その後 flash-scan.py を実行します。
scripts/flash-scan.pyレンダリングのすべてのフレーム:両方の隣接フレームと異なるフレーム(1/60秒間表示される要素)= バグ。
scripts/encode-web.mjsWebエンコード(H.264 CRF 21、faststart、AACまたは --silent)+ ポスターフレーム。
scripts/blink-fit.py、scripts/build-presenter.pyナレーションフィルム:シートポーズ用のまばたきオーバーレイ(--band = 目帯)。そのトラックからのプレゼンターオーバーレイ。マスコットに mouth-fit.json がある場合はリップシンク付き。
scripts/el-tts.py、scripts/el-words.py、scripts/el-music.pyElevenLabsを介したナレーションフィルム(キーはenv EL にあり、ディスクには保存しない):SCRIPT.mdからのボイス候補、Scribe単語タイミング、生成されたベッド。
scripts/seam-diff.pyプレゼンターボックスをマスクしたシーム差分(ナレーションフィルム)。
scripts/fromto-scan.pyプリレンダリング:レンダリングワーカー(シーン内シーク)が決して適用しない fromTo FROM-onlyプロパティをフラグします。すべてのレンダリング前に0ヒット。
scripts/patch_exact.pyスクリプト化されたシーン修正のための、正確でCRLFセーフな検索/置換(それぞれ1つの一致をアサート)。
scripts/selftest.mjsこのスキルがこのマシンで動作することをエンドツーエンドで証明します。PASSと出力する必要があります。

ナレーションフィルム(ボイスオーバー+画面上のプレゼンター)

ナレーション付きの「API vs Webhook」説明動画(83秒、縦長と横長、複製音声、マスコットプレゼンター、5人のワーカーによる10シーン)で実証済みです。上記のミュージックフィルムのルールは、以下のもので置き換えられる場合を除き、引き続き有効です。

  • まずボイス候補から。 EL=<key> python <skill>/scripts/el-tts.py . eric=<voice_id> michelle=<voice_id> は SCRIPT.md のインデントされた行を読み、音声ごとに1つのMP3を src/audio/voice-candidates/ に書き込みます(キーはenvからのみ、ディスクには保存しない)。ユーザーに耳で選んでもらい、選んだものだけを書き起こします。
  • ナレーションが時計です。 単語時間:EL=<key> python <skill>/scripts/el-words.py <voice.mp3> src/timing/voice-words.json(ElevenLabs Scribe - ローカルモデルは不要、キーが必要)、または より高速なwhisperを word_timestamps(言語設定、VADオン。explainer-kitの transcribe.py、無料でローカル)で使用します。単語JSONは src/timing/ に保持します。film.json で "bpm": 60, "beats_per_bar": 1 と設定すると、「バー」は1秒になり、シーンの長さは文のポーズでカットされた単なる秒数になります。ストーリーボードブロックはシーンローカルの単語時間を提供します。到着は約0.1秒早く始まり、単語に着地します。タイトルは次のカットの前に少なくとも約1秒画面に表示します(表示される行の最後の単語に対してカットを確認します)。
  • film.json の追加項目(assemble.py + make-harness.pyが読み取る):"audio": [{id, src, dur, volume}] は音声用、"overlays": [{id, track, z}] はすべてのシーンの上にある全フィルムレイヤー(プレゼンター)用で、シームでポップすることがなく、シーンはプレゼンターなしのままになります。
  • ポーズPNGからのプレゼンター: ポーズごとに1つの透明PNG(explainer-kitの prep-mascot.py がトリミング、スケーリング、マニフェストの書き込みを行います)。その後 scripts/blink-fit.py <mascot_dir>(オプション、torchが必要。NCCによって各ポーズにBLINK目帯をフィットさせます。0.70未満のポーズはまばたきなし)と scripts/build-presenter.py(src/presenter-track.json からオーバーレイを書き込みます。キューワード上のポーズ、ヘッドアンカーなのでスワップが飛ばない、声のエンベロープボブ、呼吸、まばたき、fx)。その出力は手動で編集しないでください。
  • オーディオ: 音声を-16 LUFS / -1.5 dBTPにラウドノーマライズ(+4.6 dBTPでクリップされたプレーンゲイン)。その下のベッドをオフラインでサイドチェインダック(音声より約17 dB下)。SFXは小さめ(0.1-0.45)。音声キーをパッドするか、ダックがベッドを音声長に切り詰めます(「bgm 110.25s vs slot 113s」の警告を確認): [1:a]apad=whole_dur=<TOTAL>[k];[0:a][k]sidechaincompress=threshold=0.03:ratio=6:attack=25:release=450,atrim=end=<TOTAL>。 カタログベッドなし(HeyGen検索500秒またはログインなし):EL=<key> python <skill>/scripts/el-music.py .media/candidates/bed.mp3 <ms> "<prompt: instrumental, BPM, sits under a narrator, no drops, ends on a resolved chord>" - フィルムの長さで自然に終了するトラックはスプライス不要です。
  • シームチェックノイズ: プレゼンターはすべてのシームを横切って移動するため、seam-check.py はそれだけでFAILします。フラグが立てられた各ペアを scripts/seam-diff.py a.png b.png out.png x0,y0,x1,y1(プレゼンターボックスをマスク)で判断します - その外側が0.00%なら合格です。
  • 縦向き(1080x1920)サイズルール: すべてのシーンの上部バンドは実際の見出し(60-84px)、本文テキスト40px以上、オブジェクトは全幅とプレゼンターの上の高さを使用します。最初のウェーブはフレームの上部45%に約33pxのテキストを出荷しました - ディスパッチ前、後ではなく、frame.mdにこれらの数値を入れます。
  • ループではない: ナレーションフィルムは保持されたロックアップで終了します。シームチェックのループ行は無視します。

ハードルール(プレミアムでスムーズにするためのルール)

  1. ソースからの真実。 プロダクトUIはプロダクト自身のHTML/CSSから移植されます。サードパーティのキャンバスは、ユーザーの実際のキャプチャから再構築され、記憶からではありません。架空のデータのみ。プロダクトが実際に持つ機能のみ。
  2. 1つのカメラ。 すべてのシーンでパースペクティブ1866px(35mm)。カメラレイヤーの下にドリフトレイヤーがあり、何も静止しません。遠くの葉にパララックス+被写界深度ブラー。3Dペアレントに不透明度/フィルター/オーバーフロー/クリップパスなし(フラット化します)。
  3. 1080p+でくっきり。 CSS zoom(.kit-zoom)でUIをスケーリングし、transform: scale(>1) は絶対に使用しません。各ピースは画面上の最も近いサイズで作成します。キャプチャは2倍のPNGで、ネイティブ以下で表示されます。
  4. 60fpsスムーズさの法則。 Transform/不透明度のみ。着地にはスプリング、到着にはexpo/power3、カメラレッグにはinOut。リニア、ステップ、スナップ、丸めは禁止。カメラレッグは0.15~0.3秒重なり、0.6秒以上持続します。
  5. 決定論的。 シーンごとに1つの一時停止されたタイムラインが window.__timelines[id] に最後に登録され、fromTo は明示的な開始を持ち、ランダム/クロック/CSSトランジションはありません。
  6. シームは共有フレームです。 各シームは1つの正確なピクチャで、両方の隣接フレームが約0.1秒間保持し、リグの外側の2Dプレートでロックされます。ハードカット、空フレーム、フェードアウトはありません。最後のフレーム=最初のフレーム。
  7. グリッド上で。 シーンは全体/半バー。クリック、ティック、スタンプ、スワップはビートに着地。音楽はネイティブテンポでバーラインでカット。
  8. コントラクトの修正、シーンの修正ではない。 ルート相対の assets/... パス、数字で始まらないID、シーンIDをプレフィックスとするIDとクラス。すべてのシーンが破るルールは、frame.md / worker-briefで一度修正します。
  9. 禁止事項。 絵文字、ネオングロー、紫から青へのグラデーション、色付き左ボーダーカード、円形スピナー(きらめき)、ストックピープル、「John Doe」、スライドショーペーシング、または何も言わないモーション。
  10. 最終出力はハードウェアGPUを使用したCLIから。 Studioのエクスポート(ソフトウェアパス、約15倍遅い)は決して使用しない。

問題が発生した場合

症状原因修正
Lint: アセットパスエラーサブコンポジション内の ../assets/ルート相対の assets/...(コントラクトで修正)
Lint: 無効なコンポジションIDIDが数字で始まるs01-click、決して 01-click ではない(film.pyが拒否)
パケットビルダーがスローする/パケット > 48KB2つのブループリントID、または rules: が多すぎる1つの id (Adapt)。ブロックあたり2~3ルール
UIがぼやけて見えるトランスフォームスケール、またはオーサリングサイズを超えてカメラに向かって押された.kit-zoom、最も近いサイズでオーサリング
3Dが突然フラットに3D祖先要素に対する不透明度/フィルター/オーバーフローリーフに移動
シームがポップする一方の側が共有フレームを保持していないseam-check.py、ドリフトしている側のプレート/ポーズを修正
シームが数%FAILするがフレームは同一に見えるプレートの下のリグコピーがそのシャドウを2倍にする保持中はプレートのみ表示(frame.mdシーム)
プロダクトUIが実際のアプリからずれる記憶または部分的なCSSから再構築実際のアプリを2倍でキャプチャ(surfaces.md 最初のセクション)
800以上の duplicate_audio_track 警告data-duration なしのSFXassemble.py を再実行
music-search が資格情報なしと言うログインしていないユーザーが自分のターミナルで npx hyperframes auth login を実行
レンダリングが約1フレーム/秒で遅いソフトウェアGPU(Studioエクスポート)render-final.mjs(CLI、--browser-gpu --workers 6)
停止後もレンダリングが実行され続けるシェルを停止してもノードは停止しないプロセスツリーを強制終了(verify-ship.md)
レンダリングの進行状況が表示されない出力が `tail` にパイプされている

セルフテスト

node <skill>/scripts/selftest.mjs

一時フォルダに2シーンのフィルムをスキャフォールドし、ハーネス、アセンブル、check、シームチェック、ハードウェアGPUレンダリング(1920x1080、60fps)、Webエンコードを実行します。PASS と出力する必要があります。新しいマシンで、および編集後に実行します。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

関連スキル