Last updated: September 17, 2025 \u00b7 7-minute read
Eight weeks ago, I ran an experiment turning HTML into video using agents and browser automation. The output was rough \u2014 10-second clips with inconsistent timing and fonts that broke at different resolutions. But the core idea stuck: if I can define a video as HTML, I can version-control it, template it, and automate it.
That early experiment is documented in my Week 3 post. Now, eight weeks later, the pipeline is production-grade. Here\u2019s what it actually looks like.
The Problem I\u2019m Actually Solving
Over on r/webdev, a post titled \u201cI Couldn\u2019t Find a Good Open-Source Web Video Editor, So I Built One\u201d hit 139 upvotes. The author built a React-based video editor because every open-source option was either abandoned, Electron-bloated, or missing basic features.
That post resonated because it described exactly the gap I\u2019d been feeling. But my approach was different. Instead of building a video editor, I eliminated the editor entirely.
The use case: I needed to generate short, structured videos programmatically \u2014 launch announcements, feature walkthroughs, data visualizations. The content was templatable. The format was repeatable. Opening Premiere or DaVinci Resolve for each one was wasteful when 90% of the \u201cediting\u201d was placing text on a background with a fade transition.
The Three-Tool Pipeline
Stage 1: HTML Templates
Every video starts as an HTML file. Not a mockup \u2014 the actual source of truth. I define layouts using plain HTML and CSS with a few conventions:
-
data-frameattributes mark animation keyframes - CSS animations handle transitions between frames
- Media assets (images, audio) are referenced by relative path
This means I can write a \u201cvideo\u201d the same way I write a webpage. I use Tailwind for styling. I use CSS @keyframes for timing. The entire template is just a static HTML file that looks correct when opened in a browser.
Why HTML? Because every developer already knows it. Because you can version-control it in git. Because you can generate it with any templating engine. Because a browser is the most reliable rendering engine on the planet.
Stage 2: HyperFrames
HyperFrames is an open-source framework from HeyGen that turns HTML, CSS, and seekable animations into deterministic MP4 videos. Apache 2.0 licensed. The pitch: \u201cWrite HTML. Render video. Built for agents.\u201d
HyperFrames takes my HTML template and renders it frame-by-frame using a headless browser. It reads the data-frame attributes to understand timing, captures each frame as an image, and pipes them to ffmpeg for encoding.
The key feature that made HyperFrames the right choice is seekable animations. It doesn\u2019t just record a playback \u2014 it can seek to any point in the animation timeline and render that specific frame. Same input, same output, every time.
Stage 3: html-video + ffmpeg
For the final encoding, I use html-video combined with ffmpeg for audio muxing and format optimization:
ffmpeg -framerate 30 -i frames/%04d.png -i audio.mp3 \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-shortest output.mp4
What the Pipeline Looks Like in Practice
video-project/
\u251c\u2500\u2500 templates/
\u2502 \u251c\u2500\u2500 launch-announcement.html
\u2502 \u2514\u2500\u2500 feature-walkthrough.html
\u251c\u2500\u2500 assets/
\u2502 \u251c\u2500\u2500 logo.svg
\u2502 \u2514\u2500\u2500 background.mp3
\u251c\u2500\u2500 scripts/
\u2502 \u251c\u2500\u2500 render.sh
\u2502 \u2514\u2500\u2500 encode.sh
\u251c\u2500\u2500 output/
\u2514\u2500\u2500 config.json
To create a new video, I either write the HTML template manually or generate it with an LLM agent. The agent gets the content brief and outputs a valid HTML file with the correct data-frame attributes. Then I run two shell commands. Done.
The Hard Parts
Frame-perfect timing was the biggest challenge. CSS animations don\u2019t guarantee sub-frame accuracy \u2014 a 1-second animation might complete at 0.98s or 1.02s depending on the browser\u2019s rendering pipeline. I solved this by using discrete keyframe steps instead of smooth transitions for anything that needs to sync precisely.
Font rendering was the second issue. Google Fonts load asynchronously, which means the first few frames might render in a fallback font. I embedded the fonts as base64 in the HTML template. Ugly in source code, but the output is consistent.
What Eight Weeks of Iteration Taught Me
The Week 3 experiment produced janky 10-second clips. The Week 10 pipeline produces broadcastable short-form content. The difference wasn\u2019t any single breakthrough \u2014 it was 40+ iterations on templates, timing calibration, and understanding where the frame-level rendering breaks down.
The biggest lesson: the tooling is finally good enough that the bottleneck is now content, not rendering. HyperFrames handles the hard part. My job is writing good HTML templates and having something worth saying in them.
TL;DR
- Three-tool pipeline: HTML templates \u2192 HyperFrames (renders frames) \u2192 ffmpeg (encodes MP4)
- No video editor needed. Code in, video out.
- Deterministic rendering: same HTML always produces the same video
- Hard parts: frame timing precision, font loading, audio sync
- 40+ iterations from janky 10-second clips to production-grade output
If you\u2019re generating video content programmatically \u2014 product demos, social posts, tutorial overlays \u2014 this pipeline eliminates the editor entirely. Check out the Lab for the templates I\u2019m open-sourcing, and the TalkDrive post for how I handle the audio layer with AI-generated voiceovers.
Not affiliated with HeyGen or HyperFrames. Tools used: HyperFrames (Apache 2.0), html-video, ffmpeg, Tailwind CSS. Built on a Linux workstation.
Originally published at ansaribilal.com. I write about AI agents, indie builds and developer tooling — more posts here.

Top comments (0)