Camera system

The camera system controls how users view and navigate the infinite canvas. It manages viewport position and zoom level, and transforms coordinates between screen space and page space. The editor uses these transformations to map mouse positions to canvas locations and render shapes at any zoom level.

The camera handles user input for panning and zooming, supports constraints for bounded experiences, and provides methods for programmatic movement with smooth animations. It also integrates with collaboration features for real-time viewport following.

How it works

The camera represents the viewport's position and zoom using three values: x and y for position in page space, and z for zoom level. A zoom of 1 means 100%, 0.5 is 50%, and 2 is 200%. The camera's x and y coordinates represent the top-left corner of the viewport in page coordinates.

The camera transforms between two coordinate spaces:

  • Screen space - Browser pixels from the document origin
  • Page space - The infinite canvas coordinate system

The screenToPage() method converts mouse positions to canvas coordinates, while pageToScreen() converts canvas coordinates to screen positions:

const pagePoint = editor.screenToPage({ x: event.clientX, y: event.clientY })
const screenPoint = editor.pageToScreen({ x: shape.x, y: shape.y })

The camera responds to user input through mouse wheel, trackpad gestures, keyboard shortcuts, and touch events. The wheelBehavior option determines whether scrolling pans or zooms the viewport.

Camera options

Camera behavior is configured through TLCameraOptions:

editor.setCameraOptions({
	isLocked: false,
	wheelBehavior: 'pan',
	panSpeed: 1,
	zoomSpeed: 1,
	zoomSteps: [0.1, 0.25, 0.5, 1, 2, 4, 8],
})

The isLocked option prevents all camera movement, useful for fixed-viewport experiences.

The wheelBehavior option determines how mouse wheel or trackpad scroll affects the viewport: 'pan' for navigating large diagrams, 'zoom' for detail work, or 'none' to disable wheel interaction.

The panSpeed and zoomSpeed multipliers adjust input sensitivity. Values below 1 slow down movement, values above 1 speed it up.

The zoomSteps array defines discrete zoom levels. The first value sets minimum zoom, the last sets maximum zoom, and intermediate values determine snap points for zoom controls.

Camera constraints

Camera constraints limit where users can navigate. Use them for presentations, guided experiences, or applications with fixed content areas:

editor.setCameraOptions({
	constraints: {
		bounds: { x: 0, y: 0, w: 1920, h: 1080 },
		padding: { x: 50, y: 50 },
		origin: { x: 0.5, y: 0.5 },
		initialZoom: 'fit-min',
		baseZoom: 'default',
		behavior: 'inside',
	},
})

The bounds define the constrained area in page space. The camera restricts panning outside this rectangle based on the behavior setting.

The padding adds screen space margin inside the viewport, preventing content from touching the screen edges.

The origin determines how bounds are positioned within the viewport when using 'fixed' behavior. Values of { x: 0.5, y: 0.5 } center the bounds, while { x: 0, y: 0 } aligns to the top-left.

Zoom fitting

The initialZoom and baseZoom options control how content fits the viewport:

  • 'default': 100% zoom, showing content at actual size
  • 'fit-min': Fit the smaller axis, ensuring all bounds are visible
  • 'fit-max': Fit the larger axis, potentially cropping content
  • 'fit-x': Fit horizontally, filling the viewport width
  • 'fit-y': Fit vertically, filling the viewport height
  • 'fit-x-100': Fit horizontally or use 100%, whichever is smaller
  • 'fit-y-100': Fit vertically or use 100%, whichever is smaller
  • 'fit-min-100': Fit the smaller axis or use 100%, whichever is smaller
  • 'fit-max-100': Fit the larger axis or use 100%, whichever is smaller

The initialZoom sets the starting zoom when the camera resets. The baseZoom defines the reference point for zoom steps, affecting how zoom in/out operations scale relative to the viewport.

Constraint behaviors

The behavior option controls how bounds constrain camera movement:

  • 'free': Bounds are ignored, allowing unlimited panning
  • 'fixed': Bounds are positioned at the origin regardless of pan attempts
  • 'inside': Bounds must stay completely within the viewport
  • 'outside': Bounds must stay touching the viewport edges
  • 'contain': Uses 'fixed' when zoomed out and 'inside' when zoomed in

Set behavior per axis for asymmetric constraints:

behavior: {
  x: 'free',    // Horizontal panning unrestricted
  y: 'inside',  // Vertical panning keeps bounds visible
}

Camera methods

The editor provides methods for programmatic camera control. All methods accept an optional options object with:

  • animation - add smooth transitions with duration and easing
  • immediate - move the camera immediately rather than on the next tick
  • force - move the camera even when isLocked is true

Basic navigation

Move the camera to a specific position and zoom:

editor.setCamera({ x: -500, y: -300, z: 1.5 })

Center the viewport on a point:

editor.centerOnPoint({ x: 1000, y: 500 })

Zoom in or out. Both methods accept an optional screen point to zoom toward:

editor.zoomIn()
editor.zoomOut()
editor.zoomIn(editor.inputs.currentScreenPoint, { animation: { duration: 200 } })

Zoom to content

Focus the camera on shapes or bounds:

// Fit all shapes on the current page
editor.zoomToFit()

// Fit the current selection
editor.zoomToSelection()

// Reset zoom to 100% (or initial zoom if constraints are set)
editor.resetZoom()

// Fit specific bounds with padding
const bounds = { x: 0, y: 0, w: 1000, h: 800 }
editor.zoomToBounds(bounds, { inset: 100 })

The zoomToBounds method accepts inset to add padding around the bounds and targetZoom to limit the maximum zoom level.

Quick zoom navigation

The default tldraw UI includes a quick zoom tool activated by pressing z. This zooms out to show the entire canvas with a viewport brush indicating where you'll zoom to. Move the cursor to reposition the target viewport, then release to zoom to that location. Press Escape to cancel and return to the original view. This provides a fast way to navigate large canvases without losing your place.

Animated movement

Add smooth transitions using the animation option. The EASINGS object provides common easing functions:

import { EASINGS } from 'tldraw'

editor.setCamera(
	{ x: 0, y: 0, z: 1 },
	{
		animation: {
			duration: 500,
			easing: EASINGS.easeInOutCubic,
		},
	}
)

Camera animations stop automatically when users pan or zoom, ensuring user input takes precedence over programmatic movement. You can also stop camera animations at any time with the editor's stopCameraAnimation() method.

Momentum scrolling

Create momentum-based camera movement:

editor.slideCamera({
	speed: 2,
	direction: { x: 1, y: 0 },
	friction: 0.1,
	speedThreshold: 0.01,
})

The speed and direction control initial velocity. The friction value determines how fast the camera decelerates (higher friction stops movement faster). The speedThreshold sets the minimum speed before the animation stops completely. Use this for kinetic scrolling or to continue movement after gesture completion.

Collaboration features

The camera system integrates with collaboration through user following. When following another user, the camera tracks their viewport position and zoom.

User following respects the follower's viewport size. If aspect ratios differ, the system adjusts zoom to keep the followed user's content visible while maintaining the follower's viewport dimensions.

See User following for implementation details.

  • Camera options - Configure the camera's options and constraints including zoom behavior, pan speed, and camera bounds.
  • Image annotator - An image annotator that demonstrates how to configure camera options for fixed-viewport annotation apps.
  • Slideshow (fixed camera) - A simple slideshow app with a fixed camera using camera constraints.
  • Lock camera zoom - Lock the camera at a specific zoom level using the camera controls API.
  • Zoom to bounds - Programmatically zoom the camera to specific bounds using the editor's zoomToBounds method.
  • Scrollable container - Use the editor inside a scrollable container with proper mousewheel event handling.
Prev
Bindings
Next
Click detection