vid-product-launch
製品説明とローンチコンテキストから、ナラティブなMP4形式の製品ローンチ動画を生成します。 パイプライン: HTML/CSSアニメーション → ヘッドレスChromium(Playwright、フレームごと) → FFmpeg(H.264 MP4)。 Reactなし。AI動画APIなし。Pythonなし。Playwright + FFmpeg以外のランタイムコストはゼロ。
重要なルール(生成前に毎回読むこと)
-
タグラインは必須です。スキップしないでください。 これは4〜6語で製品の全ての約束を表します。ユーザーが提供しなかった場合は、説明から導き出してください。明確で能動的な表現にしてください。「[ここにタグラインを入力]」と書いたり、空白のままにしないでください。
-
見せ場(リビール)は、それまでに十分な緊張感を高めてから訪れる必要があります。 ティーザーとビルドのセクションは、緊張感を生み出すために存在します。製品名が動画の最初の20%に登場した場合、ナラティブは崩壊します。リビールセクションの前に製品名を決して明かさないでください。
-
製品名のフォントサイズ: 16:9の場合は最小120px、9:16の場合は最小80px。 リビールの瞬間、製品名は最も大きな要素でなければなりません。支配的でなければ、リビールは失敗します。
-
証明の統計は1つだけ。リストにしないこと。 ユーザーが複数の統計を提供した場合は、最も強力なものを選んでください。5つの数字のリストはインパクトを損なわせます。1つの特大の数字がインパクトを生み出します。
-
window.renderFrame(t)を使用してください — シーンの遷移にCSS@keyframesは使用しないでください。 Chromiumでの後方シークでは、CSSのcurrentTimeシークは暗黙的に無視されます。renderFrame関数は、ミリ秒から直接要素のスタイルを計算します。Playwrightはフレームごとにこれを1回呼び出します。 -
どの要素にも
animation-delayを使用しないでください。 renderFrameでは必要ありません。もしanimation-delayを書いたら、アーキテクチャを間違えています。すぐにやめてください。 -
window.__videoReady = trueはdocument.fonts.ready.then(...)の中でのみ設定してください。 同期的に設定しないでください。 -
window.__stopPreview()を公開してください。 rAFプレビューループは、Playwrightのevaluate/screenshot呼び出しと競合します。キャプチャスクリプトは、フレームループの前に__stopPreview()を呼び出します。 -
シーン境界のチェックには
t < startMsを使用してください(t <= startMsではありません)。 フレーム0でt <= 0とすると、シーン1が黒くなります。 -
Body = 正確なピクセル寸法。 幅と高さは整数です。
%、vw/vh、remは使用しないでください。 -
HTMLを生成する前に、必ず
references/scene-library.mdとreferences/style-presets.mdを読んでください。 -
HTMLをチャットにダンプしないでください。 ファイルに保存し、概要のみを表示してください。
-
フィルムグレインのキャンバスは必ず 240×135 にしてください。W×H ではありません。 キャンバス要素に
width="240" height="135"を設定し、style="width:[W]px;height:[H]px"で拡大してください。1920×1080のフル解像度グレインは64倍遅くなり(フレームあたり8MBのImageData)、1800フレームのエクスポートに何時間もかかります。 -
ティーザーセクションは、トーンに関係なく、必ず暗く(#000または黒に近い色)してください。 ティーザーセクションの白い背景は、製品ローンチ動画ではなく、デモスライドのように見えます。暗いものから明るいものへのナラティブアーク(暗いティーザー → 暗いビルド → 製品リビール)が、ローンチ動画にドラマを生み出します。
minimalやenergeticのプリセットでも、tease-problemやtension-buildのシーンにはbackground: #000を使用してください。 -
ティーザーの各単語は、200px以上のフォントサイズで、それぞれ独立したビートとして表示する必要があります。 単語を1行に並べるだけでは迫力が足りません。各問題単語(例:「調査。」「執筆。」「営業。」「繰り返し。」)は、
font-size: 200px; font-weight: 900で中央揃え、1回につき1500〜1800msのスクリーンタイムを与えてください。scene-library.mdのtease-wordsパターンを参照してください。 -
ビルドセクションでは、問題を説明するコンテンツを表示する必要があります — パーティクルだけではありません。
terminal-cardパターンを使用してください。タイプライターアニメーションで実行されている手作業を示す3つのカードが順番に表示されます。カードはborder: 1px solid rgba(255,255,255,0.07)、青いドットのアクセント、等幅フォントです。20秒間パーティクルだけだと、空っぽのスクリーンタイムになります。 -
暗いシーンにはドットグリッドのCSS背景を追加してください。 ビルド、リビール、プルーフ、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日付文字列(カウントダウンカードシーンを有効にします) |
| tone | cinematic | cinematic / energetic / minimal / emotional |
| duration | 60 | 30 / 60 / 90(秒) |
| aspect_ratio | 16:9 | 16:9(1920×1080) / 9:16(1080×1920) |
| letterbox | false | シネマスコープ2.35:1の黒帯 — 16:9のみ |
| music | — | オーディオファイルへのパス(mp3/m4a/wav) |
| fps | 30 | フレームレート(24、30、または60) |
product_name または product_description が欠落している場合は、正確に次のように質問してください:
「ローンチ動画を作成するには、2つの情報が必要です:
- 製品名 — 製品は何と呼ばれていますか?
- 製品説明 — 2〜3文: 何をするのか、誰のためのものか、主な利点は何ですか?
オプション: タグライン(4〜6語)、プルーフスタット(1つの数字)、CTA(URLまたはアクションフレーズ)、トーン(cinematic / energetic / minimal / emotional)、時間(30 / 60 / 90秒)。」
両方が揃っている場合は → すぐにステップ2に進みます。
ステップ 2: 内部アーキテクチャ(ユーザーには決して表示しない)
1. 不足しているパラメータを導出する:
- タグライン(提供されていない場合): product_descriptionから1つ作成します。4〜6語。能動態。不要な言葉は入れない(「○○の未来…」、「○○のご紹介…」)。最もシャープな約束を導き出します。
- problem_statement(提供されていない場合): product_descriptionから推測します。課題を述べる1文。製品名には決して触れません。聴衆が共感してうなずくように書きます。
- proof_stat(提供されていない場合): product_descriptionから推測します。統計がない場合は、信頼できる規模を示す指標(「500以上のチームに信頼されています」、「数日から数分へ」)を使用します。
- cta(提供されていない場合): product_nameから推測可能であれば製品ドメインを使用し、そうでなければアクションフレーズとして「詳細を見る」を使用します。
2. duration からセクションタイミングを計算する:
| セクション | 30秒 | 60秒 | 90秒 | 開始式 |
|---|---|---|---|---|
| Tease | 0–5秒 | 0–10秒 | 0–15秒 | 0ms |
| Build | 5–12秒 | 10–30秒 | 15–40秒 | Tease終了 |
| Reveal | 12–20秒 | 30–45秒 | 40–60秒 | Build終了 |
| Proof | 20–25秒 | 45–55秒 | 60–75秒 | Reveal終了 |
| CTA | 25–30秒 | 55–60秒 | 75–90秒 | Proof終了 |
すべての境界をミリ秒に変換します。定数に割り当てます:
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. セクションごとにシーンを選択する:
- Tease: 常に
blackout-opener(1500ms)で始まります。次にtease-words(各問題キーワードを200px以上の独自の1600〜1800msビートとして)。launch_dateが提供されている場合は、オプションでティーザーの最後にcountdown-cardを追加します。 - Build:
terminal-cardシーケンス(手動タスクが入力されている3つのカード)+ オプションのクロージングアクセントライン。duration ≥ 30秒の場合は3つのカードすべて。duration = 30秒の場合は2つのカードを使用。5秒以上、裸のパーティクルのみを使用しないでください — 画面には問題を説明するコンテンツが表示されている必要があります。 - Reveal:
reveal-hero— このセクションでは常に最初に配置。duration ≥ 60秒の場合は、オプションでtagline-cardを続けます。 - Proof: 数値統計が利用可能な場合は
proof-stat。それ以外の場合はfeature-bullet。 - CTA:
cta-card— 常に、必ず最後。
典型的なシーン数: 30秒 = 4シーン、60秒 = 6〜7シーン、90秒 = 7〜8シーン。
4. シーンごとのタイミングを計算する:
各セクション内で、時間をそのセクションのシーン間で均等に分割します。
例外: blackout-opener は常にTeaseの最初の1500msを正確に取得します。
5. ピクセル寸法を決定する:
16:9→ W=1920, H=10809: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: フィルムグレイン=ON(キャンバス 240×135、不透明度 0.025)、ビネット=ON、ライトリーク=ON(暖かいゴールド)、ダークシーンでドットグリッド=ONenergetic: フィルムグレイン=OFF、ビネット=OFF、ドットグリッド=ON、リビール時の白いフラッシュminimal: フィルムグレイン=ON(キャンバス 240×135、不透明度 0.022)、ビネット=ON(控えめ、0.5 radial)、ドットグリッド=ON、ライトリークなし — アクセントカラー = #4B9FFF(エレクトリックブルー)emotional: フィルムグレイン=ON(キャンバス 240×135、不透明度 0.018、暖色系のブレンド)、ビネット=ON(ソフト)、ライトリーク=ON(銅のような暖かさ)、ドットグリッド=OFF
すべてのプリセット: ティーザーとビルドのシーンは、プリセットに関係なく、常に暗い背景(#000または黒に近い色)を使用します。
8. HTMLに window.__sfxTimeline を埋め込む(音楽が提供されていない場合でも、常に):
エクスポートスクリプトはこの配列を読み取り、各SFXタイプをFFmpegの aevalsrc/anoisesrc でネイティブに合成し、adelay を使用して正確なミリ秒オフセットでイベントを配置します。外部オーディオファイルは不要です。
SFXタイプリファレンス:
| タイプ | サウンド | 継続時間 | メモ |
|---|---|---|---|
word-hit | サブパンチ(50Hz)+ 過渡的なクリック(2.2kHz)+ ノイズバースト | 180ms | ティーザーの単語ごとに1つ — amixによる3レイヤー |
type-sequence | 15Hzのメカニカルキーボードクリック(sin^30パルスエンベロープ) | 1.9s | カードごとに1つ — TYPE_DUR全体で実行 |
whoosh | 2バンドノイズスイープ(1.1kHzボディ + 4–8kHzエアー) | 700ms | BUILD_CLOSE 遷移時 |
tension-riser | 低音ランブル(250Hz)+ t/2.8ランプで成長するサブトーン | 2.9s | BUILD_CLOSE + 700ms で開始 — REVEAL_START でピーク |
reveal-boom | サブ(45Hz)+ ボディ(90Hz)+ シャマー(4.5–11kHz)+ 85msエコー | 900ms | 正確に REVEAL_START で — 決定的な一撃 |
counter-tick | 倍音クリック(880Hz + 440Hz + 1760Hz + 2640Hz) | 80ms | 3ビート — デクレッシェンド 0.32 → 0.26 → 0.20 |
cta-chime | Aメジャーコード(440 + 554 + 659 + 880Hz)+ エコーベルシャマー | 1.2s | 正確に CTA_START で |
SFXタイミングマップ — セクション定数から ms 値を計算し、ハードコードしないでください:
window.__sfxTimeline = [
// ── TEASE: 単語ビートごとに1つの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 }, // 最後の単語はより大きく
// ── BUILD: カードごとにtype-sequence(1.9s、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 },
// ── TRANSITION: whooshの後、2.9秒のライザーがREVEAL_STARTでピークに ───────────
{ ms: BUILD_CLOSE, sfx: 'whoosh', vol: 0.50 },
{ ms: BUILD_CLOSE + 700, sfx: 'tension-riser', vol: 0.35 },
// ── REVEAL: 映画的な一撃 ─────────────────────────────────────────────
{ ms: REVEAL_START, sfx: 'reveal-boom', vol: 0.88 },
// ── PROOF: 倍音のティック、デクレッシェンド ───────────────────────────────────
{ 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>
<!-- シネマティックオーバーレイ要素 — すべてのシーンの上にレンダリングされる -->
[if フィルムグレイン: ON]
<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>
[end if]
[if ビネット: ON]
<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>
[end if]
[if ライトリーク: ON]
<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>
[end if]
[if レターボックス: ON]
<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>
[end if]
<!-- シーン -->
[scene-library.mdテンプレートからのシーンHTML、選択されたシーンごとに1つ]
<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 =
…
[翻訳のため省略]