高能量解說片
一個 12–20 秒的 hype 短片,效果如同作品集展示。每半秒就有一個元素隨著節拍落下,但從不感覺是隨機剪接,因為一個形狀引導視線貫穿每個轉場:火花變成線條,線條淹沒畫面成為下一個背景,句號脫離變成下一個場景圍繞的球體。
你將它寫成一個 HTML 頁面,根據時間值渲染任意影格,然後渲染成 MP4,並帶有運動模糊和合成配樂。
步驟 1:獲取需求摘要(一次訊息,然後開始)
在一次訊息中詢問所有事項,然後獨立工作。只需要產品資訊;其他欄位如果空白,請自行決定並告知你的選擇。
- 產品:名稱,以及用幾句話說明它的用途。提供網站 URL 更佳。
- 觀眾必須記住的一件事(例如:「輸入一個想法,獲得一個即時應用程式」)。
- 品牌:顏色、字型、Logo 檔案,或「使用該網站」。如果都沒有,使用下方預設色板。
- 格式:16:9(1920×1080)或 9:16(1080×1920)。預設 16:9。
- 長度:此風格的預設長度如下所示。
- 音樂:使用者自己的曲目(一個檔案路徑),或讓你合成。預設:合成。
- 方向(可選):他們想要的場景、標語、行動呼籲,或任何需要避免的事項。
如果他們提供了 URL,請先讀取(網路擷取)產品的實際用語、功能、顏色和字型名稱,再開始撰寫。UI 標籤和標語請使用他們的實際用語。
切勿捏造事實。 不許有虛構的客戶數量、營收、評分、見證或引言。數字只有當使用者或網站陳述時才可使用;否則使用相對長條圖、進度條和佔位用的骨架線條。UI 內部的範例內容(提示詞、檔名、專案名稱、收據上的價格)可以接受,且應合理可信;但關於產品本身的聲明(統計數據、使用者數量、評分、如「免費」等優惠)則不被允許。行動呼籲僅使用使用者或網站提供的 URL,如果沒有,則以名稱和標語結束。
Logo。 僅在使用者提供檔案,或這是他們自己的產品且他們要求時,才使用 Logo。否則,以品牌字型將名稱顯示為文字。第三方 Logo(整合合作)僅在提供時使用;否則使用中性圓角方塊加上名稱。
此風格的預設長度:15 秒(128 BPM,8 小節)。範圍 12–20 秒。
步驟 2:規劃場景,然後建構
結構(6–8 個場景,每個 1.5–3 秒,在 120–140 BPM 的節奏網格上)
從以下場景類型中選擇,並將產品映射到上面。首先撰寫節拍表(場景、在節拍網格上的開始時間、帶入什麼、帶出什麼、音效)。
- 火花開場(暗色)。近乎黑色的畫布,帶有微弱的網格。一個微小的強調標記出現,脈動,伸展成一條橫跨畫面的細水平線,帶有一個小型等寬標籤(
產品 / 上市 — 2026),抖動一次,然後線條垂直擴張成一個全螢幕的強調色 flood。這個 flood 就是下一個背景。 - 品牌重擊。 產品名稱使用厚重的無襯線字體,全大寫,巨大,字母透過連續的 1/16 拍遮罩顯現,上方有一個小型等寬引導文字(
INTRODUCING — 2026),下方有一行斜體襯線文字(它是什麼,2–4 個字)。字重可以從細體 morph 到黑體。句號或字母的點脫離並變大,成為下一個場景的起始形狀。 - 動態三重奏。 價值主張以三個簡短單詞或短語呈現,每個兩個拍子(手寫字則三個拍子,以便閱讀),每個都有自己的處理方式和背景,在節拍上硬切換(場景內的切換是節奏的一部分,因此不計為剪接):深色配米色;強調色配黑色,單詞以輪廓圖案重複填滿整個畫面;白色配電光藍,位於取景器角落括號內,帶有小型
1920 × 1080標籤;最後一個單詞以手寫字體自行繪製。每個單詞以快速模糊/滑動進入,並帶有輕微 overshoot。 - 招牌視覺。 一個「他們怎麼做到的」瞬間,象徵產品:一個從中心漣漪擴散的 3D 立方體場(在畫布上用你自己的透視投影繪製,根據高度著色使用色板)、一個凝聚成環狀並向外爆發的生成粒子流,或一個在形狀之間 morph 的液體 blob。選擇符合產品概念的(積木 → 立方體,數據/AI → 粒子,靈活性 → 液體)。播放時攝影機緩慢傾斜或環繞。
- 產品微觀時刻。 產品的一個真實互動,顯示為浮動 UI 卡片,稍微 3D 傾斜,背後疊放兩張幽靈卡片:游標撥動開關、按下主要按鈕、進度條以強調色填滿並顯示百分比、按鈕變為
Done ✓、右上角滑入一個 Toast。使用產品的實際標籤。 - 證明(僅當有真實數字時)。 一個大的計數器(
+312%風格,數字旋轉時模糊)、一個甜甜圈環、交錯上升的長條圖(其中一條為強調色)、一條在它們上方繪製的線與一個工具提示。無真實數字 → 跳過此場景,或將產品的真實能力作為列表猛烈呈現。 - 液體/morph 橋接。 前一個場景的形狀坍縮成一個點,該點在暗色背景上變成一個脈衝環,然後變成 Logo 中的點。
- 結束卡片(維持 2 秒)。Logo 或名稱加上強調色點,斜體襯線標語,一行等寬文字逐個字母輸入(如果有的話是實際 URL 或行動呼籲,例如
PRODUCT.COM;如果沒有,則從需求摘要中取一行簡短文字),一條細線。
並非每個產品都需要所有場景;6 個強有力的場景勝過 8 個薄弱的場景。每個場景保持一個想法。
讓它看起來昂貴的規則
- 轉場是物體,不是剪接。 每個場景透過一個形狀(flood、由一個增長的 bar 擦拭、一個成長為下一個背景的點、一個穿過攝影機的單詞)交給下一個場景。場景間的硬剪接僅允許在 downbeat 上,最多兩次。
- 所有東西都落在網格上。 重大變化在節拍上,字體在 1/8 或 1/16 音符上。為撞擊感提供輕微 overshoot(
z≈ 0.6–0.7),然後穩定下來。 - 每個場景都有 HUD 框架: 細小的角落括號,右上角
SCENE 02 / 07,左下角一個小型等寬類別標籤(KINETIC TYPE、PRODUCT、DATA),右下角一個帶有紅點的運行時間碼。小型字體(1080p 下 12–14 px),透明度約 50%;應感覺像攝影機的觀景窗。 - 紋理: 每個純色背景上有柔和的徑向暗角,非常微弱的底片顆粒(預先在載入時生成幾個帶種子的顆粒圖塊並循環它們),以及真實的運動模糊。
- 字型: 重擊用厚重的無襯線字體(如 Inter Tight 900 或 Archivo Black),人性化線條用斜體襯線字體(Instrument Serif italic),HUD 和說明文字用等寬字體(JetBrains Mono 或 Geist Mono)。如果品牌有字型,他們的字型用於名稱,並保留等寬字體用於 HUD。
- 預設色板: 墨水色
#0B0B10、強調色#FF4F1A、米色#EDEBE4、電光藍#2F47FF、白色。品牌的主色取代強調色,其第二色取代藍色;單色品牌則使用其顏色的深色調作為第二色。 - 禁止事項: 範本風格的外觀、萬物發光、表情符號、一個場景中使用超過 3 種顏色(不計黑色和白色)、一眼無法閱讀的文字、沒有任何動態的場景。
音效
128 BPM(或使用者的曲目)。有衝擊力:每個節拍上放 kick、2 和 4 拍上放 clap、跟隨場景根音的低音、進入品牌重擊和結束卡片時放一個濾波 riser、每次 flood 上放一個撞擊音效(低頻轟鳴 + 噪音爆發)、每個打字單詞上放短調 blip、每次承載轉場上放一個 whoosh(帶通濾波噪音 sweep)、結束卡片上放一個柔和的 ring-out。將音樂混合在撞擊音效之下。
頁面建構方式
一個自包含的 HTML 檔案(內嵌 CSS 和 JS;圖片僅在使用者提供時使用)。使用 DOM + CSS transforms、SVG 或一個全螢幕 canvas,取最適合的方式。
頁面合約
window.VIDEO = {w, h, duration}和window.seek(t),一個純時間函數:它為時間t(秒)設定每個元素。不能有 CSS transitions 或 animations、不能有setTimeout、不能有從前一影格繼承的狀態。任何影格必須能獨立、以任何順序正確渲染。在seek內部測量佈局(例如offsetLeft、measureText)是可以的;它是確定性的。- 隨機性(顆粒、粒子、音訊中的噪音)來自帶種子的生成器,絕不使用
Math.random。 - 任何需要歷史的內容(如落後於移動物體的攝影機、模擬)在載入時一次烘焙完成:以 1/240 秒的間隔從 0 步進到
duration,儲存數值,然後讓seek透過插值讀取它們。對於循環,從循環開始時的狀態啟動模擬(參見此風格的接縫規則)。 - 循環:
seek(t)使用t % duration;跨越接縫的效果(著陸壓縮、音效尾巴)在兩端都繪製或混合。 - 如果 URL 中包含
?render:無控制項、無預覽縮放;舞台精確為w × h並位於左上角。如果不包含:舞台使用 CSS 縮放以適應視窗,並顯示預覽控制項。 - 字型:請嵌入它們。 下載 woff2 檔案(Google Fonts 或品牌的)並內嵌為 base64
@font-face,這樣渲染永遠不會無聲地後退。對於字重 morph,使用帶有範圍([email protected])的可變字型。 - 圖層: 為場景圖層提供透明背景,並讓一個舞台背景在它們後面提供底色,這樣之後的圖層就不會意外遮蓋之前的圖層。絕對定位的文字應設定
white-space: nowrap。 - 速度: 大型 CSS
filter: blur()和大範圍模糊漸層在螢幕截圖時非常慢。在載入時一次性地在 canvas 上繪製漸層和發光效果(或作為圖片),然後移動它。
輔助工具:貼上這些並在你所有的東西上建構
// 阻尼彈簧步進響應 0 -> 1。 w = 速度(8 慢,14 正常,22 快速),z = 阻尼(1 = 無 overshoot)
function spring(t, w = 14, z = 0.82) {
if (t <= 0) return 0;
if (z >= 1) return 1 - Math.exp(-w * t) * (1 + w * t);
const wd = w * Math.sqrt(1 - z * z);
return 1 - Math.exp(-z * w * t) * (Math.cos(wd * t) + (z * w / wd) * Math.sin(wd * t));
}
// 一個值多次改變目標,每個目標都彈性追蹤:track(start, [[time, value, w?, z?], ...]) -> f(t)
const track = (v0, keys) => t => { let v = v0, p = v0; for (const [tk, val, w, z] of keys) { v += (val - p) * spring(t - tk, w, z); p = val; } return v; };
// 精確的緩動關鍵影格(最適合攝影機移動):kf([[t0, v0], [t1, v1, easeFn?], ...]) -> f(t)
const kf = keys => t => { if (t <= keys[0][0]) return keys[0][1]; for (let i = 1; i < keys.length; i++) { const [a, va] = keys[i - 1], [b, vb, e = ease] = keys[i]; if (t <= b) return lerp(va, vb, e((t - a) / (b - a))); } return keys[keys.length - 1][1]; };
const clamp = (x, a = 0, b = 1) => Math.min(b, Math.max(a, x));
const ease = x => (x = clamp(x), x < .5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2); // easeInOutCubic
const lerp = (a, b, k) => a + (b - a) * k;
const seg = (t, a, b) => clamp((t - a) / (b - a)); // t 在 [a, b] 區間的 0..1 進度
const rng = s => () => (s = (s + 0x6D2B79F5) | 0, s = Math.imul(s ^ (s >>> 15), 1 | s), s ^= s + Math.imul(s ^ (s >>> 7), 61 | s), ((s ^ (s >>> 14)) >>> 0) / 4294967296);
- 打字效果:可見字串是
full.slice(0, n(t)),帶有一個從t開始閃爍的游標。計數器:格式化lerp(a, b, ease(seg(t, …)))。 - 一個攝影機是一個包裝元素,具有由
kf或track驅動的transform: translate() scale() rotate()。
音效
window.buildAudio(ctx)從時間 0 開始,在給定的任何AudioContext或OfflineAudioContext上排程整個配樂(音樂+效果),使用與視覺效果相同的時間軸數字。使用振盪器、濾波噪音(來自帶種子的緩衝區)和包絡進行合成。如果使用者提供了音樂檔案,使用fetch+decodeAudioData載入,將其節拍網格設定為其速度(測量它),然後只在其上添加效果。- 預覽播放: 在第一次播放時,將配樂一次渲染到緩衝區中,然後從任何拖曳的位置播放。時鐘驅動
seek:
let buf, ac, src, startAt = 0, offset = 0, playing = false;
async function play() {
ac ??= new AudioContext();
if (!buf) { const off = new OfflineAudioContext(2, Math.ceil(48000 * VIDEO.duration), 48000); await buildAudio(off); buf = await off.startRendering(); }
src = ac.createBufferSource(); src.buffer = buf; src.connect(ac.destination);
src.start(0, offset % VIDEO.duration); startAt = ac.currentTime; playing = true;
}
function pause() { if (!playing) return; offset += ac.currentTime - startAt; src.stop(); playing = false; }
function now() { return playing ? (offset + ac.currentTime - startAt) % VIDEO.duration : offset; }
(function loop() { seek(now()); requestAnimationFrame(loop); })(); // 拖曳器設定 offset(暫停、設定、播放)
- 將節拍表作為註解放在腳本的頂部:每個時刻一行,包含時間、發生什麼事以及使用哪種音效。
步驟 3:檢查、渲染、交付
在工作資料夾中一次性設定:npm i playwright,然後如果尚未安裝 Chromium,執行 npx playwright install chromium;ffmpeg 必須在 PATH 中。將此檔案底部的腳本儲存為 render.mjs。
- 靜止幀。
node render.mjs demo.html check.png --stills 0.5,1.2,…,每個時間點對應節拍表的一行(節拍表以可讀大小顯示,每行 3 幀)。查看它。修復任何被裁切、重疊、不可讀、不符合品牌、取景不當或空白的問題。再次檢查。 - 草稿。
node render.mjs demo.html draft.mp4 30 1 4(快速:30 fps,無運動模糊,4 個平行工作執行緒;將最後一個數字設為機器的核心數)。這是審查副本:將其與 HTML 一起發送給使用者,並在最終版本前要求修改。 - 最終版。
node render.mjs demo.html demo.mp4 60 8 8。運動模糊會將渲染時間乘以子幀數;一個 60 秒的最終版在筆電上可能需要 20–60 分鐘,所以在開始前告知這一點。