VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
Getting started
InstallationQuick startCore conceptsYour first video
Builder
Builder APITime formatsParallel & wait
Layers
TextImageVideoAudioCaptionsShapeGroups
Animation
Animate & keyframesEasing functionsTransitionsEffects
Renderers
Browser rendererServer rendererDOM preview
React Video Editor
QuickstartThemingUploadsCustom panelsHooks & commandsKeyboard shortcuts
API reference
Overview@videoflow/core@videoflow/renderer-browser@videoflow/renderer-server@videoflow/renderer-dom@videoflow/react-video-editor

Rendering on a server

@videoflow/renderer-server runs in Node. It launches headless Chromium via Playwright, loads the same browser renderer the browser package uses, and exports an MP4 — by default entirely inside the browser (WebCodecs + MediaBunny mux), with no ffmpeg dependency. An alternative ffmpeg pipeline is also available for hosts that need custom encoder flags.

Install

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

Node 18+ is required. ffmpeg is optional — only install it system-wide if you plan to pass { ffmpeg: true }.

To a file

import ServerRenderer from '@videoflow/renderer-server';
import VideoFlow from '@videoflow/core';

const $ = new VideoFlow({ width: 1920, height: 1080, fps: 30 });
// ... build flow ...
const json = await $.compile();

await ServerRenderer.render(json, {
  outputType: 'file',
  output: './out.mp4',
  onProgress: (p) => console.log(`${Math.round(p * 100)}%`),
});

To a buffer

const buffer = await ServerRenderer.render(json, { outputType: 'buffer' });
// e.g. upload to S3 straight from memory
await s3.send(new PutObjectCommand({ Bucket, Key, Body: buffer, ContentType: 'video/mp4' }));

Options

OptionDefaultNotes
ffmpegfalseWhen true, switches to the alternative per-frame screenshot → ffmpeg pipeline. Requires ffmpeg 4.4+ on PATH.
outputType'buffer''file' | 'buffer'.
outputPath when outputType: 'file'.
signalAbortSignal for cancellation.
onProgress(p: 0..1) => void.
verbosefalseStream stage logs to the console.

Pipelines

Browser export (default)ffmpeg pipeline
EncoderWebCodecs (H.264) + MediaBunny muxJPEG screenshots → libx264 via ffmpeg
AudioEncoded inside the browser, muxed with videoRendered to WAV, muxed by ffmpeg
System depsPlaywright Chromium onlyPlaywright + ffmpeg 4.4+
SpeedSeveral × faster — no per-frame round-tripSlower; useful when you need ffmpeg flags
OutputMP4 (H.264 + AAC)MP4 (configurable via ffmpeg)

Single frame and audio

Need a thumbnail or a music-only export? The renderer exposes per-frame and audio-only methods on the instance:

const renderer = new ServerRenderer(json);
const jpeg = await renderer.renderFrame(120);   // Buffer (JPEG)
const wav  = await renderer.renderAudio();      // Buffer (WAV) | null
await renderer.cleanup();

Inside an HTTP handler

// Express / Node / Bun
app.post('/render', async (req, res) => {
  const json = req.body;
  try {
    const buffer = await ServerRenderer.render(json, { outputType: 'buffer' });
    res.setHeader('Content-Type', 'video/mp4');
    res.setHeader('Content-Disposition', 'attachment; filename="video.mp4"');
    res.end(buffer);
  } catch (err) {
    res.status(500).json({ error: String(err) });
  }
});
Scaling. Render jobs are CPU-heavy. In production, push them into a queue (BullMQ, SQS, Temporal) and cap concurrency to your CPU count. Call closeSharedBrowser() on shutdown — the package keeps a single Chromium instance alive across renders.
VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContact

Legal

TermsPrivacy
© 2026 VideoFlow. Apache-2.0 core.