How to Convert HTML to a Video File (MP4, WebM, GIF)
First time someone asked me to convert HTML to a video file, I thought they were joking. But it's actually a real need that comes up more than you'd expect. Maybe you built a slick CSS animation that needs to go on Instagram as an MP4. Maybe you've got a data visualization that a client wants as a video. Maybe your boss wants a recording of a web-based presentation.
No browser has a "Save as Video" button. But there are ways to get this done. Five of them, actually.
Method 1: Just Record Your Screen
Simplest approach. No code. No setup.
Mac: Cmd+Shift+5. Pick your area. Hit Record. You get a .mov file. If you need MP4, run it through a file converter or QuickTime.
Windows: Win+G opens Xbox Game Bar. Hit the record button. Saves directly as MP4.
OBS Studio (free, works on everything): Way more control here — you pick the resolution, frame rate, encoding, all of it. Point a Window Capture at your browser and record.
Screen recording is perfect for one-off jobs. The downside? It runs in real time. A 60-second animation takes 60 seconds to record. If you need to batch process stuff or want exact frame-by-frame control, you need to go programmatic.
Method 2: Puppeteer + FFmpeg
This is the real deal. Headless Chrome captures individual frames, FFmpeg stitches them into video.
// capture-frames.js
const puppeteer = require('puppeteer');
async function captureFrames() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('http://localhost:3000/animation.html');
const fps = 30;
const duration = 10; // seconds
const totalFrames = fps * duration;
for (let i = 0; i < totalFrames; i++) {
// Jump animation to the exact right moment
await page.evaluate((time) => {
window.seekAnimation(time);
}, i / fps);
await page.screenshot({
path: `frames/frame-${String(i).padStart(5, '0')}.png`,
type: 'png'
});
}
await browser.close();
}
Then stitch it together:
ffmpeg -framerate 30 -i frames/frame-%05d.png -c:v libx264 -pix_fmt yuv420p output.mp4
What you get: frame-perfect video at whatever resolution you want. The animation plays at exactly the right speed no matter how slow the actual rendering is. Zero dropped frames. Zero timing weirdness.
The catch though — your HTML animation needs a seek function. It has to be able to jump to a specific point in time. If it's all CSS with no JavaScript hooks, you'll need to add a seekAnimation function or tap into the Web Animations API.
Method 3: Remotion (If You're a React Person)
Remotion was literally built for turning React components into videos. You write your animation as JSX, and it renders frame by frame to MP4.
// MyVideo.tsx
import { useCurrentFrame, useVideoConfig } from 'remotion';
export const MyVideo = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = Math.min(1, frame / (fps * 2));
return (
<div style={{ opacity, fontSize: 80 }}>
Hello from HTML
</div>
);
};
Render it: npx remotion render src/index.tsx MyVideo out.mp4
Remotion is awesome for programmatic stuff — data viz videos, personalized content, animated infographics. It's total overkill for just capturing a static webpage. But if converting HTML to video is something you do regularly, it's worth the time to learn.
Method 4: CCapture.js (Browser Only, No Server)
This one's interesting because it runs entirely in the browser. No server, no installs.
- CCapture.js hooks into requestAnimationFrame, grabs each frame, and can export as WebM or GIF
- html2canvas turns DOM elements into canvas, which CCapture can then capture
const capturer = new CCapture({
format: 'webm',
framerate: 30,
verbose: true
});
capturer.start();
function animate() {
// your animation stuff here
capturer.capture(document.getElementById('canvas'));
requestAnimationFrame(animate);
}
// When you're done:
capturer.stop();
capturer.save(); // downloads a .webm file
On the plus side: no tools to install, runs right in the browser. On the minus side: you get WebM (not MP4), it can chug on complex DOM stuff, and the quality depends on how well html2canvas renders your page. Not always perfect.
Method 5: Playwright Video Recording
Playwright has built-in video recording. Probably the lowest-effort automated approach:
const context = await browser.newContext({
recordVideo: { dir: './videos/', size: { width: 1920, height: 1080 } }
});
const page = await context.newPage();
await page.goto('http://localhost:3000');
// interact with the page however you need to...
await context.close(); // saves the video automatically
One config option and it records everything that happens in the browser. That's it.
Which One Should You Pick?
| Your Situation | Go With |
|---|---|
| Need it done once, right now | Screen recording (OBS or built-in) |
| Need exact frame control | Puppeteer + FFmpeg |
| Building with React already | Remotion |
| No tools, browser only | CCapture.js |
| Automated test recordings | Playwright |
| Lots of pages to process | Puppeteer + FFmpeg |
A Quick Note on Formats
MP4 with H.264: The safe choice. Plays everywhere — Instagram, Twitter, email clients, phones. Default to this unless you have a reason not to.
WebM with VP9: Smaller files, open format. Good for embedding on websites. But some platforms won't accept it for uploads.
GIF: No sound. Giant file sizes. Only 256 colors. Fine for short loops under 10 seconds — documentation screenshots, Slack messages, that kind of thing.
If you recorded in one format and need another, our file converter can swap between them.
I won't pretend the HTML-to-video pipeline is as clean as it should be. There's no one-click solution. But once you've picked the right method for your situation and set it up, it gets pretty painless to repeat. And honestly, the Puppeteer + FFmpeg combo is way more straightforward than it looks at first glance.