How OpenScreen Speed Regions Enable Variable Playback Speed from 0.25x to 2x

OpenScreen implements speed regions as a first-class editing primitive that adjust the HTMLVideoElement playbackRate during live playback and recalculate effective duration during export to support variable speeds between 0.25× and 2×.

OpenScreen, an open-source video editor by siddharthvaddem, treats speed regions as core editing primitives that allow precise control over variable playback speed. Unlike simple global speed adjustments, these regions enable users to apply different speeds to specific time segments while maintaining accurate export rendering. This article examines the three-layer architecture—data model, live playback engine, and export pipeline—that makes variable playback speed from 0.25× to 2× possible.

What Are Speed Regions in OpenScreen?

Speed regions are time-bound segments defined by start and end timestamps that carry an explicit speed multiplier. They function independently of trim regions, allowing non-destructive speed adjustments that apply to both preview playback and final exports.

The SpeedRegion Data Model

At the core of the feature lies the SpeedRegion interface defined in src/components/video-editor/types.ts. This simple structure stores the temporal boundaries and desired velocity:

// src/components/video-editor/types.ts (lines 137-142)
interface SpeedRegion {
  id: string;
  startMs: number;
  endMs: number;
  speed: number;  // 0.25 to 2.0
}

The application constrains user-selectable speeds through the SPEED_OPTIONS constant (lines 44-51), which enumerates discrete steps from 0.25× (displayed in the UI as "0.2Sx") up to 2×. While the playback engine accepts any positive number, this predefined list ensures consistent UX across the timeline editor.

How Variable Playback Speed Works During Live Playback

During normal playback, OpenScreen dynamically adjusts the native video playback rate by looking up which speed region—if any—contains the current playhead position.

Finding the Active Speed Region

The createVideoEventHandlers hook utilizes the findActiveSpeedRegion helper to perform a temporal lookup. Located in src/components/video-editor/videoPlayback/videoEventHandlers.ts (lines 46-53), this function iterates through the speed regions array to identify which region contains the current timestamp in milliseconds.

Setting playbackRate on the HTMLVideoElement

Once identified, the hook applies the speed directly to the DOM element:

// src/components/video-editor/videoPlayback/videoEventHandlers.ts (lines 73-76)
const activeSpeedRegion = findActiveSpeedRegion(currentTimeMs);
video.playbackRate = activeSpeedRegion ? activeSpeedRegion.speed : 1;

Because the HTMLVideoElement's playbackRate property accepts any positive floating-point number, OpenScreen applies the user-selected speed (0.25–2.0) without intermediate conversion layers. This ensures immediate visual feedback when the playhead crosses into a new speed region during preview.

Speed Regions in the Export Pipeline

While live playback relies on browser-native speed control, the export pipeline must manually account for speed regions when calculating frame counts and timestamps. The StreamingVideoDecoder class handles these transformations to ensure the rendered video matches the preview.

Calculating Effective Duration with splitBySpeed

The export process begins by computing effective duration—the real-time length the exported video will occupy after speed adjustments. In src/lib/exporter/streamingDecoder.ts, the getEffectiveDuration method (lines 83-88) first computes trim segments, then splits them by speed regions:

// src/lib/exporter/streamingDecoder.ts (lines 90-118)
splitBySpeed(trimSegments: Segment[], speedRegions: SpeedRegion[]) {
  // Splits each trimmed segment into sub-segments carrying explicit speed values
  // Returns segments with adjusted (endSec - startSec) / speed duration multipliers
}

For each sub-segment, the algorithm divides the real duration by the speed multiplier ((endSec - startSec) / speed). A 10-second segment at 0.5× speed contributes 20 seconds to the effective duration, while 2× speed contributes only 5 seconds.

Frame Count Calculations for Variable Speed

The VideoExporter class uses this effective duration to determine how many frames to render. As implemented in src/lib/exporter/videoExporter.ts (lines 55-58), the total frame count derives from:

// src/lib/exporter/videoExporter.ts
const totalFrames = Math.ceil(effectiveDuration * frameRate);

By calculating frames based on speed-adjusted duration rather than source duration, OpenScreen ensures that slow-motion segments render with sufficient frames for smooth playback and fast-motion segments skip appropriately without dropping essential frames.

Configuring Speed Regions Programmatically

Developers can create and manipulate speed regions directly through the TypeScript API.

Defining a Speed Region

import { SpeedRegion } from "@/components/video-editor/types";

const slowMotionRegion: SpeedRegion = {
  id: "sr-01",
  startMs: 10_000,   // Start at 10 seconds
  endMs: 20_000,     // End at 20 seconds
  speed: 0.25,       // 0.25× playback (displayed as "0.2Sx")
};

Adding Regions via Timeline Interface

The TimelineEditor component (located in src/components/video-editor/timeline/TimelineEditor.tsx) handles user interactions. When a user presses the "S" key or clicks the speed region row (SPEED_ROW_ID), the component generates a new region:

const addSpeedRegion = () => {
  const newRegion: SpeedRegion = {
    id: uuid(),
    startMs: playheadMs,
    endMs: playheadMs + 5_000,
    speed: 2,  // 2× speed
  };
  onAddSpeedRegion(newRegion);
};

Summary

OpenScreen's variable playback speed system operates through three coordinated mechanisms:

  • Data Model: The SpeedRegion interface in types.ts stores temporal boundaries and speed values between 0.25× and 2×.
  • Live Playback: The videoEventHandlers.ts hook uses findActiveSpeedRegion to update video.playbackRate in real-time as the playhead moves between regions.
  • Export Pipeline: StreamingVideoDecoder.splitBySpeed calculates effective duration by dividing segment lengths by their respective speeds, while VideoExporter uses this data to compute accurate frame counts.

Frequently Asked Questions

What speed range does OpenScreen support?

OpenScreen supports variable playback speeds from 0.25× (displayed in the UI as "0.2Sx") to 2×. The SPEED_OPTIONS array in src/components/video-editor/types.ts defines the discrete steps available in the interface, though the underlying HTMLVideoElement technically supports any positive value passed programmatically.

How does OpenScreen handle overlapping speed regions?

According to the findActiveSpeedRegion implementation in videoEventHandlers.ts, the system selects the first matching region found at the current timestamp. The timeline UI (TimelineEditor.tsx) typically prevents overlapping speed regions through its drag-and-drop validation logic, ensuring clean, contiguous speed adjustments.

Are speed regions preserved when exporting video?

Yes. Unlike simple playback adjustments, speed regions permanently affect the exported output. The splitBySpeed method in src/lib/exporter/streamingDecoder.ts ensures that the renderer produces the correct number of frames for each speed-adjusted segment, so a 0.5× region exports as genuine slow-motion video rather than slowed audio with dropped frames.

Where is the speed region data stored?

Speed region definitions persist within the project's metadata as SpeedRegion objects, stored alongside trim regions and other edit decisions. The interface definition in src/components/video-editor/types.ts serves as the source of truth for the data structure used across the playback, persistence, and export layers.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →