How to convert HTML to MP4

You record the page rather than convert it. A screen recorder is enough for a demo. For smooth, repeatable output you render numbered frames and encode them, which removes every timing problem at once.

To convert HTML to MP4 you record the page while it runs. A page is a document and a video is a sequence of frames, so something has to play the page and watch it.

There are three ways to do that, and they differ in how much control you have over timing. Timing is what separates a smooth clip from a stuttering one.

A browser window sized to 1280 by 720 with the page filling it, ready to record.
A browser window sized to 1280 by 720 with the page filling it, ready to record.

Three ways to convert HTML to MP4

Route Smoothness Setup Repeatable
Screen recorder Depends on your machine Install one app No
Browser video capture Usually good A script and a browser Mostly
Frame render plus ffmpeg Exact Node, a browser, ffmpeg Yes

Pick the third route if the clip goes into a product page or documentation that gets rebuilt. Pick the first if you need something in the next five minutes.

Route 1: a screen recorder

Size the browser window to the output dimensions before you start, not after. Cropping later costs resolution.

Then record, and keep three things in mind.

  • Close everything else. Real time recording and page animation compete for the same processor.
  • Hide the cursor unless a click is the point of the clip.
  • Record a second of stillness at each end. It gives you room to trim cleanly.

The output is usually MP4 already. If it is MOV or WebM, ffmpeg converts it in one command.

Route 2: let the browser record

Playwright can write a video of a page session while your script drives it. You get a file without running a separate recorder.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  recordVideo: { dir: 'out/', size: { width: 1280, height: 720 } },
});
const page = await context.newPage();
await page.goto('file:///C:/pages/demo.html');
await page.waitForTimeout(6000);
await context.close();
await browser.close();

The video is written when the context closes, so closing the context is not optional. The output is WebM, which converts to MP4 with a single ffmpeg call.

This route is good for recording a scripted interaction: clicks, scrolls, a form being filled.

Route 3: render frames, then encode

This is the route that removes timing from the problem entirely. You decide the frame rate, step the page forward, and capture each frame.

const fps = 30, seconds = 5;
for (let i = 0; i < fps * seconds; i++) {
  await page.evaluate((t) => window.setFrame(t), i / fps);
  await page.screenshot({ path: `frames/f${String(i).padStart(4, '0')}.png` });
}

The page exposes a function that puts the animation at a given time, and the script calls it. Nothing depends on how fast the machine is.

Then encode:

ffmpeg -framerate 30 -i frames/f%04d.png \
  -c:v libx264 -pix_fmt yuv420p -crf 20 out.mp4
A folder of numbered PNG frames written by the capture loop, ready for encoding.
A folder of numbered PNG frames written by the capture loop, ready for encoding.

The encode settings that matter

Four flags cover almost every compatibility problem.

  1. -c:v libx264. H.264 is the codec that plays everywhere, including in the preview panes of chat apps.
  2. -pix_fmt yuv420p. Without it, some players show a black frame or refuse the file. This is the most common cause of "it plays for me".
  3. -crf. Quality, where lower is better. 18 is visually lossless for interface footage, 23 is a reasonable default.
  4. Even dimensions. H.264 requires them. An odd width fails the encode outright.

If a source has an odd dimension, crop or pad rather than scaling, so text stays on whole pixels.

Making the page look right on video

Interface footage compresses badly when it is full of thin lines and small text. Adjust the page rather than the encoder.

  • Increase the base font size for the recording. Text that is comfortable on a laptop is unreadable in an embedded player.
  • Avoid one pixel borders and hairline dividers; they shimmer under compression.
  • Give animations an obvious start and end state so the viewer can follow them.
  • Let web fonts finish loading before the first frame, or the opening will show the fallback.

Two more habits make clips easier to reuse. Hold the final state for a full second before cutting, so a viewer who looks up late still sees the result.

And record without a cursor unless a click is the subject. A pointer drifting across an otherwise still frame reads as an accident rather than a demonstration.

MP4 or GIF

MP4 is smaller by a wide margin at the same quality, and it supports sound and longer durations. GIF still wins for a two second loop that must autoplay with no controls.

Converting HTML to GIF covers the frame capture and palette steps for that format. The capture half is identical; only the encode differs.

What video cannot carry

A recording is a fixed path through the page. The viewer cannot hover a data point, sort a column, or read the row you scrolled past.

Numbers in the clip are also frozen. A dashboard video is out of date the day after you record it, and nothing about the file says so.

A clip of a dashboard next to the live page, with a different figure visible in each.
A clip of a dashboard next to the live page, with a different figure visible in each.

Screenshot versus live page makes the same argument for stills, and it holds for video.

Sending the page with the clip

Record the clip when you need attention in a feed or a slide. Send the page when the reader needs the detail.

Paste the HTML into a NOS document and it renders as written, scripts and animation included, at its own address. The reader gets the live page rather than a recording of it.

Turning HTML into a link is that step. After it, fixing a figure does not mean re-recording, because the link already points at the corrected page.

Questions people ask

How do I record an HTML page as MP4?

Open the page and use a screen recorder, or drive a headless browser that writes video while it runs. For exact output, capture numbered frames and encode them with ffmpeg into an MP4.

Why does my recording stutter?

Real time recording competes with the animation for the same processor. Deterministic frame capture removes the problem, because you step the animation forward yourself and time never runs short.

What settings make the MP4 play everywhere?

Encode with H.264, set the pixel format to yuv420p, and keep both dimensions even. Files that skip the pixel format play in some players and show a black frame in others.

Is a video better than a link to the page?

A video is better when the audience will not click, or when the point is a sequence of actions. A link is better whenever the numbers change or the reader needs to interact, because the video freezes both.

Keep reading