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

Builder API

VideoFlow's builder is a thin fluent shell over a flow pointer. You add layers and animations in source order; the pointer advances automatically after each action. There's no React tree, no component model, no declarative diff — just a sequence of imperative calls that compile to JSON.

On this page

Constructor

const $ = new VideoFlow({
  name: 'My video',           // used as filename / metadata
  width: 1920, height: 1080,  // output resolution
  fps: 30,                    // frames per second — 24, 30, 60 all work
  backgroundColor: '#000',    // fills under all layers
  autoDetectDurations: true,  // probe video/audio layers for their duration
  verbose: false,             // log keyframe/action push events to the console
  defaults: {
    easing: 'easeOut',
    fontFamily: 'Inter',
  },
});

Adding layers

Five factory methods. Each pushes a layer onto the timeline at the current flow pointer and returns the layer instance.

CallReturns
$.addText(props, settings?, options?)TextLayer
$.addImage(props, settings?, options?)ImageLayer (settings.source)
$.addVideo(props, settings?, options?)VideoLayer (settings.source)
$.addAudio(props, settings?, options?)AudioLayer (settings.source)
$.addCaptions(props, settings, options?)CaptionsLayer (settings.captions)
$.addShape(props, settings?, options?)ShapeLayer (settings.shapeType: rectangle, ellipse, polygon, star)
$.group(props, settings, fn, options?)GroupLayer — children authored inside fn are composited as one. See Groups.

Layer instance methods

MethodEffect
.animate(from, to, opts)Push a pair of keyframes on each listed prop.
.set(patch)Patch static layer props — no keyframes.
.fadeIn(duration)Shorthand for animate({ opacity: 0 }, { opacity: 1 }, ...)
.fadeOut(duration)Shorthand for the opposite.
.show() / .hide()Toggle visibility at the current time — no interpolation.
.remove(opts?)End the layer at the current flow position. Pass { in: time } to defer.

Layer factory options

The third argument to every add* / group call is an AddLayerOptions object that controls how the layer interacts with the flow pointer and the layer stack:

OptionDefaultNotes
waitFor0 (visual layers); 'finish' (groups, video / audio with explicit duration)How much the outer flow pointer advances. Pass a Time to wait that long, or 'finish' to wait for the layer's full duration.
indexZ-order. Negative pushes the layer behind earlier ones; positive pulls it forward.

Flow control

CallEffect
$.wait(duration)Advance the flow pointer without adding layers.
$.parallel([fns])Run each fn with the same start time; advance the pointer by the longest branch.
$.compile()Finalise and return the VideoJSON.

Parallel branches

Each function in $.parallel([...]) starts at the same flow-pointer time. Inside a branch, $.wait() advances only that branch — use it to stagger.

const $ = new VideoFlow({ width: 512, height: 512, fps: 30, backgroundColor: '#0b0b1f' });

const a = $.addText({ text: 'A', fontSize: 16, fontWeight: 800, color: '#ff6b6b', position: [0.25, 0.5], opacity: 0 });
const b = $.addText({ text: 'B', fontSize: 16, fontWeight: 800, color: '#4ecdc4', position: [0.5,  0.5], opacity: 0 });
const c = $.addText({ text: 'C', fontSize: 16, fontWeight: 800, color: '#45b7d1', position: [0.75, 0.5], opacity: 0 });

// All three branches share a start time — stagger each with $.wait().
$.parallel([
  () => a.fadeIn('600ms'),
  () => { $.wait('200ms'); b.fadeIn('600ms'); },
  () => { $.wait('400ms'); c.fadeIn('600ms'); },
]);
$.wait('2s');
return $;
Compiling00:00

Holding the pointer still

animate(..., { wait: false }) pushes keyframes but doesn't advance the pointer — so you can stack another animation on the same window.

const $ = new VideoFlow({ width: 512, height: 512, fps: 30, backgroundColor: '#111' });

const box = $.addText({ text: 'BG', fontSize: 40, fontWeight: 900, color: '#222', position: [0.5, 0.5] });
const label = $.addText({ text: 'Foreground', fontSize: 8, fontWeight: 700, color: '#fff', position: [0.5, 0.5], opacity: 0 });

// wait: false — the flow pointer doesn't advance while the background pulses,
// so the label fades in on top during the same window.
box.animate({ scale: 1 }, { scale: 1.15 }, { duration: '1.5s', easing: 'easeInOut', wait: false });
label.fadeIn('800ms');
$.wait('1.5s');
return $;
Compiling00:00

Compiling

const json = await $.compile();
// json is plain, serialisable VideoJSON — save it, send it, render it later.

Beyond compile(), the builder also exposes one-shot convenience methods that skip the intermediate JSON:

await $.renderVideo({ outputType: 'buffer' });    // → Buffer | Blob
await $.renderFrame(60);                          // → OffscreenCanvas | Buffer
await $.renderAudio();                            // → AudioBuffer | Buffer
These helpers pick a renderer based on the environment (browser → renderer-browser, Node → renderer-server). If you already have a renderer imported, pass it explicitly for finer control.

Next

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.