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

> Discover how OpenScreen speed regions enable variable playback speed from 0.25x to 2x. Learn how this editing primitive adjusts playback and recalculates duration for seamless high-quality video.

- Repository: [Sid/openscreen](https://github.com/siddharthvaddem/openscreen)
- Tags: deep-dive
- Published: 2026-04-03

---

**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`](https://github.com/siddharthvaddem/openscreen/blob/main/src/components/video-editor/types.ts). This simple structure stores the temporal boundaries and desired velocity:

```typescript
// 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`](https://github.com/siddharthvaddem/openscreen/blob/main/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:

```typescript
// 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`](https://github.com/siddharthvaddem/openscreen/blob/main/src/lib/exporter/streamingDecoder.ts), the `getEffectiveDuration` method (lines 83-88) first computes trim segments, then splits them by speed regions:

```typescript
// 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`](https://github.com/siddharthvaddem/openscreen/blob/main/src/lib/exporter/videoExporter.ts) (lines 55-58), the total frame count derives from:

```typescript
// 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

```typescript
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`](https://github.com/siddharthvaddem/openscreen/blob/main/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:

```typescript
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`](https://github.com/siddharthvaddem/openscreen/blob/main/types.ts) stores temporal boundaries and speed values between 0.25× and 2×.
- **Live Playback**: The [`videoEventHandlers.ts`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/videoEventHandlers.ts), the system selects the first matching region found at the current timestamp. The timeline UI ([`TimelineEditor.tsx`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/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`](https://github.com/siddharthvaddem/openscreen/blob/main/src/components/video-editor/types.ts) serves as the source of truth for the data structure used across the playback, persistence, and export layers.