VideoFlowcodeGitHubStudioTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubStudioTry 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 EditorPlaygroundStudio

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContactTermsPrivacy

From the blog

All posts →Markdown to MP4: Automating Technical Explainer Videos with VideoFlowHow to Render MP4s in Node.js Without FFmpegResolution-Agnostic Video: Why Your Code Should Render 4K and 720p IdenticallyScaling Personalized Video with Zero Server CostMastering Cinematic Compositing: A Guide to VideoFlow BlendModesJSON is the New MP4: Building a Video-First SaaSThe Multi-Language Video Stack: Generating MP4s from Python, Go, and RustVideoFlow vs. Remotion: Why Portable JSON is the Future of Programmatic Video
© 2026 VideoFlow. Apache-2.0 core.