利用 HyperFrames 将 HTML 转换为视频的技术实践 ✍ James Russo🕐 2026-04-18📦 14.5 KB 🟢 已读 𝕏 文章列表 文章探讨了利用 LLM 生成 HTML 代码进而制作视频的难题与解决方案。作者介绍了 HyperFrames,这是一个通过最小化创作模型、控制 Headless Chrome 逐帧渲染来生成确定性 MP4 的系统。文章详细阐述了核心技术难点,包括使用 Seek 替代 Play 模式、利用 Chrome DevTools Protocol 的 beginFrame 控制渲染同步,以及解决事件循环阻塞和字体加载异步问题的具体工程技巧。 HTMLCSSJavaScript视频生成PuppeteerChrome DevTools渲染技术LLM应用前端工程自动化 # HTML to Video was not easy - here’s how we solved it. **作者**: James Russo **日期**: 2026-04-16T17:17:10.000Z **来源**: [https://x.com/Rames_Jusso/status/2045230663805317349](https://x.com/Rames_Jusso/status/2045230663805317349) ---  The Problem Creating video is hard, but LLMs are great at writing code specifically HTML. What if we can have LLMs write code to create a video? The pitch was clean (and exciting!). The reality was not… About a year ago we started our journey. Huge prompts, lots of back-and-forth, and plenty of hand-written glue to cover what the model missed. We added an agent to hand hold the model. It helped, but it still wasn’t production ready. Next up we tried Remotion. It’s the right shape for deterministic video (React components, great tooling, production ready rendering), but the React framework kept boxing the agent in. Outputs got safer and more repetitive the more guardrails we added. When we dropped back to plain HTML/CSS/JS, the creativity came back. That raised the real question: “Can we keep the freedom of HTML and still render a deterministic MP4?” HyperFrames is our answer: - a minimal authoring model (HTML + data-* clip attributes) - a pluggable animation runtime - a render pipeline that forces headless Chrome to produce the same pixels every run Browsers don’t want to do this. Rendering is threaded and asynchronous: images decode in the background, videos drop frames under load, animations follow the display clock. All of that is performance—and nondeterminism. Long story short, we set out to solve it and we did! However, we also wanted to ensure it worked well and grew with the models. One tell that a framework is fighting an agent is if you need the biggest model just to get working output. We started by shaping the simplest version of HyperFrames around what Gemini Flash could reliably author. From there, we ran evals across different models, tightened the skills and the runtime wherever they failed, and repeated. The goal was not to optimize for one model. It was to make the authoring model simple enough, and the agent scaffolding strong enough, that a wide range of models could produce usable compositions. We build AI models and agents for a living. The feedback loop that tightens an agent's output is the same loop that tightens HyperFrames. How We Got Here... ## The one trick: seek, don't play Every composition in HyperFrames exposes exactly one thing to the runtime: The renderer never calls play(). It calls seek(0), screenshots, seek(1/30), screenshots, seek(2/30), screenshots, until it has 300 frames for a 10-second 30fps video. Time doesn't advance on its own. Nothing is driven by requestAnimationFrame. The browser's job is to hold a fixed frame until the next one is requested. This one abstraction is what collapses two very different systems into one codebase. The studio preview runs the same window.__hf inside an iframe with a postMessage bridge for play/pause/scrub. The headless render runs the same window.__hf via Puppeteer and CDP. When a user scrubs the timeline, the preview iframe calls seek. When the renderer captures frame 147, it calls seek(147 / fps). Same code path. Same output. Animation libraries plug in through a three-method FrameAdapter: GSAP is the default because its timelines are already paused-and-seekable by design. timeline.pause() followed by timeline.totalTime(t, false) is the exact functionality we need. Lottie, CSS via WAAPI, and Three.js clocks all fit the same shape as well easily. What doesn't fit is anything that insists on owning the clock: CSS keyframe animations without a controller, video elements, most canvas libraries running their own requestAnimationFrame. For those you either wrap them in an adapter that takes the clock away, or you render them to frames offline and replay them as images. This is similar to how we handle videos which will explain more later. ## Capture: controlling Chrome frame by frame The first version of the capture loop was four lines of Puppeteer: Here are the four things that we ran into. 1. Page.captureScreenshot races the renderer. The call returns an image as soon as the compositor is willing to hand one over. That is not the same moment as "layout is done, fonts are loaded, the GSAP tween has committed its final style, and the GPU has finished painting." You get frames where text hasn't rendered yet, where an SVG fill is still the unanimated default, where a video element is showing its 300x150 default size because metadata hasn't loaded. Every one of these is a frame that renders fine the second time and wrong the first. We spent longer than we'd like to admit writing "did the frame land" heuristics: poll for fonts.ready, wait for computed styles, compare pixel hashes. The heuristics works well, although it is not the most robust. This is still the path we use on macOS and Windows, where the fully deterministic alternative isn't available. It's not how you want to run production at scale. But more on that next. 2. HeadlessExperimental.beginFrame gives you that control. It's a CDP method that runs one layout→paint→composite→screenshot cycle atomically and returns the result: One call, one frame. The compositor is paused until you ask for the next one. The response includes hasDamage, which tells you whether anything visually changed since the previous frame. There are no race conditions because there is no concurrent render pipeline still settling in the background. You seek, you call beginFrame, you get the screenshot image. Making this work requires a specific Chrome build and a specific set of flags. The binary is chrome-headless-shell, not regular Chrome. The flags are: Every one of those flags is turning off a source of async scheduling: threaded compositor, threaded scrolling, incremental image decoding, image animation resync, vsync-based surface timing. With them on, the compositor runs synchronously on the main thread and does not advance until CDP tells it to. With --deterministic-mode the time source is fixed too, so performance.now() is driven by the frameTimeTicks you pass in rather than by the system clock. Constraint worth stating: this combination works on Linux with chrome-headless-shell. On macOS and Windows we fall back to Page.captureScreenshot with the "did the frame land" heuristics, because Chrome on those platforms either crashes under --deterministic-mode or has its own issues with the flag combination. For CI and production renders we run in Docker on Linux. Local dev works anywhere, with lower fidelity on non-Linux. 3. Chrome stops advancing its event loop. When --enable-begin-frame-control is active, Chrome's main thread stops ticking on its own. No frame callbacks, no setTimeout, no microtask drain between tasks. Nothing runs until a beginFrame message arrives over CDP. Which is great for determinism during capture. It is catastrophic during page load, because document.fonts.ready is a promise that resolves on a task, and tasks don't drain if nothing's ticking. Your GSAP script loads. Your timeline registers on window.__timelines. window.__hf.seek is wired up. And then document.fonts.ready hangs forever. The fix is a warmup loop. While the page is loading, we fire a beginFrame every 33ms with noDisplayUpdates: true, which advances the event loop without producing a frame: We kill the loop once window.__hf is ready and fonts have loaded, then start real capture at a frame time past the warmup range so the compositor never sees time going backwards. It's the kind of workaround you only know to write after the first render hangs at "loading fonts" until the timeout fires. 4. Puppeteer's waitForFunction stops working. The idiom for "wait until the page is ready" in Puppeteer is page.waitForFunction(...). Under the hood, that polls via requestAnimationFrame in the injected world. rAF doesn't fire in beginFrame mode. So waitForFunction hangs for the same reason fonts.ready does, except you lose the nice Puppeteer error message and get a generic timeout once the deadline fires. The fix is to stop using waitForFunction and write the polling loop yourself with evaluate and setTimeout: Less clever. More obvious. Doesn't depend on anything running inside the page's frame loop. After those four things, the capture loop that ships today is approximately this: One seek, one beginFrame, one frame on disk. No retries. No flaky frames. Deterministic. ## The video-in-video problem Letting a browser play <video> at render time does not work. In headless mode with BeginFrame on, video decoders skip frames, fail to decode, or sit at readyState: 0 long enough to break the capture deadline. Even without BeginFrame, different machines and different codec paths produce different output on the same composition. What you see is not what you get. A <video> on a webpage is happy to drop frames and call it good. A video renderer cannot. So we took the decoding away from Chrome. Before capture starts, FFmpeg pre-extracts every <video> in the composition into numbered JPEGs at the target fps. A 5-second clip at 30fps becomes 150 files. During capture, for each active video on the current frame, we inject an <img> sibling with the right frame's bytes as a data URI and hide the original <video>: The interesting part is making the <img> look exactly like the <video> it replaced, so GSAP tweens, CSS transforms, opacity fades, and object-fit rules all keep working. We read computed styles off the original element and copy them onto the injected image: From the animation library's perspective, nothing has changed. The element is in the same place with the same styles. It just happens to show a still image that changes every frame. We don't let Chrome decode and schedule video. We hand it the exact frame we want, like a flipbook, and take a picture. Others solve the same problem differently. Remotion runs a long-running Rust compositor that decodes frames on demand and serves them over HTTP to its <OffthreadVideo> component. Replit demuxes frames in the browser with mp4box.js and decodes through WebCodecs, then paints into a <canvas>. Ours is the simplest of the three: decode everything ahead of time in FFmpeg, serve JPEGs off disk. We trade flexibility (harder to handle blob URLs, streaming sources, dynamically-set src) for a much shorter pipeline. There’s lots of improvements we can make here still, but the base works for our case great. ## The other determinism traps Controlling time and rendering gets you most of the way. It does not get you all the way. Fonts. Most compositions use Google Fonts via @import url(fonts.googleapis.com/...). That call is a coin flip at render time. The network might be fast, slow, or blocked. The font might load before or after your first frame. To get rid of the variance, we rewrite every Google Fonts @import in the compiled HTML to point at a local, base64-embedded copy of the font from @fontsource. The composition renders exactly the same, minus the network round-trip and the flakiness. Time quantization. A 30fps video has a frame every 33.3333ms. If the renderer calls seek(0.0333333) for frame 1 and seek(0.0333334) for some edge-case path that recomputes the time, we want those to be the exact same frame. So every seek, in both preview and render, runs through a quantizer: It's one line. It turns out to matter. Without it, two code paths that compute the same nominal time different ways can produce frames that differ by a pixel, and then you end up staring at a pixel-level diff wondering where it came from. Rules for the author. No Date.now() in composition code. No unseeded Math.random(). No network fetches at render time. These are part of the contract. If you violate them, you get nondeterministic output even with everything else we just built. ## What you get The same window.__hf runtime bundle runs in the studio preview (inside an iframe) and in the headless render. The renderer verifies a sha256 of the bundle against a manifest before starting, which means "what you see in the preview" is literally the same code that produced your MP4. Preview and render parity isn't hoped for. It's enforced. For longer videos, rendering gets split across N Chrome processes. Each worker renders its share of frames, and FFmpeg concatenates the per-worker MP4 chunks at the end. The one gotcha: video-heavy compositions can time out in parallel mode because Chrome can't seek multiple <video> elements simultaneously without running out of decoders. T he fix is to drop back to a single worker for video-heavy renders. Not elegant, but honest. We didn't invent any of this from nothing. GSAP's timelines are already paused-and-seekable by design, which is why the adapter is three methods. Remotion proved years ago that HTML could be a video format if you built the authoring model right. Replit and Vinlic's WebVideoCreator pioneered time virtualization and BeginFrame capture for arbitrary web content. We took a different path, a constrained authoring model with a seekable runtime contract, but the underlying techniques rest on work other people did first. ## Why we built HyperFrames We think this is how agents will make video, and we think agents should be able to communicate through video. That's why we open sourced it. We want others to help extend this to further possibilities. We welcome contributions to our adapter system. And we look forward to seeing what products are built on top of HyperFrames. To try it just run and tell your agent > “/hyperframes create me a video about XYZ”. Repo: github.com/heygen-com/hyperframes. ## 相关链接 - [James Russo](https://x.com/Rames_Jusso) - [@Rames_Jusso](https://x.com/Rames_Jusso) - [68K](https://x.com/Rames_Jusso/status/2045230663805317349/analytics) - [Apr 17](https://x.com/HeyGen/status/2044827454460871072) - [2.4M](https://x.com/HeyGen/status/2044827454460871072/analytics) - [performance.now](https://performance.now/) - [@import](https://x.com/@import) - [fonts.googleapis.com/](https://fonts.googleapis.com/) - [@import](https://x.com/@import) - [@fontsource](https://x.com/@fontsource) - [Date.now](https://date.now/) - [github.com/heygen-com/hyperframes](https://github.com/heygen-com/hyperframes) - [Upgrade to Premium](https://x.com/i/premium_sign_up) - [3:59 AM · Apr 18, 2026](https://x.com/Rames_Jusso/status/2045230663805317349) - [68.3K Views](https://x.com/Rames_Jusso/status/2045230663805317349/analytics) - [View quotes](https://x.com/Rames_Jusso/status/2045230663805317349/quotes) --- *导出时间: 2026/4/18 10:19:35* --- ## 中文翻译 # HTML 转 Video 并不容易 - 这是我们如何解决的。 **作者**: James Russo **日期**: 2026-04-16T17:17:10.000Z **来源**: [https://x.com/Rames_Jusso/status/2045230663805317349](https://x.com/Rames_Jusso/status/2045230663805317349) ---  问题所在 创作视频很难,但大语言模型(LLM)非常擅长编写代码,尤其是 HTML。如果我们能让 LLM 编写代码来生成视频会怎样? 这个切入点很简洁(也很令人兴奋!)。 但现实却并非如此…… 大约一年前,我们开始了这段旅程。巨大的提示词,大量的来回沟通,还有大量手工编写的“胶水代码”来填补模型的疏漏。我们添加了一个 Agent 来“手把手”指导模型。这有点用,但仍然无法投入生产环境。 接下来,我们尝试了 Remotion。对于确定性视频(React 组件、优秀的工具链、可投入生产的渲染)来说,它的形态是正确的,但 React 框架总是把 Agent 框得死死的。 我们添加的护栏越多,输出就越保守、越重复。当我们退回到纯 HTML/CSS/JS 时,创造力又回来了。 这就引出了真正的问题: “我们能保留 HTML 的自由度,同时还能渲染出确定性的 MP4 吗?” HyperFrames 就是我们的答案: - 一个极简的创作模型(HTML + data-* 剪辑属性) - 可插拔的动画运行时 - 一个强制无头 Chrome 每次运行都产生相同像素的渲染管道 浏览器不想做这种事。渲染是线程化且异步的:图片在后台解码,视频在高负载下丢帧,动画跟随显示时钟。所有这些都是为了性能——以及不确定性。 长话短说,我们着手解决了这个问题,而且成功了! 然而,我们也想确保它能良好运行并随模型一起成长。如果一个框架仅仅为了获得可用的输出就需要最大的模型,这就是框架在与 Agent 抗衡的迹象。 我们首先围绕 Gemini Flash 能够可靠编写的内容,构建了最简单版本的 HyperFrames。 从那里开始,我们在不同模型上运行评估,在它们失败的任何地方优化技能和运行时,然后重复这一过程。 目标不是针对某一个模型进行优化。 而是要让创作模型足够简单,Agent 支架足够强大,从而使广泛的模型都能生成可用的合成效果。 我们靠构建 AI 模型和 Agent 谋生。优化 Agent 输出的反馈循环与优化 HyperFrames 的反馈循环是一样的。 我们是如何做到的... ## 唯一的诀窍:seek(定位),而不是 play(播放) HyperFrames 中的每个合成只向运行时暴露一件事: 渲染器从不调用 `play()`。它调用 `seek(0)`,截屏,`seek(1/30)`,截屏,`seek(2/30)`,截屏,直到获得 300 帧来组成一个 10 秒 30fps 的视频。 时间不会自动前进。没有任何东西由 `requestAnimationFrame` 驱动。浏览器的工作是保持固定帧,直到请求下一帧。 正是这一抽象将两个截然不同的系统坍缩到了同一个代码库中。 工作室预览在 iframe 内运行相同的 `window.__hf`,并通过 postMessage 桥接进行播放/暂停/拖动。 无头渲染通过 Puppeteer 和 CDP 运行相同的 `window.__hf`。当用户拖动时间线时,预览 iframe 调用 seek。当渲染器捕获第 147 帧时,它调用 `seek(147 / fps)`。相同的代码路径。相同的输出。 动画库通过一个只有三个方法的 `FrameAdapter` 插入: GSAP 是默认选项,因为其时间轴设计上就是可暂停和可定位的。`timeline.pause()` 紧接着 `timeline.totalTime(t, false)` 正是我们需要的确切功能。Lottie、通过 WAAPI 的 CSS 以及 Three.js 时钟也都能轻松适应这种形态。 不适合的是任何试图独占时钟的东西:没有控制器的 CSS 关键帧动画、视频元素,以及大多数运行自己的 `requestAnimationFrame` 的 canvas 库。 对于这些,你要么将它们包装在一个剥夺时钟控制的适配器中,要么将它们离线渲染为帧并作为图像重放。这类似于我们稍后会详细解释的视频处理方式。 ## 捕获:逐帧控制 Chrome 捕获循环的第一个版本只有四行 Puppeteer 代码: 以下是我们要解决的四个问题。 1. `Page.captureScreenshot` 与渲染器存在竞态。 该调用在合成器愿意交出图像时立即返回图像。但这与“布局完成、字体加载、GSAP 补间已提交其最终样式、且 GPU 已完成绘制”不是同一时刻。 你会得到文本尚未渲染的帧、SVG 填充仍是未动画默认值的帧,或者因为元数据尚未加载而显示 300x150 默认尺寸的视频元素的帧。这些每一帧都是第二次渲染正确但第一次渲染错误的帧。 我们花了比想象中更长的时间编写“帧是否落地”的启发式算法:轮询 `fonts.ready`,等待计算样式,比较像素哈希。 这些启发式算法效果很好,尽管不是最稳健的。这仍然是我们目前在 macOS 和 Windows 上使用的路径,因为在这些平台上完全确定性的替代方案不可用。这不是你想大规模运行生产环境的方式。但更多相关内容在后面。 2. `HeadlessExperimental.beginFrame` 给了你那种控制权。 这是一个 CDP 方法,原子化地运行一个 布局→绘制→合成→截图 循环并返回结果: 一次调用,一帧。合成器会暂停,直到你请求下一帧。 响应包含 `hasDamage`,它会告诉你自上一帧以来是否有视觉变化。没有竞态条件,因为没有并发渲染管道仍在后台稳定运行。 你定位,你调用 `beginFrame`,你得到截图图像。 使其工作需要特定的 Chrome 构建版本和特定的标志集。二进制文件是 `chrome-headless-shell`,而不是常规的 Chrome。标志如下: 这些标志中的每一个都关闭了一个异步调度的来源:线程合成器、线程滚动、增量图像解码、图像动画重新同步、基于 vsync 的表面计时。 开启它们后,合成器在主线程上同步运行,并且在 CDP 通知之前不会推进。配合 `--deterministic-mode`,时间源也是固定的,因此 `performance.now()` 由你传入的 `frameTimeTicks` 驱动,而不是由系统时钟驱动。 值得说明的约束:这种组合在 Linux 上搭配 `chrome-headless-shell` 有效。 在 macOS 和 Windows 上,我们回退到使用“帧是否落地”启发式算法的 `Page.captureScreenshot`,因为这些平台上的 Chrome 要么在 `--deterministic-mode` 下崩溃,要么对这个标志组合有自身的问题。 对于 CI 和生产渲染,我们在 Linux 的 Docker 中运行。本地开发可以在任何地方进行,但在非 Linux 系统上保真度较低。 3. Chrome 停止推进其事件循环。 当 `--enable-begin-frame-control` 处于活动状态时,Chrome 的主线程停止自行滴答运行。 没有帧回调,没有 `setTimeout`,任务之间没有微任务排空。在通过 CDP 到达 `beginFrame` 消息之前,什么都不会运行。 这对于捕获期间的确定性来说非常棒。但在页面加载期间则是灾难性的,因为 `document.fonts.ready` 是一个在任务上 resolve 的 Promise,而如果没有什么东西在滴答运行,任务就不会排空。 你的 GSAP 脚本加载了。你的时间轴在 `window.__timelines` 上注册。`window.__hf.seek` 接线完成。然后 `document.fonts.ready` 永远挂起。 修复方法是一个预热循环。在页面加载期间,我们每 33ms 触发一次带有 `noDisplayUpdates: true` 的 `beginFrame`,这会推进事件循环而不产生帧: 一旦 `window.__hf` 准备就绪且字体已加载,我们就终止循环,然后在预热范围之外的时间点开始真实捕获,这样合成器永远不会看到时间倒退。这是一种只有当你第一次渲染在“加载字体”处挂起直到超时触发后,才知道要编写的变通方法。 4. Puppeteer 的 `waitForFunction` 停止工作。 在 Puppeteer 中“等待页面准备好”的习惯用法是 `page.waitForFunction(...)`。在底层,它通过注入世界中的 `requestAnimationFrame` 进行轮询。 在 `beginFrame` 模式下 rAF 不会触发。 因此 `waitForFunction` 挂起的原因与 `fonts.ready` 相同,除了你不会收到很好的 Puppeteer 错误消息,而是在截止时间触发后得到一个通用的超时。 修复方法是停止使用 `waitForFunction`,并使用 `evaluate` 和 `setTimeout` 自己编写轮询循环: 不那么巧妙。更加明显。不依赖任何在页面帧循环内运行的东西。 解决了这四个问题之后,今天交付的捕获循环大致如下: 一次 seek,一次 `beginFrame`,磁盘上一帧。无需重试。没有脆弱的帧。确定性。 ## 视中视频问题 让浏览器在渲染时播放 `<video>` 是行不通的。 在开启 BeginFrame 的无头模式下,视频解码器会跳帧、解码失败,或者在 `readyState: 0` 停留太久,从而破坏捕获截止时间。 即使没有 BeginFrame,不同的机器和不同的编解码器路径也会在同一合成上产生不同的输出。所见非所得。 网页上的 `<video>` 很乐意丢帧并觉得没问题。视频渲染器则不能。 因此,我们剥夺了 Chrome 的解码权。 在捕获开始之前,FFmpeg 会预先将合成中的每个 `<video>` 以目标 fps 提取为带编号的 JPEG。 一个 30fps 的 5 秒剪辑变成了 150 个文件。 在捕获期间,对于当前帧上的每个活动视频,我们注入一个带有正确帧字节的 data URI 的 `<img>` 兄弟节点,并隐藏原始的 `<video>`: 有趣的部分是让 `<img>` 看起来与它替换的 `<video>` 完全一样,这样 GSAP 补间、CSS 变换、不透明度淡入淡出和 `object-fit` 规则都能继续工作。 我们从原始元素读取计算样式并将它们复制到注入的图像上: 从动画库的角度来看,什么都没有改变。 元素在相同的位置具有相同的样式。只是它恰好显示的是每帧都在变化的静止图像。 我们不让 Chrome 解码和调度视频。我们像翻页书一样交给它我们想要的确切帧,然后拍张照。 其他人以不同的方式解决了同样的问题。 Remotion 运行一个长期运行的 Rust 合成器,按需解码帧并通过 HTTP 提供给其 `<OffthreadVideo>` 组件。Replit 在浏览器中使用 mp4box.js 解复用帧并通过 WebCodecs 解码,然后绘制到 `<canvas>` 中。 我们的方法是三种中最简单的:在 FFmpeg 中提前解码所有内容,从磁盘提供 JPEG。我们用灵活性(更难处理 blob URL、流源、动态设置的 src)换取了一个更短的管道。 在这里我们还有很多可以改进的地方,但基础对我们的情况来说效果很好。 ## 其他确定性陷阱 控制时间和渲染能让你完成大部分工作。但不是全部。 字体。大多数合成通过 `@import url(fonts.googleapis.com/...)` 使用 Google Fonts。该调用在渲染时就像是抛硬币。 网络可能很快、很慢或被阻止。字体可能在你的第一帧之前或之后加载。 为了消除这种差异,我们将编译后 HTML 中的每个 Google Fonts `@import` 重写为指向来自 `@fontsource` 的本地 base64 嵌入字体副本。合成渲染完全相同,只是没有了网络往返和脆弱性。 时间量化。30fps 的视频每 33.3333ms 有一帧。 如果渲染器为第 1 帧调用 `seek(0.0333333)`,并为某个重新计算时间的边缘情况路径调用 `seek(0.0333334)`,我们希望这些是完全相同的帧。因此,每次 seek,无论是在预览还是渲染中,都会通过量化器: 只有一行代码。结果证明这很重要。没有它,以不同方式计算相同名义时间的两条代码路径可能会产生相差一个像素的帧,然后你最终盯着像素级差异,不知道它是从哪里来的。 给创作者的规则。合成代码中不要用 `Date.now()`。不要使用未播种的 `Math.random()`。渲染时不要进行网络获取。 这些是契约的一部分。如果你违反它们,即使有了我们刚才构建的所有其他东西,你也会得到不确定的输出。 ## 你得到了什么 同一个 `window.__hf` 运行时包在工作室预览(在 iframe 内)和无头渲染中运行。 渲染器在开始前根据清单验证包的 sha256,这意味着“你在预览中看到的”字面上就是生成你的 MP4 的相同代码。 预览和渲染的一致性不是靠希望实现的。是强制执行的。 对于较长的视频,渲染会被拆分到 N 个 Chrome 进程中。 每个 Worker 渲染它份额的帧,FFmpeg 在最后拼接每个 Worker 的 MP4 块。 一个注意事项:视频密集型的合成在并行模式下可能会超时,因为 Chrome 在不耗尽解码器的情况下无法同时定位多个 `<video>` 元素。 修复方法是对视频密集型的渲染回退到单个 Worker。虽不优雅,但诚实。 我们并非凭空发明了这一切。 GSAP 的时间轴设计上已经是可暂停和可定位的,这就是为什么适配器只有三个方法。Remotion 几年前就证明了,如果你构建了正确的创作模型,HTML 可以成为一种视频格式。 Replit 和 Vinlic 的 WebVideoCreator 为任意 Web 内容开创了时间虚拟化和 BeginFrame 捕获。 我们走了一条不同的路,一个受约束的创作模型和一个可定位的运行时契约,但底层技术依赖于其他人首先完成的工作。 ## 我们构建 HyperFrames 的原因 我们认为这就是 Agent 将要制作视频的方式,而且我们认为 Agent 应该能够通过视频进行交流。 这就是我们开源它的原因。 我们希望其他人帮助将其扩展到更多的可能性。我们欢迎对我们的适配器系统做出贡献。 我们期待看到有什么产品构建在 HyperFrames 之上。 要尝试它,只需运行 并告诉你的 Agent > “/hyperframes create me a video about XYZ”(为我创建一个关于 XYZ 的视频)。 仓库:github.com/heygen-com/hyperframes. ## 相关链接 - [James Russo](https://x.com/Rames_Jusso) - [@Rames_Jusso](https://x.com/Rames_Jusso) - [68K](https://x.com/Rames_Jusso/status/2045230663805317349/analytics) - [Apr 17](https://x.com/HeyGen/status/2044827454460871072) - [2.4M](https://x.com/HeyGen/status/2044827454460871072/analytics) - [performance.now](https://performance.now/) - [@import](https://x.com/@import) - [fonts.googleapis.com/](https://fonts.googleapis.com/) - [@import](https://x.com/@import) - [@fontsource](https://x.com/@fontsource) - [Date.now](https://date.now/) - [github.com/heygen-com/hyperframes](https://github.com/heygen-com/hyperframes) - [Upgrade to Premium](https://x.com/i/premium_sign_up) - [3:59 AM · Apr 18, 2026](https://x.com/Rames_Jusso/status/2045230663805317349) - [68.3K Views](https://x.com/Rames_Jusso/status/2045230663805317349/analytics) - [View quotes](https://x.com/Rames_Jusso/status/2045230663805317349/quotes) --- *导出时间: 2026/4/18 10:19:35*
F Faceless-explainer Skill:一键将文本转化为解说视频 本文介绍了 HeyGen 推出的 'Faceless-explainer' Agent Skill,它能在没有任何素材的情况下,仅通过文本输入自动生成完整的解说视频。该工具负责脚本撰写、GitHub 实时调研、HTML 动态视觉设计、配音及字幕渲染,极大简化了视频制作流程。 技术 › Skill ✍ HeyGen🕐 2026-07-11 HeyGenAgent视频生成自动化HyperFramesHTMLJS写作视觉设计
连 连接四个 Skill 实现视频生成到成片的全流程自动化 文章介绍如何通过串联 Seedance 2.0、video-use、Remotion 和 FFmpeg 四个 Skill,让 Codex 实现从视频生成、剪辑、字幕制作到格式导出的全自动化流程,大幅提升视频制作效率。 技术 › Skill ✍ sitin🕐 2026-07-29 Codex视频生成自动化RemotionFFmpeg视频剪辑AI工具
C Claude Design 与 HyperFrames 的无创意工作流 本文介绍了 Anthropic 的 Claude Design 工具如何与 HyperFrames 结合,通过 HTML/CSS 直接将设计转化为视频。文章详细阐述了工作流程、品牌系统集成及实际案例,展示了从设计到渲染的无缝衔接。 技术 › Claude ✍ HyperFrames🕐 2026-07-26 Claude DesignHyperFrames设计工具视频生成工作流HTML/CSSAnthropic前端创意工具自动化
V Video Agent:基于 HeyGen 的视频生成智能体 文章介绍了 HeyGen 推出的 Video Agent 产品。这是一个无需代码、通过自然语言交互即可自动生成完整视频的智能体。用户只需描述需求,Agent 便会自动规划、编剧、选角并生成视频,支持风格定义和后期精细编辑。 技术 › Agent ✍ HeyGen🕐 2026-07-24 HeyGen视频生成AIGC智能体自动化视频编辑产品设计
打 打造爆款短视频的AI Agent实战 作者分享了如何构建一个能自动制作爆款视频的AI Agent。该代理通过分析数据寻找热门格式,利用Kimi、Seedance等工具自动完成脚本、剪辑和分发,并根据反馈自我优化。月成本仅111美元,收入达14700美元,实现了全自动化的内容生产闭环。 技术 › Agent ✍ Fokki🕐 2026-07-23 AI Agent短视频自动化KimiSeedance变现工作流视频生成
H HyperFrames Storyboard: 掌控 AI 视频输出的分镜流程 文章介绍了 HyperFrames 的新功能 Storyboard,通过模仿电影制作中的分镜流程,让 AI 在生成视频前先制定计划、草图和布局,允许用户在低成本阶段进行修改和指导,从而提高视频生成的可控性和效率。 技术 › AI视频 ✍ HeyGen🕐 2026-07-23 AI视频分镜HyperFramesHeyGen自动化工作流视频生成
拼 拼贴动画 B-roll Skill 正式开源 作者开源了一款基于 Codex 的拼贴动画 B-roll 生成 Skill。该工具可将口播文稿转化为黑白半调风格的 B-roll 视频,采用三重 QA 机制节省成本。目前仅支持 Codex,后续更新将增加定制化角色功能。 技术 › Skill ✍ 狗哥笔记🕐 2026-07-19 开源视频生成AI自动化CodexGeminiB-roll
开 开源发布灵剪 lingjian-video 作者开源了灵剪(lingjian-video)项目,这是一种可以逐关审核的AI短视频生成工具。它将脚本、分镜、配音等流程自动化,但允许用户在每个环节对候选内容进行审核和决策。项目支持4种首发风格,基于Apache-2.0协议开源,并可用作Agent Skill。 技术 › Skill ✍ 爆裂队长NEXT🕐 2026-07-19 开源灵剪视频生成AIAgent自动化审核Apache-2.0工具
为 为什么顶级 AI 工程师不再写提示词:Loops 与 Harness 的崛起 文章指出,AI 交互方式正在从单一的“提示词”转向“循环”。顶级工程师不再手动编写 Prompt,而是设计自动化循环,让智能体自我迭代直至任务完成。以 Slate 工具为例,介绍了如何通过编写 JavaScript“程序”来掌控循环逻辑,实现多模型并行处理和真实世界交互,从而构建更强大的智能系统。 技术 › Agent ✍ Rahul🕐 2026-07-18 AI工程AgentLoopSlate自动化Claude编程提示词工作流JavaScript
数 数字人口播实战攻略 文章介绍了如何利用 MiniMax 和 HeyGen 制作数字人口播视频的完整流程,并封装为 Codex Skill 实现自动化。详细讲解了声音克隆、画面驱动、素材准备及 API 调用技巧,特别强调先生成 15 秒样片以降低风险,适合知识口播和短视频批量生产。 技术 › TTS ✍ Rachel🕐 2026-07-17 数字人MiniMaxHeyGenCodexSkill视频生成自动化工作流API短视频
我 我把自己攒了半年的提示词秘籍,喂给了一个Agent当导演 作者分享了使用 LibTV Agent 制作视频的体验,包括选择预设的 Skill 作为导演,自动分镜、生成视频,以及上传自定义 Skill 的功能。该平台降低了视频制作门槛,支持手动干预和爆款视频复刻,还推出了 Skill 激励计划,鼓励创作者分享经验。 技术 › Agent ✍ zhouluobo🕐 2026-07-15 LibTVAgent视频生成Skill提示词短剧AIGC工具评测自动化经验封装
M Motion-Graphics Skill:用自然语言制作动态图形 文章介绍了 HyperFrames 的 Motion-Graphics Skill,这是一个通过 Agent 自动生成动态图形的工具。用户只需描述想要传达的内容(如趋势、品牌、数据),无需掌握专业术语或设计软件,Agent 即可自动完成数据获取、设计排版和动画渲染,极大地降低了视频设计和数据可视化的门槛。 技术 › Agent ✍ HeyGen🕐 2026-07-13 HeyGenHyperFramesMotion-GraphicsAIGC视频生成自动化设计工具数据可视化