Understanding the Coordinate System for Zoom Focus Points in OpenScreen

OpenScreen utilizes a normalized 0–1 coordinate system for zoom focus points where (0,0) represents the top-left corner and (1,1) the bottom-right, clamping values to safe margins based on zoom depth to prevent viewing outside video bounds.

The open-source video editor OpenScreen (siddharthvaddem/openscreen) implements a resolution-independent approach to pan-and-zoom functionality through a normalized coordinate system for focus points. This design ensures that zoom focus points remain valid regardless of video resolution or UI stage size, with robust clamping mechanisms to keep the visible viewport within content boundaries.

Normalized Coordinate System for Zoom Focus Points

OpenScreen defines a zoom focus point using the ZoomFocus interface, which employs normalized coordinates rather than pixel values.

The ZoomFocus Interface

In src/components/video-editor/types.ts, the interface is defined as:

interface ZoomFocus {
  cx: number; // normalized horizontal center (0-1)
  cy: number; // normalized vertical center (0-1)
}

The cx and cy values represent fractions of the underlying video or stage dimensions. A value of 0 corresponds to the extreme left or top edge, while 1 represents the extreme right or bottom edge. The center of the frame sits at (0.5, 0.5). Because these values are normalized, the same focus coordinates work consistently across different video resolutions and display sizes without requiring recalculation.

Why Clamping Is Required

When users zoom in, the visible window becomes smaller relative to the content. If the focus point were allowed to sit arbitrarily close to the edge, portions of the viewport would fall outside the video bounds, producing blank areas or black bars. To prevent this visualization error, OpenScreen clamps the focus point based on the current zoom depth (a discrete level from 1 to 6) and its associated scale multiplier.

How OpenScreen Clamps Zoom Focus Points

The clamping system operates through a pipeline of margin calculations and boundary enforcement functions located in src/components/video-editor/videoPlayback/focusUtils.ts.

Depth-Based Scale Mapping

Each ZoomDepth level maps to a specific multiplier stored in the ZOOM_DEPTH_SCALES array, defined in src/components/video-editor/types.ts (lines 56-63). These scales determine how aggressively the viewport magnifies the content, which directly affects how much margin must be preserved at the edges.

Margin Calculation with getFocusBoundsForScale

For any given scale, the allowed focus region shrinks by a margin calculated as 1 / (2 × scale) on every side. The getFocusBoundsForScale function (lines 42-51 in focusUtils.ts) implements this logic, returning the valid minimum and maximum bounds for both cx and cy at the specified zoom level.

Stage-Level and Scale-Level Clamping

OpenScreen provides two primary clamping functions:

  • clampFocusToStage (lines 54-66): First constrains raw focus values to the generic [0, 1] range, then applies depth-specific bounds from getFocusBoundsForScale to ensure the focus stays within safe margins for the current zoom depth.

  • clampFocusToScale (lines 68-78): Performs the same boundary enforcement using a raw numeric zoomScale rather than a discrete depth level, useful for temporary zoom values during animations or pinch-to-zoom gestures.

Soft Clamping for Smooth UX

To prevent jarring snaps when users drag near boundaries, softenFocusToScale (lines 81-96 in focusUtils.ts) implements soft clamping. This function applies gentle easing near the margins, allowing the focus point to slide smoothly rather than hitting hard stops at the edge.

All clamping functions rely on a utility clamp function (lines 74-77 in types.ts) that safely handles NaN values and out-of-range inputs.

Implementation Examples

Clamping a Focus Point to Current Zoom Depth

When processing user input from drag operations, clamp the raw coordinates to safe bounds:

import {
  clampFocusToStage,
} from '@/components/video-editor/videoPlayback/focusUtils';
import { DEFAULT_ZOOM_DEPTH } from '@/components/video-editor/types';

const rawFocus = { cx: 0.95, cy: 0.03 }; // User dragged near corner

// Clamp based on current depth (e.g., depth = 3)
const safeFocus = clampFocusToStage(rawFocus, 3, { width: 1280, height: 720 });
console.log(safeFocus); // => { cx: 0.78, cy: 0.22 } (example values)

Using Custom Zoom Scales

For non-discrete zoom animations, use clampFocusToScale:

const customScale = 2.4; // During pinch-zoom animation
const clamped = clampFocusToScale(rawFocus, customScale);
// Returns focus constrained to safe margins for scale 2.4

Converting Stage Coordinates to Video Space

Transform normalized focus points to actual video pixel coordinates:

import { stageFocusToVideoSpace } from '@/components/video-editor/videoPlayback/focusUtils';

const stageSize = { width: 800, height: 600 };
const videoSize = { width: 1920, height: 1080 };
const baseScale = 1.5;
const baseOffset = { x: 100, y: 50 };

const videoSpaceFocus = stageFocusToVideoSpace(
  rawFocus,
  stageSize,
  videoSize,
  baseScale,
  baseOffset,
);
// Conversion logic resides in focusUtils.ts lines 98-124

Summary

  • OpenScreen employs a normalized 0–1 coordinate system for zoom focus points defined in src/components/video-editor/types.ts, enabling resolution-independent focus positioning.
  • The clamping mechanism prevents viewport bleed by calculating safe margins based on the current zoom scale using getFocusBoundsForScale.
  • clampFocusToStage enforces boundaries for discrete zoom depths, while clampFocusToScale handles arbitrary numeric scales for animations.
  • Soft clamping via softenFocusToScale provides smooth UX by easing focus movements near boundaries rather than applying hard cuts.
  • All boundary calculations reside in src/components/video-editor/videoPlayback/focusUtils.ts, with type definitions and utility functions in types.ts.

Frequently Asked Questions

What is the valid range for zoom focus point coordinates in OpenScreen?

Zoom focus points use normalized coordinates where both cx (horizontal) and cy (vertical) range from 0 to 1, inclusive. A value of 0 represents the left or top edge, 1 represents the right or bottom edge, and 0.5 represents the exact center of the video or stage. These normalized values allow the same focus coordinates to work across different video resolutions without modification.

How does OpenScreen prevent the zoom viewport from showing areas outside the video?

OpenScreen calculates safe margins based on the current zoom scale using the formula 1 / (2 × scale) and clamps focus points within these bounds. The clampFocusToStage function in focusUtils.ts first ensures values stay within [0, 1], then applies depth-specific constraints to guarantee the zoomed viewport never extends beyond the video edges, eliminating blank spaces or black bars.

What is the difference between clampFocusToStage and clampFocusToScale?

clampFocusToStage accepts a discrete ZoomDepth level (1–6) and uses the predefined scale mappings in ZOOM_DEPTH_SCALES to determine clamping boundaries. clampFocusToScale accepts a raw numeric zoomScale directly, making it suitable for temporary zoom values during pinch gestures or smooth zoom animations where the scale doesn't correspond to a discrete depth level. Both functions ultimately rely on getFocusBoundsForScale to calculate safe margins.

Why does OpenScreen implement soft clamping for zoom focus points?

Soft clamping via softenFocusToScale applies mathematical easing near the boundaries rather than hard truncation, preventing jarring visual snaps when users drag the focus point toward the edges of the frame. This creates a smoother user experience by allowing the focus to glide gently toward its limit instead of stopping abruptly, while still ensuring the viewport remains within valid video bounds.

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 →