VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
← Back to Blog

The Parallel & Wait Playbook: Mastering Complex Video Timelines

August 22, 2026 · By VideoFlowMaster the core timing primitives of VideoFlow. Learn how to use $.parallel() and $.wait() to build sophisticated, multi-layered video compositions with code.The Parallel & Wait Playbook: Mastering Complex Video Timelines

The Parallel & Wait Playbook: Mastering Complex Video Timelines

Managing time in code-driven video often feels like a return to the dark ages of manual frame arithmetic. In most frameworks, if you want a title to appear 2.5 seconds after a background transition finishes, you're stuck calculating absolute timestamps or chaining complex promises. It’s fragile, hard to read, and even harder to refactor.

VideoFlow changes this by treating video composition as a structured flow. By mastering just two core primitives—$.wait() and $.parallel()—you can build sophisticated, multi-layered cinematic sequences without ever touching a calculator. In this playbook, we’ll explore how to move beyond sequential scripts and into high-performance video orchestration.

A futuristic digital timeline showing parallel orange execution flows

The Sequential Default

By default, the VideoFlow core builder API operates like a tape recorder. Every time you call a method that has a duration—like .fadeIn(), .animate(), or .fadeOut()—the internal flow pointer advances by that amount of time. Subsequent calls start exactly where the last one left off.

This makes simple "Intro -> Content -> Outro" sequences trivial to write:

const title = $.addText({ text: 'Hello World', opacity: 0 });

title.fadeIn('500ms');   // Pointer moves to 0.5s
$.wait('2s');            // Pointer moves to 2.5s
title.fadeOut('500ms');  // Pointer moves to 3.0s

But what happens when you want a background video to play throughout the entire sequence? Or when you want three different text elements to stagger their entrance? That's where the sequential model hits a wall, and where $.parallel() takes over.

Branching Out with $.parallel()

$.parallel([...]) allows you to run multiple execution branches starting from the same point in time. Each function inside the array gets its own local flow pointer. When the parallel block finishes, the master flow pointer jumps to the end of the longest branch.

Think of it as creating a multi-track timeline on the fly. This is the secret to creating the "staggered reveal" effect seen in professional motion graphics.

const lines = ['Build', 'Render', 'Deploy'].map((t, i) => 
  $.addText({ text: t, position: [0.5, 0.3 + i * 0.1], opacity: 0 })
);

$.parallel(lines.map((layer, i) => () => {
  $.wait(`${i * 200}ms`); // Stagger each start by 200ms
  layer.fadeIn('600ms');
}));

$.wait('1s'); // Wait for the whole group to be visible

By using $.wait() inside a parallel branch, you stagger the entry without affecting the start time of the other layers in that block. This pattern is essential for building automated social-media video factories where timing needs to be dynamic based on the content length.

Isometric diagram of code blocks assembling into a film strip

Background Effects with wait: false

Sometimes, you don't need a full parallel branch. If you have a long-running background effect—like a slow zoom on a background image—that shouldn't block the rest of your UI logic, you can use the wait: false option.

This tells VideoFlow to fire off the animation and immediately move the flow pointer to the next line of code, ignoring the animation's duration for flow purposes.

const bg = $.addImage({ fit: 'cover', scale: 1 }, { source: '...' });

// Background zoom starts but does NOT advance the pointer
bg.animate({ scale: 1 }, { scale: 1.2 }, { duration: '10s', wait: false });

// These calls happen while the background is still zooming
title.fadeIn('1s');
$.wait('2s');
title.fadeOut('1s');

How VideoFlow Handles This

Under the hood, VideoFlow compiles these imperative calls into a portable VideoJSON document. Because the timing logic is resolved at compile-time, the resulting JSON is resolution-agnostic and renderer-neutral.

Whether you are using @videoflow/renderer-browser for zero-cost client-side export or @videoflow/renderer-server for headless automation, the timing will be frame-accurate. This architecture is what enables the Three-Renderer Rule, allowing you to preview in the DOM at 60fps and render to MP4 using the exact same logic.

Summary: The Timing Hierarchy

To build maintainable video code, follow this hierarchy for timing:

  1. Sequential calls for elements that follow one another.
  2. wait: false for background loops or ambient effects that shouldn't block the flow.
  3. $.parallel() for complex, multi-layered scenes where elements need to overlap or stagger.

Ready to start building? Head over to the VideoFlow Playground to see these primitives in action, or dive into our Parallel and Wait guide for a deeper look at the implementation details. If you're building for production, don't forget to star the project 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 →Mastering Blend Modes: Creating Cinematic Visuals with CodeThe Parallel & Wait Playbook: Mastering Complex Video TimelinesRender MP4s in Node without FFmpeg: A Guide to Headless WebCodecs RenderingThe Three-Renderer Rule: Why Your Video Pipeline Needs a Browser, a Server, and a DOMProgrammatic Video Ads: Scaling Creative Production with JSON TemplatesBuilding a Canva for Video: Embeddable Editing in Your React AppResponsive Video Design: Why Pixels Are the Wrong Unit for Video AutomationZero-Server-Cost Video Rendering: Exporting MP4s in the Browser
© 2026 VideoFlow. Apache-2.0 core.