How to Render MP4s in Node.js without FFmpeg: A Guide to Headless WebCodecs
July 8, 2026 · By VideoFlowLearn how to render high-quality MP4s in Node.js without FFmpeg. Discover how VideoFlow uses headless WebCodecs to eliminate per-frame overhead and boost performance.
How to Render MP4s in Node.js without FFmpeg: A Guide to Headless WebCodecs
For years, generating video on the server meant one thing: wrestling with FFmpeg shell commands. Whether you were concatenating strings to build a complex command or using a wrapper library, the underlying process was the same—and it was often the bottleneck of your application.
If you've ever tried to render MP4s in Node.js without FFmpeg, you know the struggle. Traditional headless rendering involves taking a screenshot of every single frame, saving it to disk as a JPEG, and then piping those thousands of files back into an encoder. This "FFmpeg Tax" introduces massive I/O overhead, latency, and resource contention.
Today, we’re looking at a faster way: using headless WebCodecs with VideoFlow.

The FFmpeg Tax: Why Per-Frame Screenshots are Slow
Most programmatic video tools work by driving a headless browser (like Chromium) and calling page.screenshot() for every frame. At 30 FPS, a one-minute video requires 1,800 screenshots. Each screenshot must be compressed as a JPEG or PNG, sent over a socket from the browser to Node, and then sent again to an FFmpeg process for re-encoding into H.264.
This architecture is inherently inefficient because it treats video as a sequence of unrelated images rather than a continuous stream of data. It’s why server-side video generation has traditionally been expensive and slow to scale.
Enter Headless WebCodecs
VideoFlow changes the equation by moving the encoding step inside the browser. Our server-side renderer, @videoflow/renderer-server, drives a headless Chromium instance via Playwright, but it doesn't take screenshots. Instead, it uses the WebCodecs API to encode the video buffer directly within the browser process.
The result is a single, optimized stream that is POSTed back to your Node.js environment as a finished MP4. By bypassing the per-frame round-trip, VideoFlow can often render videos several times faster than traditional screenshot-based pipelines.
Tutorial: Render a 1080p Video in 20 Lines
Let’s build a simple programmatic video. We'll start by installing the core library and the server-side renderer. Note that you'll also need to install the Chromium binary for Playwright.
npm install @videoflow/core @videoflow/renderer-server
npx playwright install chromium
Now, we can write a script to define our timeline and trigger a render. Unlike other frameworks that require a full React runtime, VideoFlow uses a portable VideoJSON format that can be generated by any environment.
import VideoFlow from '@videoflow/core';
import '@videoflow/renderer-server';
const $ = new VideoFlow({ width: 1920, height: 1080, fps: 30 });
// 1. Add a background image
const bg = $.addImage(
{ fit: 'cover', opacity: 0.8 },
{ source: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe' }
);
// 2. Add a title with a cinematic transition
const title = $.addText(
{
text: 'HEADLESS RENDER',
fontSize: 8,
color: '#FF5A1F',
fontWeight: 800,
position: [0.5, 0.4]
},
{
transitionIn: { transition: 'slideUp', duration: '800ms' }
}
);
// 3. Animate a GLSL effect
title.animate(
{ filterBlur: 0 },
{ filterBlur: 1 },
{ duration: '2s', wait: false }
);
$.wait('3s');
// 4. Render to a local file
await $.renderVideo({
outputType: 'file',
output: './headless-video.mp4',
verbose: true,
});
This script initializes a 1080p project, adds an image and text, applies a slideUp transition, and animates a blur filter. When $.renderVideo() is called, VideoFlow detects the Node.js environment and automatically uses the headless WebCodecs pipeline.

Why Portability Matters
One of the biggest advantages of this approach is that the exact same code runs everywhere. You can preview this video at 60 FPS in your React app using the DOM renderer, let users export it in their own browser via the renderer-browser package to save on server costs, or queue it for a high-quality server-side render using the code above.
Because the rendering engine is pixel-for-pixel identical across all three official renderers, you never have to worry about "environment drift" where a video looks different on your machine than it does on the server.
Conclusion
Rendering MP4s in Node.js doesn't have to be a battle with FFmpeg dependencies and screenshot overhead. By leveraging headless WebCodecs, you can build video generation pipelines that are faster, more portable, and easier to maintain.
Ready to start building? Head over to the VideoFlow Playground to experiment with the builder API in real-time, or check out the documentation to see the full list of available transitions and GLSL effects. If you like what we're building, come say hi on GitHub.