vid-sizzle-reel
ブランドアセットとキーメッセージから、ハイエナジーなサズルリール(ハイプ動画)を生成します。 パイプライン: HyperFrames HTML 構成 (GSAP タイムライン) → ヘッドレス Chromium → FFmpeg H.264 MP4。 Runway、Pika、AI 動画 API は不要。Node.js + FFmpeg 以外のランタイムコストはゼロ。
vid-product-launch との違い:
vid-product-launchは物語を語る — ひとつの明かし、5つのセクション、緊張の弧vid-sizzle-reelはエネルギーと興奮を生み出す — 高速カット、音楽ファースト、単一のストーリービートなし
重要ルール(生成前に毎回読むこと)
-
キーメッセージは自分で書くか、ユーザーに書いてもらうこと。 これらは画面に表示される正確なフレーズです。「AI がなんとかする」は中身のないものになります。3〜5行の鋭いメッセージが、サズルリールの全内容です。
-
ブランド名は Land セクションでのみ表示すること。 Cold Open と Build ではブランド名を出さずに緊張感を構築します。Land セクションがその見返りです。
-
リズムパターンを、HTML を書く前にスクリプトの先頭にコメントとして宣言すること。 フォーマット:
// RHYTHM: flash-sequence | Cold[0-5s]: stat | Build[5-40s]: msg(1.5s)|flash|... | Peak[40-55s]: ... | Land[55-60s]: logo+CTA。すべてのタイミング決定はこれに照らしてチェックします。 -
まず CSS で最終状態のレイアウトを構築すること — GSAP はまだ使わない。 すべての要素を、最も見える瞬間の位置に静的な CSS で配置します。その後、
gsap.from()で GSAP による登場アニメーション、gsap.to()で終了アニメーションを追加します。アニメーションの開始状態で配置しないこと。 -
タイミングを持つすべての要素に
class="clip"を付与すること。 HyperFrames で必須です。これがないと、要素はプレイヤーから見えなくなります。 -
すべてのクリップに
data-start、data-duration、data-track-indexを設定すること。 例外はありません。同じトラックインデックスのクリップは重複できません — 異なるトラックインデックスを使用してください。 -
Math.random()を使用しないこと。 HyperFrames では決定論的な構成が必要です。疑似乱数値が必要な場合は、シード付き PRNG(mulberry32)を使用してください。 -
すべての GSAP タイムラインは
{ paused: true }で開始し、window.__timelines["comp-id"]に登録すること。 HyperFrames プレイヤーが再生を制御します —tl.play()を呼び出さないでください。 -
.scene-contentコンテナにposition: absoluteを使用しないこと。width:100%; height:100%; padding:Npx; display:flexを使用してください。position:absoluteは装飾要素にのみ予約します。 -
音楽: 別の
<audio>要素にdata-track-indexを設定すること。 動画はmuted playsinlineにする必要があります。<video>要素に音声を入れないでください。 -
HTML を生成する前に、
references/cut-patterns.mdとreferences/tone-presets.mdを読むこと。 -
HTML をチャットにダンプしないこと。 ファイルに保存し、概要のみを表示してください。
ステップ 1: 情報収集
必須:
key_messages— 画面に表示する、3〜5行のキャッチーなフレーズ(ユーザーが書く必要があります)
オプションパラメータとデフォルト値:
| パラメータ | デフォルト | 説明 |
|---|---|---|
| brand_assets | なし | ロゴ URL/パス、ブランドカラー(16進数)、キースクリーンショット |
| tone | energetic | energetic / cinematic / emotional / professional |
| music | なし | ファイルパス、または BPM/ジャンル文字列(例: "128bpm electronic") |
| duration | 60 | 30 / 60 / 90 秒 |
| aspect_ratio | 9:16 | 9:16 (1080x1920) / 16:9 (1920x1080) |
| cut_style | auto | fast (1-2s) / cinematic (3-5s) — auto は tone から自動設定 |
| end_card | auto | Land セクション用のロゴ + タグライン + CTA URL |
cut_style の自動デフォルト値: energetic → fast; professional → fast (2s); cinematic → cinematic; emotional → mixed
key_messages がない場合、正確に次のように質問してください:
「サズルリールを生成するには、キーメッセージが必要です。画面に表示する正確なフレーズを教えてください。3〜5行のキャッチーなメッセージをお願いします。これがこの動画で最も重要な要素です。
例: '500以上のグロースチームが利用' / '日単位から分単位へ' / 'Claude、Codex、Geminiと連携'
また、トーン(energetic/cinematic/emotional/professional)、尺(30/60/90秒)、ブランドカラーやロゴ URL があると便利です。」
キーメッセージが提供されたら → すぐにステップ 2 に進みます。
ステップ 2: HyperFrames のインストール
HyperFrames スキルをインストールします(初回のみ — すでにインストール済みの場合はスキップ):
npx skills add heygen-com/hyperframes
環境を確認:
node --version # 22 以上であること
ffmpeg -version # インストールされていること
プロジェクトをスキャフォールド:
npx hyperframes init sizzle-[slug] --example kinetic-type --non-interactive
cd sizzle-[slug]
Slug: end_card のブランド名または最初のキーメッセージから kebab-case で、最大 25 文字。
ステップ 3: 内部アーキテクチャ(ユーザーには非表示)
1. duration + cut_style からカットポイントのタイミングを導出:
| セクション | 30s | 60s | 90s |
|---|---|---|---|
| Cold Open | 0-5s | 0-5s | 0-8s |
| Build | 5-25s | 5-40s | 8-65s |
| Peak | 25-28s | 40-55s | 65-80s |
| Land | 28-30s | 55-60s | 80-90s |
すべての境界をミリ秒に変換します。cut_style からシーンごとの長さを計算:
fast: メッセージあたり 1.2s-1.8s(デフォルト 1.5s を使用)cinematic: メッセージあたり 3.5s-5.0s(デフォルト 4.0s を使用)mixed: セクションのエネルギーに応じて 2.0s-4.0s で変動
2. key_messages をシーンにマッピング:
- Cold Open(1シーン): 1つのインパクト統計または質問 — ブランド名なし、興味をそそる
- Build: キーメッセージごとに 1 シーン
- Peak: 最も強いメッセージを 2 倍のスケールで繰り返す、または 2 つのメッセージを高速フラッシュで組み合わせる
- Land: ブランド名 + タグライン + CTA URL
3. 音楽が提供された場合、ビート同期を検出:
npx hyperframes tts --analyze-beats [music-file]
利用できない場合は、BPM から計算: beat_ms = 60000 / BPM。2 拍ごとにカットを合わせます。
4. トーンプリセットを選択 — CSS トークンについては references/tone-presets.md を参照。
5. リズムパターンを宣言 — スクリプトの先頭にコメントを書きます:
// RHYTHM: flash-sequence
// Cold Open [0-5000ms]: stat
// Build [5000-40000ms]: msg-1(1500ms)|flash|msg-2(1500ms)|flash|msg-3(1500ms)|flash|msg-4(1500ms)|flash|build-accum(10000ms)
// Peak [40000-55000ms]: msg-strongest(1200ms)|flash|msg-2nd(1200ms)|flash|HOLD(5600ms)
// Land [55000-60000ms]: logo+tagline+cta
6. ピクセル寸法を決定:
9:16→ W=1080、H=1920(デフォルト — Instagram Reels、TikTok、YouTube Shorts)16:9→ W=1920、H=1080
ステップ 4: HyperFrames HTML 構成
コードを書く前に references/cut-patterns.md と HyperFrames スキル (/hyperframes) を読んでください。
必須の HTML スケルトン:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[トーンプリセットからのフォント CDN リンク]
<style>
:root {
[トーンプリセットからのすべての CSS トークン]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
</style>
</head>
<body>
<div
data-composition-id="sizzle-[slug]"
data-width="[W]"
data-height="[H]"
style="position:relative;width:[W]px;height:[H]px;overflow:hidden;background:var(--bg)"
>
[cut-flash: true の場合]
<div id="cut-flash"
style="position:absolute;inset:0;background:var(--cut-flash-bg);opacity:0;pointer-events:none;z-index:200"></div>
[end if]
[film-grain: true の場合]
<canvas id="grain-overlay"
width="240" height="135"
style="position:absolute;inset:0;width:[W]px;height:[H]px;pointer-events:none;opacity:0.022;mix-blend-mode:overlay;z-index:50"></canvas>
[end if]
[vignette: true の場合]
<div id="vignette-overlay"
style="position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 35%,rgba(0,0,0,0.65) 100%);pointer-events:none;z-index:51"></div>
[end if]
<!-- シーン — シーンごとに 1 つの div -->
<div id="scene-cold"
class="clip"
data-start="0"
data-duration="5"
data-track-index="0"
style="position:absolute;inset:0;opacity:0"
>
<div class="scene-content" style="width:100%;height:100%;padding:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box">
[cold open コンテンツ — 1 つの統計またはインパクトフレーズ、ブランド名なし]
</div>
</div>
[Build シーンごとに 1 つの div、data-start はカット時間分ずつ増加]
[Peak シーン]
[Land シーン — ブランド名 + タグライン + CTA]
[音楽が提供された場合]
<audio id="music"
class="clip"
data-start="0"
data-duration="[total_duration]"
data-track-index="10"
data-volume="0.85"
src="[music_path]"
></audio>
[end if]
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
// ── リズム宣言 ──────────────────────────────────────────────────────────────
// RHYTHM: [flash-sequence | cinematic-hold | beat-sync | mixed]
// Cold Open [0-Nms]: ...
// Build [N-Nms]: ...
// Peak [N-Nms]: ...
// Land [N-Nms]: ...
// ── シード付き PRNG(grain のみ — その他の乱数は使用しない)───────────────────
function mulberry32(seed) {
return function() {
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
[film-grain: true の場合]
// ── フィルムグレイン(cinematic/emotional のみ)─────────────────────────────
const grainCanvas = document.getElementById('grain-overlay');
const grainCtx = grainCanvas ? grainCanvas.getContext('2d') : null;
// グレインは GSAP ティッカーで更新(フレームキャプチャではない — HyperFrames が処理)
[end if]
// ── GSAP タイムライン ───────────────────────────────────────────────────────
const tl = gsap.timeline({ paused: true });
// ── COLD OPEN ───────────────────────────────────────────────────────────────
[cold open の登場と終了のトゥイーン — 正確なパターンは cut-patterns.md を参照]
// ── BUILD セクション ─────────────────────────────────────────────────────────
[メッセージごとのトゥイーン — cut-patterns.md の flash-sequence または cinematic-hold]
// ── PEAK セクション ─────────────────────────────────────────────────────────
[peak トゥイーン — build と同じパターンだがタイミングはよりタイトに]
// ── LAND セクション ─────────────────────────────────────────────────────────
[land の登場 — ロゴ、タグライン、CTA。終了なし — 最後まで保持]
// ── タイムライン登録 ────────────────────────────────────────────────────────
window.__timelines = window.__timelines || {};
window.__timelines["sizzle-[slug]"] = tl;
</script>
</div>
</body>
</html>
デザイン品質ルール:
- Cold open の統計: 16:9 の場合はフォントサイズ ≥160px、9:16 の場合は ≥100px
- キーメッセージ: フォントサイズ ≥80px — 一目で読めること
- Land セクションのブランド名: 16:9 の場合は ≥120px
- すべての色はプリセットのトークン変数から — フリーヘックスは不可。
var(--accent)リテラル16進数のみ、canvas のfillStyle内で使用 scene-contentは常にpadding + flexを使用 — コンテンツコンテナにposition:absoluteは使用しないdata-startを持つすべての要素にclass="clip"
ステップ 5: セルフ QA(ステップ 6 の前にすべての失敗を修正すること)
コンテンツ:
-
key_messages— 各フレーズが正確に 1 回出現、重複なし - ブランド名は Land セクションでのみ表示(cold open、build、peak では表示しない)
- Cold open にはブランド名も製品名も含まれていない — 統計またはインパクトフレーズのみ
- Land セクションに以下がある: ブランド名 + タグライン + CTA URL(最低 3 要素)
- end_card CTA URL が最終フレームに存在する
HyperFrames 契約:
-
data-startを持つすべての要素にclass="clip" - すべてのクリップに
data-start、data-duration、data-track-index - ルートのコンポジション div に
data-composition-id -
window.__timelines["sizzle-[slug]"] = tlが登録されている - すべてのタイムラインは
{ paused: true }で開始 -
Math.random()なし — シード付き PRNG のみ -
tl.play()またはtl.resume()の呼び出しなし
GSAP / レイアウト:
- すべての登場は
gsap.from()(画面外から CSS 位置へ) - すべての終了は
gsap.to()(CSS 位置から画面外へ) -
.scene-contentコンテナにposition:absoluteなし - リズムパターンがスクリプトの先頭にコメントとして宣言されている
カットタイミング:
-
fastトーン: 平均カット時間 ≤ 2.0s -
cinematicトーン: 平均カット時間 ≥ 3.0s -
energeticトーンの場合、カット境界ごとにカットフラッシュが発火(60ms、opacity 0→1→0) - 同じトラックのクリップは重複しない(すべての
data-track-indexグループを確認)
SFX:
-
window.__sfxTimelineが存在し、タイムライン登録の前に配置されている - キーメッセージごとの
word-hitイベント - Build → Peak の遷移境界での
whoosh - Land 開始時の
cta-chime - 500ms より前に SFX が発火しない
ステップ 6: Lint、レンダリング、出力
npx hyperframes lint
npx hyperframes inspect --json
すべての lint エラーを修正します。すべての inspect エラー(テキストオーバーフロー、クリップがコンテナからはみ出すなど)を修正します。その後、レンダリング:
npx hyperframes render --output sizzle/[slug]/sizzle-reel.mp4
出力:
## サズルリール: [end_card のブランド名]
トーン: [tone] | 尺: [N]s | カットスタイル: [cut_style] | アスペクト比: [ratio]
リズム: [宣言されたパターン]
セクション
Cold Open [0-Ns]: [インパクト統計またはフレーズ]
Build [N-Ns]: [N 個のキーメッセージをリスト]
Peak [N-Ns]: [最も強いメッセージを最大スケールで]
Land [N-Ns]: [ブランド名] + "[タグライン]" + [CTA URL]
ファイル
Source: sizzle/[slug]/index.html
Output: sizzle/[slug]/sizzle-reel.mp4
SFX タイムライン(すべてのコンポジションに埋め込む)
vid-product-launch と同じ FFmpeg 合成 SFX システム。window.__sfxTimeline を GSAP タイムライン登録の直前に配置します。
サズルリール用の SFX タイプリファレンス:
| タイプ | 音声 | 長さ | 配置 |
|---|---|---|---|
word-hit | サブパンチ (50Hz) + 過渡クリック (2.2kHz) + ノイズバースト | 180ms | キーメッセージシーン開始ごとに 1 回 |
whoosh | 2バンドノイズスイープ (1.1kHz ボディ + 4-8kHz エアー) | 700ms | Build → Peak 境界 |
cta-chime | Aメジャーコード (440+554+659+880Hz) + 反響ベルのきらめき | 1.2s | Land 開始と同時 |
window.__sfxTimeline = [
// キーメッセージごとに 1 つの word-hit(シーン開始時間から計算)
{ ms: MSG_1_START_MS, sfx: 'word-hit', vol: 0.50 },
{ ms: MSG_2_START_MS, sfx: 'word-hit', vol: 0.50 },
{ ms: MSG_3_START_MS, sfx: 'word-hit', vol: 0.50 },
// ... 各メッセージ分繰り返す
{ ms: PEAK_START_MS - 700, sfx: 'whoosh', vol: 0.55 },
{ ms: LAND_START_MS, sfx: 'cta-chime', vol: 0.65 },
];
すべての ms 値は、宣言されたセクションの境界から計算します。