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

Parallel & wait

Every VideoFlow timeline is built from two primitives: $.wait(duration) pushes the flow pointer forward, and $.parallel([...]) runs multiple branches starting at the same pointer. That's the whole model — everything else is sequenced layer calls.

On this page

Sequential by default

Calls run in source order. .animate() and .fadeIn() advance the pointer by their duration — so the next call starts when the previous finishes.

title.fadeIn('500ms');   // pointer: 0 → 0.5s
$.wait('1s');            // pointer: 0.5 → 1.5s
title.fadeOut('500ms');  // pointer: 1.5 → 2s

Stagger with parallel

Inside each branch, $.wait() advances only that branch. The parent pointer jumps to the longest branch when parallel() returns.

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

const lines = ['First', 'Second', 'Third', 'Fourth'].map((t, i) =>
  $.addText({ text: t, fontSize: 7, fontWeight: 700, color: '#fff', position: [0.5, 0.3 + i * 0.13], opacity: 0 })
);

$.parallel(lines.map((l, i) => () => {
  $.wait(`${i * 120}ms`);
  l.animate({ opacity: 0, position: [0.42, 0.3 + i * 0.13] }, { opacity: 1, position: [0.5, 0.3 + i * 0.13] }, { duration: '600ms', easing: 'easeOut' });
}));
$.wait('1.5s');

return $;
Compiling00:00

Overlapping with wait: false

Without parallel(), you can still overlap by setting wait: false on the longer animation. The pointer ignores it, so subsequent calls start immediately.

bg.animate({ scale: 1 }, { scale: 1.2 }, { duration: '3s', wait: false });
// 3s pulse runs in background, rest of the flow keeps its own rhythm
title.fadeIn('500ms');
$.wait('2s');
title.fadeOut('500ms');

Nesting

$.parallel([...]) can be nested inside another parallel branch. Each level creates a new scope for its wait calls.

$.parallel([
  () => title.fadeIn('500ms'),
  () => $.parallel([
    () => sub1.fadeIn('500ms'),
    () => { $.wait('200ms'); sub2.fadeIn('500ms'); },
  ]),
]);
Rule of thumb. If two animations need to happen at the same time on the timeline, they belong in the same parallel() call. If one is a backdrop effect that shouldn't block, wait: false is simpler.
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.