Communitygithub.com

vid-product-launch — 五幕式電影感MP4

從產品簡述生成電影感發布影片。五幕敘事;HTML+Playwright+FFmpeg,零 AI 影片 API。666★ 包。今晚新入庫。

vid-product-launch — 五幕式電影感MP4 是什麼?

Launch Video 步驟2:OpenDirectory 666★。零 API 五幕成片,接在官網构图之後。

相容平台✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/Varnan-Tech/opendirectory/tree/main/skills/vid-product-launch

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

vid-product-launch

從產品描述和發布情境生成敘事性的 MP4 產品發布影片。 流程:HTML/CSS 動畫 → 無頭 Chromium(Playwright,逐幀擷取)→ FFmpeg(H.264 MP4)。 不需要 React。不需要 AI 影片 API。不需要 Python。除了 Playwright 和 FFmpeg 之外,零執行成本。


關鍵規則(每次生成前請閱讀)

  1. 標語(tagline)不可省略。請勿跳過它。 它是產品在 4-6 個字內的核心承諾。如果使用者未提供,請從描述中推導出一個——一個精準、使用主動語態的濃縮。切勿寫「[標語在此]」或留空。

  2. 揭露時刻(reveal moment)必須讓人感覺是應得的。 「鋪陳」(tease)和「建構」(build)章節存在的目的是為了營造張力。如果產品名稱出現在影片的前 20%,敘事就會崩潰。在「揭露」(Reveal)章節之前,絕對不能透露產品名稱。

  3. product_name 的字型大小:16:9 最小 120px,9:16 最小 80px。 在揭露時刻,產品名稱必須是最大的元素。如果它不突出,揭露就失敗了。

  4. 一個證明數據(proof stat)。不是一個列表。 如果使用者提供多個數據,選最強的一個。列出 5 個數字會破壞衝擊力。一個超大的數字才能創造它。

  5. 使用 window.renderFrame(t) — 場景轉場不要用 CSS @keyframes。 CSS 的 currentTime 查找對於 Chromium 中的反向查找會被無聲地忽略。renderFrame 函式直接從毫秒計算元素樣式。Playwright 每幀呼叫一次。

  6. 任何元素上都不使用 animation-delay。 有了 renderFrame 就不需要它。如果你寫了 animation-delay,停下來——你正在使用錯誤的架構。

  7. window.__videoReady = true 只能在 document.fonts.ready.then(...) 內部設定。 絕不能同步設定。

  8. 暴露 window.__stopPreview()。 rAF 預覽迴圈會與 Playwright 的 evaluate/screenshot 呼叫產生競爭。擷取腳本會在幀迴圈之前呼叫 __stopPreview()。

  9. 在場景邊界檢查中使用 t < startMs(而不是 t <= startMs)。 t <= 0 在第 0 幀會導致場景 1 變成黑色。

  10. Body = 精確的像素尺寸。 寬度和高度是整數。不能使用 %、vw/vh、rem。

  11. 在生成任何 HTML 之前,請閱讀 references/scene-library.md 和 references/style-presets.md。

  12. 切勿在聊天中傾倒 HTML。 儲存到檔案。只顯示摘要。

  13. 膠片顆粒畫布(Film grain canvas)必須是 240×135,而不是 W×H。 在 canvas 元素上設定 width="240" height="135",然後用 style="width:[W]px;height:[H]px" 拉伸。在 1920×1080 解析度下的全尺寸顆粒處理速度會慢 64 倍——每幀 8MB 的 ImageData——這會讓 1800 幀的匯出花費數小時。

  14. 鋪陳章節必須是暗色(#000 或接近黑色),無論調性為何。 鋪陳章節的白色背景看起來像示範幻燈片,而不是產品發布影片。從暗到亮的敘事弧線(暗色鋪陳 → 暗色建構 → 產品揭露)是發布影片製造戲劇張力的方式。即使是 minimal 和 energetic 預設,也應該在鋪陳-問題和張力-建構場景中使用 background: #000。

  15. 每個鋪陳詞(tease word)都必須是它自己的節拍(beat),字型大小 200px 以上。 逐詞顯示在同一行衝擊力不夠。每個問題詞(例如「研究。」/「寫作。」/「推廣。」/「重複。」)獲得 1500–1800ms 的螢幕時間,使用 font-size: 200px; font-weight: 900 置中,一次一個。請參閱 scene-library.md 中的 tease-words 模式。

  16. 建構章節必須展示敘述問題的內容——而不僅僅是粒子。 使用 terminal-card 模式:3 張卡片依序出現,並帶有打字機動畫,顯示正在進行的手動工作。卡片具有 border: 1px solid rgba(255,255,255,0.07)、藍色圓點強調、等寬字型。僅靠粒子持續 20 秒是空白的螢幕時間。

  17. 暗色場景上的點陣 CSS 背景(Dot-grid CSS background)。 在建構、揭露、證明和 CTA 場景中,加入 background-image: radial-gradient(circle, rgba(255,255,255,0.035) 1px, transparent 1px); background-size: 60px 60px;。它不需要計算成本,還能增加深度。


步驟 1:輸入

必要:

  • product_name — 正在發布的產品或功能的名稱
  • product_description — 2–3 句話:它做什麼、為誰而做、主要益處

可選參數和預設值:

參數預設值描述
tagline自動4–6 個字 — 產品的核心承諾
problem_statement自動推斷用於鋪陳章節的 1 句話(產品解決的痛苦)
proof_stat自動推斷單一指標(例如「500+ 團隊」、「10 倍速度」、「節省 $200 萬」)
cta自動URL 或行動短語(例如「opendirectory.dev」、「加入候補名單」)
launch_date—ISO 日期字串(啟用倒數計時卡場景)
tonecinematiccinematic / energetic / minimal / emotional
duration6030 / 60 / 90(秒)
aspect_ratio16:916:9 (1920×1080) / 9:16 (1080×1920)
letterboxfalse電影級 2.35:1 黑邊 — 僅限 16:9
music—音訊檔案路徑 (mp3/m4a/wav)
fps30每秒幀數 (24、30 或 60)

如果缺少 product_name 或 product_description,請確切詢問:

「要建立發布影片,我需要兩件事:

  1. 產品名稱 — 這個產品叫什麼?
  2. 產品描述 — 2–3 句話:它做什麼、為誰而做、主要益處是什麼?

可選:標語(4–6 個字)、證明數據(一個數字)、CTA(URL 或行動短語)、調性(cinematic / energetic / minimal / emotional)、長度(30 / 60 / 90 秒)。」

如果兩者都存在 → 立即進行步驟 2。


步驟 2:內部架構(永遠不向使用者顯示)

1. 推導缺失的參數:

  • tagline(如果未提供):根據 product_description 撰寫一個。4–6 個字。主動語態。沒有贅詞(「未來是…」、「隆重介紹…」)。推導出最精準的承諾。
  • problem_statement(如果未提供):從 product_description 推斷。1 句話陳述痛苦。絕不提及產品名稱。撰寫讓觀眾點頭認同的內容。
  • proof_stat(如果未提供):從 product_description 推斷。如果沒有可用的數據,請使用可信的規模指標(「獲得 500+ 團隊信賴」、「從數天縮短到數分鐘」)。
  • cta(如果未提供):如果可從 product_name 推斷,則使用產品域名,否則使用「了解更多」作為行動短語。

2. 從 duration 計算章節時間:

章節30 秒60 秒90 秒起始公式
鋪陳0–5s0–10s0–15s0 毫秒
建構5–12s10–30s15–40s鋪陳結束
揭露12–20s30–45s40–60s建構結束
證明20–25s45–55s60–75s揭露結束
CTA25–30s55–60s75–90s證明結束

將每個邊界轉換為毫秒。指派給常數:

TEASE_START_MS, TEASE_END_MS
BUILD_START_MS, BUILD_END_MS
REVEAL_START_MS, REVEAL_END_MS
PROOF_START_MS, PROOF_END_MS
CTA_START_MS, CTA_END_MS

3. 為每個章節選擇場景:

  • 鋪陳: 總是以 blackout-opener(1500 毫秒)開始。然後是 tease-words(每個問題關鍵字作為自己的 1600–1800 毫秒節拍,200px 以上)。如果提供了 launch_date,可選擇在鋪陳結尾加入 countdown-card。
  • 建構: terminal-card 序列(3 張卡片顯示正在輸入的手動任務)+ 可選的結尾強調行。長度 ≥ 30 秒可獲得全部 3 張卡片。長度 = 30 秒則使用 2 張卡片。切勿讓僅有粒子的螢幕持續超過 5 秒——螢幕必須顯示敘述問題的內容。
  • 揭露: reveal-hero — 始終在這個章節的第一個。如果長度 ≥ 60 秒,可選擇接著顯示 tagline-card。
  • 證明: 如果有數字型數據,則使用 proof-stat。否則使用 feature-bullet。
  • CTA: cta-card — 始終、始終放在最後。

典型場景數量:30 秒 = 4 個場景,60 秒 = 6–7 個場景,90 秒 = 7–8 個場景。

4. 計算每個場景的時間: 在每個章節內,將時間平均分配給該章節中的各個場景。 例外:blackout-opener 總是準確獲得鋪陳章節的前 1500 毫秒。

5. 決定像素尺寸:

  • 16:9 → W=1920, H=1080
  • 9:16 → W=1080, H=1920

6. 信箱效果計算(如果啟用,僅限 16:9):

  • 2.35:1 內容高度 = W / 2.35 = 1920 / 2.35 ≈ 817px
  • 條形高度 = (H - 817) / 2 = (1080 - 817) / 2 ≈ 132px
  • 頂部條:top: 0; height: 132px
  • 底部條:bottom: 0; height: 132px

7. 電影效果標誌(按預設):

  • cinematic:膠片顆粒=開啟(畫布 240×135,透明度 0.025),暗角=開啟,漏光=開啟(暖金色),暗色場景上的點陣=開啟
  • energetic:膠片顆粒=關閉,暗角=關閉,點陣=開啟,揭露時有白色閃光
  • minimal:膠片顆粒=開啟(畫布 240×135,透明度 0.022),暗角=開啟(輕微,0.5 徑向),點陣=開啟,無漏光 — 強調色 = #4B9FFF(電光藍)
  • emotional:膠片顆粒=開啟(畫布 240×135,透明度 0.018,暖色調混合),暗角=開啟(柔和),漏光=開啟(銅暖色),點陣=關閉

所有預設:鋪陳和建構場景一律使用暗色背景(#000 或接近黑色),無論預設為何。

8. 在 HTML 中嵌入 window.__sfxTimeline(始終存在——即使未提供音樂時也是如此):

匯出腳本會讀取這個陣列,使用 FFmpeg 的 aevalsrc/anoisesrc 原生合成每種 SFX 類型,並使用 adelay 將事件放置在精確的毫秒偏移處。不需要外部音訊檔案。

SFX 類型參考:

類型聲音持續時間備註
word-hit低頻衝擊(50Hz)+ 瞬態點擊(2.2kHz)+ 噪音爆發180 毫秒每個鋪陳詞一個 — 透過 amix 合成 3 層
type-sequence15Hz 機械鍵盤點擊聲(sin^30 脈衝包絡)1.9 秒每張卡片一個 — 持續整個 TYPE_DUR
whoosh雙頻噪音掃掠(1.1kHz 主體 + 4–8kHz 空氣聲)700 毫秒在 BUILD_CLOSE 轉場處
tension-riser低沉隆隆聲(250Hz)+ 隨 t/2.8 斜坡增長的次低音2.9 秒從 BUILD_CLOSE + 700ms 開始 — 在 REVEAL_START 處達到峰值
reveal-boom次低音(45Hz)+ 主體(90Hz)+ 閃爍聲(4.5–11kHz)+ 85 毫秒迴響900 毫秒精確在 REVEAL_START 處 — 最重的打擊
counter-tick諧波點擊聲(880Hz + 440Hz + 1760Hz + 2640Hz)80 毫秒3 個節拍 — 漸弱 0.32 → 0.26 → 0.20
cta-chimeA 大三和弦(440 + 554 + 659 + 880Hz)+ 帶有迴響的鐘聲閃爍1.2 秒精確在 CTA_START 處

SFX 時間映射 — 從章節常數計算 ms 值,切勿硬編碼:

window.__sfxTimeline = [
  // ── 鋪陳:每個詞節拍一個 word-hit,最後一個更大聲 ───────────────────────
  { ms: WORD_BEATS[0].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[1].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[2].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[N].start,   sfx: 'word-hit',      vol: 0.65 },  // 最後一個詞更大聲

  // ── 建構:每張卡片一個 type-sequence(1.9 秒,匹配 TYPE_DUR 1800ms) ────────
  { ms: CARDS[0].start + 180,  sfx: 'type-sequence', vol: 0.28 },
  { ms: CARDS[1].start + 180,  sfx: 'type-sequence', vol: 0.28 },
  { ms: CARDS[2].start + 180,  sfx: 'type-sequence', vol: 0.28 },

  // ── 轉場:whoosh 接著 2.9 秒的 riser,在 REVEAL_START 達到峰值 ───────────
  { ms: BUILD_CLOSE,           sfx: 'whoosh',        vol: 0.50 },
  { ms: BUILD_CLOSE + 700,     sfx: 'tension-riser', vol: 0.35 },

  // ── 揭露:電影級重擊 ─────────────────────────────────────────────
  { ms: REVEAL_START,          sfx: 'reveal-boom',   vol: 0.88 },

  // ── 證明:諧波滴答聲,漸弱 ───────────────────────────────────
  { ms: PROOF_START,           sfx: 'counter-tick',  vol: 0.32 },
  { ms: PROOF_START + 2200,    sfx: 'counter-tick',  vol: 0.26 },
  { ms: PROOF_START + 4400,    sfx: 'counter-tick',  vol: 0.20 },

  // ── CTA:A 大三和弦降落 ────────────────────────────────────────────
  { ms: CTA_START,             sfx: 'cta-chime',     vol: 0.62 },
];

將此區塊放在預覽迴圈之前,window.renderFrame 之後。


步驟 3:HTML 生成

在撰寫任何程式碼之前,請閱讀 references/scene-library.md 和 references/style-presets.md。 使用這些檔案中的確切 CSS 類別名稱、HTML 結構和 renderFrame 模式。

必要的 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; }
html, body {
  width: [W]px; height: [H]px;
  overflow: hidden;
  background: var(--bg);
  font-family: var(--font-body);
  position: relative;
}

.scene {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px;
  opacity: 0;
  will-change: opacity, transform;
}
.scene-inner {
  width: 100%;
  max-width: 1200px;
}

[貼上 scene-library.md 中每種場景類型的 CSS]
</style>
</head>
<body>

<!-- 電影覆蓋層元素 — 渲染在所有場景之上 -->
[如果膠片顆粒:開啟]
<canvas id="grain-overlay"
  width="[W]" height="[H]"
  style="position:fixed;inset:0;pointer-events:none;opacity:0.025;mix-blend-mode:overlay;z-index:50"></canvas>
[結束如果]

[如果暗角:開啟]
<div id="vignette-overlay"
  style="position:fixed;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 id="light-leak"
  style="position:fixed;inset:0;background:linear-gradient(135deg,rgba(255,220,140,0.5) 0%,rgba(255,255,255,0.3) 45%,transparent 70%);opacity:0;pointer-events:none;z-index:52"></div>
[結束如果]

[如果信箱效果:開啟]
<div id="lbox-top" style="position:fixed;top:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
<div id="lbox-bot" style="position:fixed;bottom:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
[結束如果]

<!-- 場景 -->
[來自 scene-library.md 模板的場景 HTML,每個選定的場景一個]

<script>
window.__videoReady = false;
window.TOTAL_DURATION_MS = [duration * 1000];

// ── 章節時間常數 ──────────────────────────────────────────────────
const TEASE_START_MS  = [N];
const TEASE_END_MS    = [N];
const BUILD_START_MS  = [N];
const BUILD_END_MS    = [N];
const REVEAL_START_MS = [N];
const REVEAL_END_MS   = [N];
const PROOF_START_MS  = [N];
const PROOF_END_MS    = [N];
const CTA_START_MS    = [N];
const CTA_END_MS      = [N];

// ── 場景節拍定義 ──────────────────────────────────────────────────
const WORD_BEATS = [
  { start: TEASE_START_MS + 1500, end: TEASE_START_MS + 3200 },
  { start: TEASE_START_MS + 3200, end: TEASE_START_MS + 4900 },
  { start: TEASE_START_MS + 4900, end: TEASE_START_MS + 6600 },
];
if (TEASE_END_MS - TEASE_START_MS > 6600) {
  WORD_BEATS.push({ start: TEASE_START_MS + 6600, end: TEASE_START_MS + 8300 });
}

const TYPE_DUR = 1800;
const BUILD_CLOSE = BUILD_END_MS - 800;
const CARDS = [
  { start: BUILD_START_MS + 400,                            end: BUILD_START_MS + 400 + TYPE_DUR },
  { start: BUILD_START_MS + 400 + (TEASE_END_MS - TEASE_START_MS < 8000 ? 3000 : 6000), end: BUILD_START_MS + 400 + TYPE_DUR + (TEASE_END_MS - TEASE_START_MS < 8000 ? 3000 : 6000) },
  { start: BUILD_START_MS + 400 + (TEASE_END_MS - TEASE_START_MS < 8000 ? 6000 : 12000), end: BUILD_START_MS + 400 + TYPE_DUR + (TEASE_END_MS - TEASE_START_MS < 8000 ? 6000 : 12000) },
];

// ── 核心渲染引擎 ──────────────────────────────────────────────────
function renderFrame(t) {
  // 場景透明度計算
  const scenes = document.querySelectorAll('.scene');
  scenes.forEach(scene => {
    const start = parseInt(scene.dataset.start);
    const end = parseInt(scene.dataset.end);
    const fadeIn = parseInt(scene.dataset.fadeIn) || 0;
    const fadeOut = parseInt(scene.dataset.fadeOut) || 0;
    
    if (t < start || t >= end) {
      scene.style.opacity = '0';
      scene.style.pointerEvents = 'none';
      return;
    }
    
    // 淡入
    if (t < start + fadeIn) {
      const progress = (t - start) / fadeIn;
      scene.style.opacity = Math.min(1, progress);
    }
    // 淡出
    else if (t > end - fadeOut) {
      const progress = (t - (end - fadeOut)) / fadeOut;
      scene.style.opacity = Math.max(0, 1 - progress);
    } else {
      scene.style.opacity = '1';
    }
    scene.style.pointerEvents = 'auto';
  });

  // ── 場景特定的 renderFrame 邏輯 ─────────────────────────────
  // blackout-opener:不需要特殊邏輯

  // tease-words:根據 t 控制每個詞的不透明度
  WORD_BEATS.forEach((beat, idx) => {
    const word = document.getElementById(`tease-word-${idx}`);
    if (!word) return;
    if (t >= beat.start && t < beat.end) {
      word.style.opacity = '1';
    } else {
      word.style.opacity = '0';
    }
  });

  // terminal-card:根據卡片時間設定打字機進度
  CARDS.forEach((card, idx) => {
    const cardEl = document.getElementById(`terminal-card-${idx}`);
    if (!cardEl) return;
    if (t >= card.start && t < card.end) {
      cardEl.style.opacity = '1';
      const typeEl = cardEl.querySelector('.typewriter');
      if (typeEl) {
        const progress = Math.min(1, (t - card.start) / TYPE_DUR);
        typeEl.style.setProperty('--type-progress', progress);
      }
    } else {
      cardEl.style.opacity = '0';
    }
  });

  // reveal-hero:在 REVEAL_START 處顯示產品名稱
  const revealHero = document.getElementById('reveal-hero');
  if (revealHero) {
    if (t >= REVEAL_START_MS && t < REVEAL_END_MS) {
      revealHero.style.opacity = '1';
      // 可選的縮放動畫
      const progress = Math.min(1, (t - REVEAL_START_MS) / 500);
      revealHero.style.transform = `scale(${0.8 + 0.2 * progress})`;
    } else {
      revealHero.style.opacity = '0';
    }
  }

  // proof-stat:在 PROOF_START 處的數字動畫
  const proofStat = document.getElementById('proof-stat');
  if (proofStat) {
    if (t >= PROOF_START_MS && t < PROOF_END_MS) {
      proofStat.style.opacity = '1';
    } else {
      proofStat.style.opacity = '0';
    }
  }

  // cta-card:在 CTA_START 處顯示
  const ctaCard = document.getElementById('cta-card');
  if (ctaCard) {
    if (t >= CTA_START_MS && t < CTA_END_MS) {
      ctaCard.style.opacity = '1';
    } else {
      ctaCard.style.opacity = '0';
    }
  }

  // 覆蓋層:如果使用,根據需要控制光洩漏和膠片顆粒
  const lightLeak = document.getElementById('light-leak');
  if (lightLeak) {
    if (t >= REVEAL_START_MS && t < REVEAL_START_MS + 2000) {
      const progress = (t - REVEAL_START_MS) / 2000;
      lightLeak.style.opacity = Math.min(0.6, progress * 0.6);
    } else {
      lightLeak.style.opacity = '0';
    }
  }
}

// ── 膠片顆粒引擎 ──────────────────────────────────────────────────
const grainCanvas = document.getElementById('grain-overlay');
if (grainCanvas) {
  const ctx = grainCanvas.getContext('2d');
  const grainW = 240, grainH = 135;
  const imageData = ctx.createImageData(grainW, grainH);
  function updateGrain() {
    const data = imageData.data;
    for (let i = 0; i < data.length; i += 4) {
      const val = Math.random() * 256;
      data[i] = val;   // R
      data[i+1] = val; // G
      data[i+2] = val; // B
      data[i+3] = 255; // A
    }
    ctx.putImageData(imageData, 0, 0);
    requestAnimationFrame(updateGrain);
  }
  updateGrain();
}

// ── SFX 時間軸 ───────────────────────────────────────────────────
window.__sfxTimeline = [
  // ── 鋪陳:每個詞節拍一個 word-hit,最後一個更大聲 ───────────────────────
  { ms: WORD_BEATS[0].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[1].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[2].start,   sfx: 'word-hit',      vol: 0.55 },
  { ms: WORD_BEATS[WORD_BEATS.length-1].start, sfx: 'word-hit', vol: 0.65 },

  // ── 建構:每張卡片一個 type-sequence(1.9 秒,匹配 TYPE_DUR 1800ms) ────────
  { ms: CARDS[0].start + 180,  sfx: 'type-sequence', vol: 0.28 },
  { ms: CARDS[1].start + 180,  sfx: 'type-sequence', vol: 0.28 },
  { ms: CARDS[2].start + 180,  sfx: 'type-sequence', vol: 0.28 },

  // ── 轉場:whoosh 接著 2.9 秒的 riser,在 REVEAL_START 達到峰值 ───────────
  { ms: BUILD_CLOSE,           sfx: 'whoosh',        vol: 0.50 },
  { ms: BUILD_CLOSE + 700,     sfx: 'tension-riser', vol: 0.35 },

  // ── 揭露:電影級重擊 ─────────────────────────────────────────────
  { ms: REVEAL_START,          sfx: 'reveal-boom',   vol: 0.88 },

  // ── 證明:諧波滴答聲,漸弱 ───────────────────────────────────
  { ms: PROOF_START,           sfx: 'counter-tick',  vol: 0.32 },
  { ms: PROOF_START + 2200,    sfx: 'counter-tick',  vol: 0.26 },
  { ms: PROOF_START + 4400,    sfx: 'counter-tick',  vol: 0.20 },

  // ── CTA:A 大三和弦降落 ────────────────────────────────────────────
  { ms: CTA_START,             sfx: 'cta-chime',     vol: 0.62 },
];

// ── 預覽迴圈 ───────────────────────────────────────────────────────
let __previewRAF = null;
let __previewStart = null;

window.__stopPreview = function() {
  if (__previewRAF) {
    cancelAnimationFrame(__previewRAF);
    __previewRAF = null;
  }
};

function __previewLoop(timestamp) {
  if (!__previewStart) __previewStart = timestamp;
  const elapsed = timestamp - __previewStart;
  const t = Math.min(elapsed, window.TOTAL_DURATION_MS);
  renderFrame(t);
  if (t < window.TOTAL_DURATION_MS) {
    __previewRAF = requestAnimationFrame(__previewLoop);
  }
}

// ── 字型就緒後啟動 ────────────────────────────────────────────────
document.fonts.ready.then(() => {
  window.__videoReady = true;
  __previewLoop(performance.now());
});
</script>
</body>
</html>

步驟 4:匯出

  1. 將 HTML 儲存到 output.html
  2. 使用 Playwright 腳本在所需幀率下逐幀擷取
  3. 使用 FFmpeg 將幀合併成 H.264 MP4
  4. 如果提供了音樂,則使用 FFmpeg 混合音訊

匯出腳本範例會先在 __videoReady 上等待,然後逐幀呼叫 renderFrame(t)。


參考資料

  • references/scene-library.md:場景模板、CSS、HTML 結構
  • references/style-presets.md:字型、顏色、背景、動畫曲線

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

Varnan-Tech/opendirectory

Use when the user asks to generate a blog cover image, thumbnail, or article header. Automatically uses modern typography, brand logos, and Google Search grounding to create beautiful 16:9 images with Gemini 3.1 Flash Image Preview.

Varnan-Tech/opendirectory

World-class brand strategist and naming expert. Uses an interrogation-led discovery phase to extract your brand's DNA, then applies scientific naming frameworks (Phonosemantics) and automated multi-TLD domain checking.

Varnan-Tech/opendirectory

Use when the user asks to generate or update a project's CLAUDE or AGENTS context file from a codebase scan. Writes a focused file under 100 lines containing only the non-obvious build commands, conventions, and gotchas Claude Code needs.

Varnan-Tech/opendirectory

Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.co or the open-source Reacher engine.

Varnan-Tech/opendirectory

Competitive intelligence orchestrator tracking companies across 8+ platforms (GitHub, Twitter, Reddit, HN, PH, YC Jobs) with heat scores and AI briefings.

Varnan-Tech/opendirectory

Give it your product URL or description. It finds your top 5 competitors, runs three-track PR research across all of them (editorial, podcasts, communities), identifies which channels appear most frequently, looks up the journalist or host for each, and returns a tiered outreach list with story angles and ready-to-send cold pitch drafts tailored to your product. Use when asked to find PR opportunities, discover where competitors got featured, build a media outreach list, find which journalists cover my space, or get pitch templates for press coverage.

Varnan-Tech/opendirectory

Generate high-converting, deep-dive growth case studies in MDX format. Use this skill when asked to write a case study or blog post about a company's growth, tech stack, or product-led strategy. It handles the full pipeline (researching the company via Tavily, generating a 16:9 cover image, quality checking the draft, uploading assets to cloud storage, and pushing directly to the target repository).

Varnan-Tech/opendirectory

Scans your project for outdated npm, pip, Cargo, Go, or Ruby packages. Runs a CVE security audit. Fetches changelogs, summarizes breaking changes with Gemini, and opens one PR per risk group (patch, minor, major). Includes Diagnosis Mode for install conflicts. Use when asked to update dependencies, check for outdated packages, open dependency PRs, scan for package updates, audit for CVEs, or flag breaking changes in upgrades. Trigger when a user says "check for outdated packages", "update my dependencies", "open PRs for dependency updates", "scan for CVEs", or "which packages need upgrading".

Varnan-Tech/opendirectory

Generates and updates README.md and API reference docs by reading your codebase's functions, routes, types, schemas, and architecture. Uses graphify to build a knowledge graph first, then writes accurate docs from it. Use when asked to write docs, generate a README, document an API, update stale docs, create an API reference from code, add an architecture section, or document a project in any language. Trigger when a user says their docs are missing, outdated, or wants to document their codebase without writing it manually.

Varnan-Tech/opendirectory

Evaluates expired domain candidates against a target niche, scores them by topical relevance, historical activity level, and history cleanliness, then outputs a ranked shortlist with explainable reasoning and risk flags.

Varnan-Tech/opendirectory

Brutally honest developer-experience audit for a GitHub repo. Scores 10 DX dimensions (time-to-first-success, README clarity, visual proof, install, quick-start, docs, examples, community, trust, marketing), writes a shareable roast in the requested tone (brutal/honest/kind), produces a prioritized action plan ranked by impact × effort, and sketches an ideal README. Trigger when user says "roast my repo", "audit my README", "dx audit", "developer experience review", "score my GitHub project", "before launch checklist", or "make my repo shareable".

Varnan-Tech/opendirectory

Drafts and designs a complete HTML email newsletter from a topic or content brief. Outputs paste-ready HTML for Loops, Mailchimp, Beehiiv, Resend, or any standard email platform. Includes subject line options and plain-text fallback. Trigger when a user says "write a newsletter", "draft an email newsletter", "create an HTML email", "design an email for my subscribers", or "write a newsletter about [topic]".

Varnan-Tech/opendirectory

Takes a GitHub PR URL or the current branch and writes a plain-English explanation of what it does and why, then posts it as a PR comment. Use when asked to explain a PR, summarize a pull request, write a plain-English description of a PR, add a summary comment to a PR, or understand what a PR changes. Trigger when a user says "explain this PR", "summarize this pull request", "what does this PR do", "add a comment explaining the PR", or shares a GitHub PR URL and asks what it does.

Varnan-Tech/opendirectory

Audit how often LLMs recommend your brand vs competitors and generate a GEO action plan.

Varnan-Tech/opendirectory

Takes a competitor's public GitHub repo URL, fetches their open issues via the GitHub REST API, filters noise locally, clusters issues into 6 demand categories, computes a demand score per issue and per cluster, and outputs a ranked demand gap report with a GTM messaging brief. Use when asked to scan a competitor's GitHub issues, find what their users are begging for, turn GitHub complaints into product positioning, identify competitor feature gaps, or generate messaging from real user demand. Trigger when a user says "scan competitor issues", "what are users asking for on X repo", "find demand gaps in Y", "turn GitHub issues into messaging", or "what should I build based on competitor complaints".

Varnan-Tech/opendirectory

Find recurring confusion in your repo's GitHub Discussions, rank it by urgency, and draft the actual docs fixes and content angles — with verbatim community quotes and source links as evidence.

Varnan-Tech/opendirectory

SEO keyword research workflow for blog generation using Google Trends data. Use when writing blog posts, planning content calendars, or optimizing articles for search engines. Finds breakout keywords, builds content structure, and generates SEO-optimized blog outlines targeting tech and developer audiences.

Varnan-Tech/opendirectory

Generates a professionally designed case study PDF for B2B SaaS sales and marketing. Supports 7 page layouts, 9 style presets, 1-4 page output. Trigger when user says "create a case study", "write a customer story", "make a case study PDF", "design a success story", "turn this customer win into a PDF".

Varnan-Tech/opendirectory

Generates data visualization charts (bar, line, area, pie, doughnut, scatter, radar, treemap) as PNG using Apache ECharts v6. 1080×1080px default, 5 style presets, highlight annotations. Trigger when user says "create a chart", "visualize data", "make a bar chart", "line graph", "pie chart", "data visualization", "chart this data", "plot", "graph", or "visualize these numbers".

Varnan-Tech/opendirectory

Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities.

相關技能