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 withdurationandeasingimmediate- move the camera immediately rather than on the next tickforce- move the camera even whenisLockedis 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.
Related examples
- 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
zoomToBoundsmethod. - Scrollable container - Use the editor inside a scrollable container with proper mousewheel event handling.