vid-sizzle-reel
從品牌資產與關鍵訊息生成高能量精選片段或 hype 影片。 管線:HyperFrames HTML 合成 (GSAP 時間軸) → headless Chromium → FFmpeg H.264 MP4。 無須 Runway、無須 Pika、無須 AI 影片 API。除 Node.js + FFmpeg 外無執行成本。
與 vid-product-launch 的區別:
vid-product-launch敘述一個故事——一次揭露、五個段落、張力曲線vid-sizzle-reel創造能量與興奮感——快速剪輯、音樂優先、無單一故事節拍
關鍵規則(每次生成前務必閱讀)
-
自行撰寫關鍵訊息——或請使用者撰寫。 這些是會在畫面上顯示的確切詞句。「AI 會搞定」只會產出填充內容。3-5 句犀利台詞就是整部精選片段的全部內容。
-
品牌名稱僅出現在落地段落。 冷開場與鋪陳段應製造懸念而不揭示品牌名稱。落地段落才是最終回報。
-
在撰寫任何 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.to()加入退場動畫。切勿定位在動畫起始狀態。 -
每個計時元素都須加上
class="clip"。 HyperFrames 要求此類別,否則元素對播放器不可見。 -
每個 clip 都須有
data-start、data-duration、data-track-index。 無例外。相同軌道的 clip 不可重疊——請使用不同的 track 索引。 -
禁止使用
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 | 無 | Logo 路徑/URL、品牌色 (hex)、關鍵截圖 |
| 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 | Logo + 標語 + 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/90s)、任何品牌色或 Logo URL。」
若 key_messages 已提供 → 立即進入步驟 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 品牌名稱或第一個 key_message 轉為 kebab-case,最多 25 個字元。
步驟 3:內部架構(不對使用者顯示)
1. 由 duration + cut_style 推導剪輯點時間:
| 段落 | 30s | 60s | 90s |
|---|---|---|---|
| 冷開場 | 0-5s | 0-5s | 0-8s |
| 鋪陳 | 5-25s | 5-40s | 8-65s |
| 高潮 | 25-28s | 40-55s | 65-80s |
| 落地 | 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 對應至場景:
- 冷開場(1 個場景):一個衝擊性統計數字或問題——不包含品牌名稱,製造懸念
- 鋪陳:每個 key_message 一個場景
- 高潮:以兩倍尺寸重複最強訊息,或將 2 則訊息快速閃爍組合
- 落地:品牌名稱 + 標語 + CTA URL
3. 若有提供音樂,偵測節拍同步:
npx hyperframes tts --analyze-beats [music-file]
若無法使用,依 BPM 計算:beat_ms = 60000 / BPM。對齊剪輯至每 2 拍。
4. 選擇語調預設 — 閱讀 references/tone-presets.md 取得 CSS 代幣。
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>
[結束條件]
[如果 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>
[結束條件]
[如果 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>
[結束條件]
<!-- 場景 — 每個場景一個 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">
[冷開場內容 — 一個統計或衝擊性詞句,不含品牌名稱]
</div>
</div>
[每個鋪陳場景一個 div,data-start 依剪輯時長遞增]
[高潮場景]
[落地場景 — 品牌名稱 + 標語 + CTA]
[如果有提供音樂]
<audio id="music"
class="clip"
data-start="0"
data-duration="[總時長]"
data-track-index="10"
data-volume="0.85"
src="[music_path]"
></audio>
[結束條件]
<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(僅用於顆粒效果——不產生其他隨機值)────────────────────
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 ticker 更新顆粒(非逐幀捕捉——HyperFrames 處理此功能)
[結束條件]
// ── GSAP 時間軸 ───────────────────────────────────────────────────────────
const tl = gsap.timeline({ paused: true });
// ── 冷開場 ───────────────────────────────────────────────────────────────
[冷開場的進入與退場補間動畫——請參閱 cut-patterns.md 取得確切模式]
// ── 鋪陳段落 ───────────────────────────────────────────────────────────────
[每則訊息的補間動畫——flash-sequence 或 cinematic-hold,參考 cut-patterns.md]
// ── 高潮段落 ─────────────────────────────────────────────────────────────
[高潮補間動畫——與鋪陳模式相同但時間更緊湊]
// ── 落地段落 ─────────────────────────────────────────────────────────────
[落地進入動畫——logo、標語、CTA。無退場——持續至結束]
// ── 時間軸註冊 ────────────────────────────────────────────────────────────
window.__timelines = window.__timelines || {};
window.__timelines["sizzle-[slug]"] = tl;
</script>
</div>
</body>
</html>
設計品質規則:
- 冷開場統計數字:16:9 格式字型大小 ≥160px,9:16 格式 ≥100px
- 關鍵訊息:字型大小 ≥80px — 一眼即可閱讀
- 落地段落品牌名稱:16:9 格式 ≥120px
- 所有顏色均使用預設代幣變數——除了
var(--accent)在 canvasfillStyle中的實際十六進位值外,禁止使用自由 hex scene-content一律使用padding + flex——內容容器禁止使用position:absolute- 每個具有
data-start的元素都須加上class="clip"
步驟 5:自我品質檢查(進入步驟 6 前修正所有失敗)
內容:
-
key_messages— 每個詞句恰好出現一次,無重複 - 品牌名稱僅出現在落地段落(不在冷開場、鋪陳、高潮)
- 冷開場不包含品牌名稱與產品名稱——僅有統計或衝擊性詞句
- 落地段落包含:品牌名稱 + 標語 + CTA URL(最少三個元素)
- end_card CTA URL 出現在最終畫面
HyperFrames 協定:
- 每個具有
data-start的元素都有class="clip" - 每個 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,透明度 0→1→0) - 相同軌道的 clip 不重疊(檢查所有
data-track-index分組)
音效:
-
window.__sfxTimeline存在且置於時間軸註冊之前 - 每則關鍵訊息有
word-hit事件 - 在鋪陳→高潮轉換邊界有
whoosh - 落地開始時有
cta-chime - 500ms 前不觸發任何音效
步驟 6:Lint、渲染、輸出
npx hyperframes lint
npx hyperframes inspect --json
修正所有 lint 錯誤。修正所有 inspect 錯誤(文字溢出、clip 超出容器)。然後渲染:
npx hyperframes render --output sizzle/[slug]/sizzle-reel.mp4
輸出範例:
## 精選片段:[end_card 中的品牌名稱]
語調:[tone] | 時長:[N]s | 剪輯風格:[cut_style] | 長寬比:[ratio]
節奏:[宣告的模式]
段落
冷開場 [0-Ns]: [衝擊性統計或詞句]
鋪陳 [N-Ns]: [N 則關鍵訊息]
高潮 [N-Ns]: [最強訊息以最大尺寸顯示]
落地 [N-Ns]: [品牌名稱] + "[標語]" + [CTA URL]
檔案
來源:sizzle/[slug]/index.html
輸出:sizzle/[slug]/sizzle-reel.mp4
音效時間軸(嵌入每個合成中)
與 vid-product-launch 相同的 FFmpeg 合成音效系統。將 window.__sfxTimeline 置於 GSAP 時間軸註冊之前。
適用於精選片段的音效類型參考:
| 類型 | 聲音 | 時長 | 放置位置 |
|---|---|---|---|
word-hit | 低頻重擊 (50Hz) + 瞬態點擊 (2.2kHz) + 噪音爆發 | 180ms | 每則關鍵訊息場景開始時 |
whoosh | 雙頻噪音掃掠(1.1kHz 主體 + 4-8kHz 空氣聲) | 700ms | 鋪陳→高潮邊界 |
cta-chime | A 大三和弦 (440+554+659+880Hz) + 迴聲鈴聲 | 1.2s | 落地開始的瞬間 |
window.__sfxTimeline = [
// 每則關鍵訊息對應一個 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 值依宣告的段落時間計算。