内容
用 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、在花额度前询问,并执行这套 workflow。
制作一条关于[想法]的 [时长] 秒 [竖版 / 横版] 视频。 风格:[风格]。受众:[受众]。音频:[无 / ./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 秒)
实际调用
给开发者:同一套 workflow 的可复制代码,按执行顺序排列。
展开实际 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 或本地文件
- 长视频需要拆场景;这不是实时录屏
- 旁白和音乐是单独步骤,workflow 只 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;workflow 不生成旁白。
参考资料
这套 workflow 参考的教程和项目。
相关 workflow
改成你的版本
同一套配方,按你实际的交付方式调整。
改用 HyperFrames 或 Remotion 项目
更长或更结构化的合成用这些框架;模型调用不变,只换渲染命令。
做方形社媒版本
保留 beat sheet,改成 1080x1080,调整字号和安全区。
一次生成三个变体
同一份 brief 生成三个不同配色/节奏的场景,先预览再只渲染最好的一个。
继续深入
这套配方背后的参考页面,以及下一步可以看什么。