VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
← Back to Blog

How to Render MP4s in Node.js Without FFmpeg

May 21, 2026 · By VideoFlowLearn how to generate high-quality MP4 videos directly in Node.js using VideoFlow and WebCodecs, eliminating the need for complex FFmpeg installations.How to Render MP4s in Node.js Without FFmpeg

How to Render MP4s in Node.js Without FFmpeg

For years, programmatic video in Node.js has been synonymous with one thing: shell-scripting FFmpeg. Whether you were using a wrapper library or spawning child processes manually, the workflow was always the same—install a heavy binary, manage codec flags, and hope the string-concatenated command didn't break in production.

But as we move toward more modern, web-standard pipelines, that dependency is becoming an unnecessary bottleneck. In this guide, we'll look at how to render high-quality MP4s directly in Node.js using VideoFlow and WebCodecs, completely bypassing the need for a local FFmpeg installation.

Why Move Away from FFmpeg?

FFmpeg is a legendary tool, but it wasn't built for the modern cloud-native developer. When you're building a video automation platform or a SaaS that generates personalized content, FFmpeg introduces several friction points:

  1. Environment Bloat: Your Docker images grow by hundreds of megabytes just to include the binary.
  2. Platform Inconsistency: 'libx264' might behave differently on your Mac than on a Linux Lambda function.
  3. The Screenshot Bottleneck: Traditional programmatic video often involves taking thousands of individual screenshots (JPEGs) and piping them into FFmpeg's stdin, which is incredibly slow and CPU-intensive.

The WebCodecs Pipeline

The Modern Alternative: WebCodecs + Playwright

VideoFlow takes a different approach. By using @videoflow/renderer-server, you can drive a headless Chromium instance via Playwright. Instead of taking screenshots, VideoFlow uses the WebCodecs API inside the browser to encode frames directly to an MP4 container.

This method is typically several times faster because it eliminates the per-frame screenshot round-trip. The frames stay in the browser's memory until the final MP4 is muxed and sent back to your Node process as a Buffer.

Step 1: Setting up the Project

First, install the core library and the server renderer:

npm install @videoflow/core @videoflow/renderer-server
npx playwright install chromium

Step 2: Building Your Video

Using the fluent VideoFlow builder API, we can compose a scene with just a few lines of TypeScript. Notice how we use normalized coordinates ([0.5, 0.5]) and em units to keep the design resolution-agnostic.

import VideoFlow from '@videoflow/core';
import '@videoflow/renderer-server'; // Registers the server-side renderer

const $ = new VideoFlow({
  width: 1920,
  height: 1080,
  fps: 30,
  backgroundColor: '#000000'
});

// Add a background image
const bg = $.addImage(
  { fit: 'cover', opacity: 0.8 },
  { source: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe' }
);

// Add a title with a transition
const title = $.addText(
  {
    text: 'NO FFMPEG REQUIRED',
    fontSize: 8,
    color: '#FF5A1F',
    fontWeight: 800,
    position: [0.5, 0.4]
  },
  {
    transitionIn: { transition: 'blurResolve', duration: '800ms' }
  }
);

$.wait('3s');
title.fadeOut('500ms');

Step 3: Rendering to a File

Now, we tell the renderer to produce our MP4. By default, VideoFlow uses the WebCodecs pipeline. You don't need to pass any special flags—it just works.

await $.renderVideo({
  outputType: 'file',
  output: './automated-video.mp4',
  verbose: true
});

Comparing FFmpeg vs WebCodecs

Deep Dive: How it Works Under the Hood

When you call renderVideo in a Node environment, the @videoflow/renderer-server package performs a few clever tricks:

  1. Shared Browser Instance: It maintains a shared Playwright browser instance to avoid the cold-start cost of launching Chromium for every render.
  2. In-Page Bundling: It bundles a small renderer script using esbuild and injects it into the headless page.
  3. Direct Buffer Handoff: The finished MP4 bytes are POSTed from the headless browser back to the Node process, where they are converted into a standard Buffer.

If you're building a high-scale SaaS video pipeline, this architecture allows you to scale horizontally without worrying about the underlying OS dependencies.

Conclusion

Rendering video in Node.js doesn't have to be a headache. By leveraging the power of WebCodecs and a portable JSON-based schema, VideoFlow makes it possible to generate cinematic content with zero FFmpeg baggage.

Whether you're migrating from Remotion to get a more flexible, Apache-2.0 licensed core, or you're simply tired of FFmpeg's complexity, the future of video-as-code is here.

Ready to try it out? Head over to the VideoFlow Playground to build your first scene in the browser, or check out our getting started guide to start building on the server. You can also explore the source code on GitHub.

VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContactTermsPrivacy

From the blog

All posts →How to Automate Loom-style Product Demos with TypeScriptAutomated Podcast Audiogram Generator: Turning Audio into Viral Video with TypeScriptHow to Turn Markdown Changelogs into Automated Product Update VideosAutomating Personalized Onboarding Videos with VideoFlow and TypeScriptAutomated Video Subtitles: A Developer's Guide to the VideoFlow Captions LayerAutomating YouTube Shorts: Build a Vertical Video Factory in 30 Lines of TypeScriptHow to Build a Browser-Based Video Editor with React and VideoFlowCinematic 3D Video with TypeScript: A Guide to Perspective and Rotation
© 2026 VideoFlow. Apache-2.0 core.