HyperFrames is an open-source renderer that turns a plain HTML page into an MP4 — no bundler, no JSX, no build step, just a page whose animation happens to be seekable. It is Apache 2.0, it wants Node 22 and FFmpeg on PATH, and its whole pitch is three sentences long: write HTML, render video, built for agents.
That last part is the part that matters here. I did not open a video editor. I described a video, and the agent stack I run every day rendered one. This is the output:
What HyperFrames actually is
A composition is an HTML file. Timing and track assignment ride on data attributes. Motion comes from whatever seekable thing you already know — GSAP, native CSS animations, Lottie, Three.js. Preview happens in a browser; the final render is headless Chrome driving FFmpeg. Nothing in that sentence requires a framework to be learned, which is exactly why an agent can drive it: the artifact is a file it can read and edit directly, not a component tree it has to reason about through a compiler.
The alternative in this space is Remotion, and it is a good tool aimed at a different room. Remotion is React plus a bundler plus a source-available license that turns into a paid company license past a size threshold. HyperFrames is plain files and Apache 2.0. If the codebase is already React, stay in React. If the thing holding the pen is an agent, the no-build option wins on the only axis that counts, which is how many layers sit between the instruction and the pixels.
Three failures, in the order they cost me
The installer hangs. The plain install command opens an interactive multi-select picker. In a normal terminal you press space a few times and move on. In a non-TTY agent shell it never returns, and there is no error to read — the process simply sits there. Pass the non-interactive flags and redirect stdin from /dev/null.
Chrome is not bundled. The doctor command reports it plainly if you run the doctor command, which is the entire argument for running it first. It is the cheapest possible way to learn what is missing, and it costs one line.
The CDN fails silently, and this one is the real lesson. The scaffold pulls GSAP from a CDN. A render container frequently has no outbound network from Chrome even when the shell does — proxied shell, unproxied browser. The script never loads, the timeline registry is never populated, and the render produces a static or blank video with a passing exit code. Not a warning. Not a non-zero status. A file that exists, plays, and is wrong.
Vendor every library, every font, every image. Local files only, never hot-linked. I had that written down as hygiene. It is not hygiene — it is a silent-failure class, and the difference between those two words is whether you find out before or after you publish.
Narration decides the cuts, not the other way around
The instinct is to author the visuals, then lay voice over them. That desynchronises by the third frame, every time, because an estimated duration and a rendered duration are different numbers and the error compounds. So the order inverts: generate the audio first, one file per sentence, then measure each one with ffprobe rather than trusting the reported length, then derive every cut cumulatively from the measurements plus a fixed gap. Roughly three tenths of a second between segments reads as breathing; less sounds rushed, more sounds like a dropout.
Render silent, mux the voice track in afterwards, and then verify that both streams actually survived — a muxed file that lost its audio stream still plays perfectly, and you will not notice on a machine where you already know what it is supposed to say. Two runs, measured: eight segments produced 28.90s of video against 28.904s of audio, and nine segments produced 58.26s against 58.26s. That is what deriving from measurements buys you instead of eyeballing.
The skills that made it one job instead of six
None of the above is hard. All of it is forgettable, which is worse, because a forgotten step here does not fail loudly — it ships a blank video with a green exit code. So the facts live in a skill file rather than in my head. I wrote about the orchestrator layer that routes them a week ago; this is what it looks like when that layer earns its keep on a real deliverable.
- hyperframes — a pointer layer, deliberately thin. It installs the upstream skill pack and defers to it rather than reimplementing the framework’s own workflow skills badly. What it does hold is the stuff that cost a failed run: the non-interactive install flags, the doctor-then-browser-ensure order, the vendoring rule, and the audio-first timing sequence.
- orchestrator — picks the category, orders the chain, and names which gates can block the result before any work starts.
- dota-companion-scheme — the numbers in the video are not decorative. They come from the extension’s own stat contract, and that file is why the script could quote them without me re-deriving anything.
- verify-before-done — the gate that will not let me say a video is finished because a command exited zero. Both streams present, or it is not done.
- preship-ritual and wpcom-dhsea-scheme — the pair that got this post onto a live page without breaking it, which on a live WordPress write is its own small discipline.
The pattern generalises past video. Every one of those files exists because something failed silently once and I did not want to pay for it twice. That is the whole method: a skill is a receipt for a mistake.
Why the video is about inventory
Dota Companion is a Chrome extension that reads public Dota 2 match data and turns it into things worth knowing about your own play — improvement streaks measured against your own baseline, not attendance. The inventory number in the short came out of a piece I wrote about counting what a Dota inventory is actually worth: a large pile of items, a much smaller pile you can do anything with. It works as a short precisely because it is one number against another number, and that is the shape that survives a vertical crop.
The extension is on the Chrome Web Store, and the data behind it comes from the excellent free OpenDota API.
What I would tell someone starting today
Run the doctor first. Vendor everything. Generate the audio before you write a single timing value. Verify the output file rather than the exit code. Four rules, and every one of them exists because I broke it first.
HyperFrames lives at github.com/heygen-com/hyperframes. There is a fuller index of everything on this site at the history page, and the builds themselves are on the projects index.
I cross-publish these writeups on dev.to.
