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 の下に記録されている |
| 2 | frame.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.cjs | src/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.mjs | media-useのRESTヘルパーを介したHeyGenカタログ(Windows対応)。 |
scripts/music-analyze.py、scripts/music-bars.py | numpyのみのテンポ+バーごとのエネルギー(librosa不要)。 |
scripts/bgm-cut.py | バーラインでベッドをカットし、トラック自身のアウトロをスプライスし、フェードします。--b は長いフィルムの下でベッドをループするために繰り返します。 |
scripts/assemble.py | film.json -> index.html(バーグリッド上のシーンスロット、bgm、持続時間とレーン付きのSFX)。 |
scripts/seam-check.py | すべてのシーム(カットの前/丁度/後)とループ(フレーム0対最後)をピクセル単位で証明します。 |
scripts/render-final.mjs | CLIレンダリング。--browser-gpu を使用し、ログをファイルに出力し、gpuモードとffprobeを検証し、その後 flash-scan.py を実行します。 |
scripts/flash-scan.py | レンダリングのすべてのフレーム:両方の隣接フレームと異なるフレーム(1/60秒間表示される要素)= バグ。 |
scripts/encode-web.mjs | Webエンコード(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.py | ElevenLabsを介したナレーションフィルム(キーは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にこれらの数値を入れます。
- ループではない: ナレーションフィルムは保持されたロックアップで終了します。シームチェックのループ行は無視します。
ハードルール(プレミアムでスムーズにするためのルール)
- ソースからの真実。 プロダクトUIはプロダクト自身のHTML/CSSから移植されます。サードパーティのキャンバスは、ユーザーの実際のキャプチャから再構築され、記憶からではありません。架空のデータのみ。プロダクトが実際に持つ機能のみ。
- 1つのカメラ。 すべてのシーンでパースペクティブ1866px(35mm)。カメラレイヤーの下にドリフトレイヤーがあり、何も静止しません。遠くの葉にパララックス+被写界深度ブラー。3Dペアレントに不透明度/フィルター/オーバーフロー/クリップパスなし(フラット化します)。
- 1080p+でくっきり。 CSS
zoom(.kit-zoom)でUIをスケーリングし、transform: scale(>1)は絶対に使用しません。各ピースは画面上の最も近いサイズで作成します。キャプチャは2倍のPNGで、ネイティブ以下で表示されます。 - 60fpsスムーズさの法則。 Transform/不透明度のみ。着地にはスプリング、到着にはexpo/power3、カメラレッグにはinOut。リニア、ステップ、スナップ、丸めは禁止。カメラレッグは0.15~0.3秒重なり、0.6秒以上持続します。
- 決定論的。 シーンごとに1つの一時停止されたタイムラインが
window.__timelines[id]に最後に登録され、fromToは明示的な開始を持ち、ランダム/クロック/CSSトランジションはありません。 - シームは共有フレームです。 各シームは1つの正確なピクチャで、両方の隣接フレームが約0.1秒間保持し、リグの外側の2Dプレートでロックされます。ハードカット、空フレーム、フェードアウトはありません。最後のフレーム=最初のフレーム。
- グリッド上で。 シーンは全体/半バー。クリック、ティック、スタンプ、スワップはビートに着地。音楽はネイティブテンポでバーラインでカット。
- コントラクトの修正、シーンの修正ではない。 ルート相対の
assets/...パス、数字で始まらないID、シーンIDをプレフィックスとするIDとクラス。すべてのシーンが破るルールは、frame.md / worker-briefで一度修正します。 - 禁止事項。 絵文字、ネオングロー、紫から青へのグラデーション、色付き左ボーダーカード、円形スピナー(きらめき)、ストックピープル、「John Doe」、スライドショーペーシング、または何も言わないモーション。
- 最終出力はハードウェアGPUを使用したCLIから。 Studioのエクスポート(ソフトウェアパス、約15倍遅い)は決して使用しない。
問題が発生した場合
| 症状 | 原因 | 修正 |
|---|---|---|
| Lint: アセットパスエラー | サブコンポジション内の ../assets/ | ルート相対の assets/...(コントラクトで修正) |
| Lint: 無効なコンポジションID | IDが数字で始まる | s01-click、決して 01-click ではない(film.pyが拒否) |
| パケットビルダーがスローする/パケット > 48KB | 2つのブループリント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 なしのSFX | assemble.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 と出力する必要があります。新しいマシンで、および編集後に実行します。