內容
用 HTML 和 Opus 5.5 做一支動態圖像影片
先寫 beat sheet,讓 Opus 5.5 透過 Felo API 寫一個確定性的 HTML 場景,再用 Playwright 逐格擷取、ffmpeg 編碼成 MP4。可以在 WorkBuddy、帶程式碼執行的 ChatGPT、Codex、Pi Agent 或 Claude Code 裡跑。
複製 prompt
把方括號裡的欄位替換掉,然後貼到 Claude Code、Codex 或任何能存取終端機的 AI。它會安裝 Felo skill、在花額度前詢問,並執行這套工作流程。
製作一支關於[想法]的 [時長] 秒 [直式 / 橫式] 影片。 風格:[風格]。受眾:[受眾]。音訊:[無 / ./audio.mp3 的授權音樂 / TTS]。 你在一個有終端機、可執行 [WorkBuddy / 帶程式碼執行的 ChatGPT / Codex / Pi Agent / Claude Code] 的資料夾中作業。如果你無法執行 Node 和無頭瀏覽器,停下來告訴我,不要假裝已經算圖。 依序執行: 1. 寫一個 3–6 個節拍的故事板,每個節拍有明確的開始和結束秒數。 2. HTML 步驟:透過 Felo API 呼叫 Claude Opus 5.5。使用模型 ID claude-opus-5-5 和我的 FELO_API_KEY。不要向我要 Claude 訂閱或另一個 Anthropic 金鑰。 3. 讓 Opus 提供一個單一檔案、自包含的 HTML:設定 window.DURATION 並暴露 async window.seek(t)。不允許外部資源、CDN、Date.now()、requestAnimationFrame()、CSS 轉場或計時器。所有隨機值都要設種子,保證同一個 t 永遠畫出同一幀。 4. 在 t = 0、25%、50%、75% 和最後一幀預覽。算全部幀前檢查溢出、裁切、字型和安全區。 5. 用 Playwright 算每一幀,用 ffmpeg 編碼為 H.264;如果有音軌,再混流音訊。 6. 展示首幀、尾幀和最終檔案路徑。不要發布。 規則: - 消耗額度前,先展示 Felo API 呼叫和預估 token 用量。 - 只使用我提供的文案,或明確標示為佔位文案。 - 不要使用受著作權保護的素材、音樂、標誌或字型。只用系統字型和生成的圖形。 - 除非我要求長片,最終影片控制在 30 秒以內。 - 把所有內容寫入 ./video/,並總結你執行的命令。
複製的內容也包含安裝步驟和安全規則。
你會得到什麼
一次執行的範例輸出,不是即時生成。
算圖報告
場景:video/scene.html(18 KB) 長度:12 秒 · 1080x1920 · 30fps · 360 幀 檢查:window.DURATION = 12 · seek(t) 存在 · 無 Date.now / rAF / 計時器
預覽:preview-0ms.png · preview-3000ms.png · preview-6000ms.png · preview-9000ms.png 編碼:video/video.mp4(H.264,360 幀) 音訊:video/final.mp4(AAC,12.0 秒)
實際呼叫
給開發者:同一套工作流程的可複製程式碼,依執行順序排列。
展開實際 API 呼叫
先寫 beat sheet
先固定長度和 3-6 個 beat,每個 beat 寫開始/結束時間、螢幕文案和主要視覺變化。
用 Opus 5.5 生成 HTML 場景
用 Anthropic SDK 指向 Felo 的 base URL,模型
claude-opus-5-5,要求只回傳 HTML,帶window.DURATION和async seek(t)。generate-scene.ts import { mkdir, writeFile } from "node:fs/promises"; import Anthropic from "@anthropic-ai/sdk"; const SYSTEM = `You write a single, self-contained HTML video scene. Requirements: - 1080x1920 (or 1920x1080), no external assets, system fonts only. - set window.DURATION = <seconds>. - expose async window.seek(t) that draws the frame for time t. - no Date.now(), no requestAnimationFrame(), no CSS transitions, no timers. - seed every random value; the same t must always produce the same frame. - return only the HTML file, no markdown fences.`; const brief = `12-second vertical motion graphic about [idea]. Beats: [0-3s hook], [3-7s point], [7-10s proof], [10-12s CTA]. Palette: [colors]. Type: system sans. Keep text inside a 64px safe area.`; const anthropic = new Anthropic({ apiKey: process.env.FELO_API_KEY, baseURL: "https://openapi.felo.ai/api", }); const message = await anthropic.messages.create({ model: "claude-opus-5-5", max_tokens: 32000, system: SYSTEM, messages: [{ role: "user", content: brief }], }); const raw = message.content .filter((block) => block.type === "text") .map((block) => block.text) .join(""); const html = raw.replace(/^```(?:html)?\s*/i, "").replace(/```\s*$/, "").trim(); await mkdir("video", { recursive: true }); await writeFile("video/scene.html", html); console.log(`scene.html: ${html.length} bytes`);範例輸出
scene.html: 18432 bytes
檢查確定性並預覽關鍵格
在 t=0、25%、50%、75% 和最後一格截圖,檢查文字溢出、字型和安全區。
preview.ts import { chromium } from "playwright"; import { pathToFileURL } from "node:url"; const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1080, height: 1920 }, deviceScaleFactor: 1, }); await page.goto(pathToFileURL("video/scene.html").href); await page.waitForFunction(() => typeof (window as any).seek === "function"); const duration = await page.evaluate(() => (window as any).DURATION as number); for (const t of [0, duration * 0.25, duration * 0.5, duration * 0.75]) { await page.evaluate(async (time) => { await (window as any).seek(time); }, t); await page.screenshot({ path: `video/preview-${Math.round(t * 1000)}ms.png` }); } await browser.close(); console.log(`duration ${duration}s`);範例輸出
duration 12s video/preview-0ms.png video/preview-3000ms.png video/preview-6000ms.png video/preview-9000ms.png
逐格渲染並編碼
依
t = frame / fps呼叫seek(t)截圖,再用 ffmpeg 編碼 H.264。render.ts import { spawn } from "node:child_process"; import { mkdir, rm } from "node:fs/promises"; import { pathToFileURL } from "node:url"; import { chromium } from "playwright"; const FPS = 30; const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1080, height: 1920 }, deviceScaleFactor: 1, }); await page.goto(pathToFileURL("video/scene.html").href); await page.waitForFunction(() => typeof (window as any).seek === "function"); const duration = await page.evaluate(() => (window as any).DURATION as number); const frames = Math.ceil(duration * FPS); await rm("video/frames", { recursive: true, force: true }); await mkdir("video/frames", { recursive: true }); for (let frame = 0; frame < frames; frame++) { await page.evaluate(async (time) => { await (window as any).seek(time); }, frame / FPS); await page.screenshot({ path: `video/frames/${String(frame).padStart(5, "0")}.png`, }); } await browser.close(); const ffmpeg = spawn("ffmpeg", [ "-y", "-framerate", String(FPS), "-i", "video/frames/%05d.png", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "video/video.mp4", ], { stdio: "inherit" }); await new Promise<void>((resolve, reject) => { ffmpeg.on("exit", (code) => { if (code === 0) resolve(); else reject(new Error(`ffmpeg exited with ${code}`)); }); }); console.log(`${frames} frames -> video/video.mp4`);範例輸出
360 frames -> video/video.mp4
mux 音訊並完整看一遍
用
-c:v copy加入授權音軌,檢查長度和音量,完整看一遍。mux.sh ffmpeg -i video/video.mp4 -i video/audio.wav \ -c:v copy -c:a aac -b:a 192k -shortest \ video/final.mp4 ffprobe -v error -show_entries format=duration \ -of default=nw=1:nk=1 video/final.mp4範例輸出
12.000000
用同一模型迭代
只描述出錯的影格和想要的修改,帶上目前 HTML 讓 Opus 改,先重跑預覽再重渲染。
包含什麼
- 先寫好的分鏡和時間軸
- 一段由 Opus 5.5 生成的確定性 HTML 場景
- Playwright 逐格渲染腳本和 ffmpeg 編碼步驟
- 帶可選音軌的最終 MP4
- 逐格 review 的修正循環
不做什麼
- Opus 5.5 輸出的是程式碼,不是影片格;真正擷取影格的是瀏覽器渲染器
- 不需要 Claude 訂閱,但 Felo 模型按用量計費
- 執行環境仍需 Node、Playwright Chromium 和 ffmpeg
- 不允許外部素材和 CDN,所有視覺必須來自 HTML/CSS/SVG/Canvas 或本地檔案
- 長影片需要拆場景;這不是即時錄影
- 旁白和音樂是另外的步驟,工作流程只 mux 你提供的音訊
常見問題
Opus 5.5 會直接生成影片嗎?
不會。它輸出文字和程式碼,寫出 HTML 場景;瀏覽器和 ffmpeg 再把場景變成影格和 MP4。
需要 Claude 訂閱嗎?
不需要。用 Felo Key 透過 Felo API 呼叫 `claude-opus-5-5`,按用量計費,不需要另外買 Claude 訂閱。
哪些 agent 能跑?
WorkBuddy、帶程式碼執行的 ChatGPT、Codex、Pi Agent、Claude Code,或任何能執行 Node 和 headless 瀏覽器的 agent。
為什麼場景需要 seek(t)?
擷取影格不是即時播放。渲染器可能在擷完第 399 格很久之後才擷第 400 格,所以每一格都必須是 t 的純函式。
可以改用 HyperFrames 或 Remotion 嗎?
可以。更長的合成或更結構化的專案適合它們;這套配方保留單一 HTML 檔,方便檢查模型呼叫和渲染迴圈。
12 秒影片要花多少?
取決於 prompt、輸出 token 和迭代次數。Felo 模型按 token 計費;長渲染前先看即時價格。
可以用自己的音樂和旁白嗎?
可以,前提是你有權利。先渲染影片,再用 ffmpeg mux 授權音軌或你自己的 TTS;工作流程不生成旁白。
參考資料
這套工作流程參考的教學和專案。
相關工作流程
改成你的版本
同一套配方,依你實際的交付方式調整。
改用 HyperFrames 或 Remotion 專案
更長或更結構化的合成用這些框架;模型呼叫不變,只換渲染指令。
做方形社群版本
保留 beat sheet,改成 1080x1080,調整字級和安全區。
一次生成三個變體
同一份 brief 生成三個不同配色/節奏的場景,先預覽再只渲染最好的一個。
繼續深入
這套配方背後的參考頁面,以及下一步可以看什麼。