--- title: Input handling created_at: 12/20/2025 updated_at: 12/20/2025 keywords: - input - pointer - keyboard - InputsManager - events reviewed_by: steveruizok status: published date: 12/20/2025 readability: 9 voice: 8 completeness: 9 accuracy: 9 notes: 'Passive constructions ("is calculated", "are added") and formulaic transition ("For each space"). Add API links like [InputsManager](?).' --- The [InputsManager](?) class tracks pointer and keyboard state for the editor. It stores pointer positions in both screen space and page space, tracks pressed keys and buttons, detects device types (mouse, touch, pen), and calculates pointer velocity. Access it through `editor.inputs`. All input state is reactive. The manager stores values as atoms from `@tldraw/state`, so components that read input state automatically update when those values change. The manager updates on every input event, converting coordinates between screen space and page space. ## Pointer position tracking The manager tracks pointer positions in two coordinate spaces. Screen space is pixels relative to the canvas container's origin. Page space is the position on the infinite canvas, adjusted for camera position and zoom. In each space, the manager keeps three positions: current, previous, and origin. ### Current and previous positions ```typescript editor.inputs.getCurrentScreenPoint() // Current position in screen space editor.inputs.getCurrentPagePoint() // Current position in page space editor.inputs.getPreviousScreenPoint() // Previous position in screen space editor.inputs.getPreviousPagePoint() // Previous position in page space ``` The current position updates on every pointer event. The previous position stores where the pointer was before the most recent update. You can use these together to calculate deltas for dragging and panning: ```typescript const delta = Vec.Sub(editor.inputs.getCurrentPagePoint(), editor.inputs.getPreviousPagePoint()) ``` ### Origin positions The origin position captures where the most recent `pointer_down` event occurred: ```typescript editor.inputs.getOriginScreenPoint() // Where pointer_down occurred in screen space editor.inputs.getOriginPagePoint() // Where pointer_down occurred in page space ``` Tools use the origin to calculate drag distances and determine whether an interaction has moved far enough to trigger behaviors like dragging. The origin resets on every `pointer_down` event and continuously while a pinch is in progress. ### Coordinate space conversion The manager converts screen coordinates to page coordinates using the camera's position and zoom: ```typescript // Screen to page conversion const pageX = screenX / camera.z - camera.x const pageY = screenY / camera.z - camera.y ``` ## Pointer velocity The manager tracks pointer velocity for gesture detection: ```typescript editor.inputs.getPointerVelocity() // Vec with x/y velocity in pixels per millisecond ``` The manager listens to the editor's `frame` event and recomputes velocity once per frame (not on each pointer event) from the screen-space distance traveled since the previous frame. It smooths the result against the previous value and clamps components below 0.01 to zero to prevent jitter. Tools use velocity to distinguish between slow, precise interactions and fast flick gestures. Velocity resets to zero on `pointer_down` events and continuously while a pinch is in progress. ## Input device detection The manager tracks whether the most recent pointer event came from a pen (`pointerType === 'pen'`): ```typescript editor.inputs.getIsPen() // true for stylus input ``` Pen mode ignores non-pen input to prevent accidental touch interactions while using a stylus. The editor only turns pen mode on automatically for direct-display pens (Apple Pencil, Surface Pen), flagged as `isPenDirect` on the pointer event; desktop graphics tablets still draw as pens without enabling it. ## Modifier keys and button states The manager tracks modifier key states: ```typescript editor.inputs.getShiftKey() editor.inputs.getAltKey() editor.inputs.getCtrlKey() editor.inputs.getMetaKey() editor.inputs.getAccelKey() // Cmd on Mac, Ctrl elsewhere ``` The `getAccelKey()` method returns true for Command on macOS and Control on other platforms. Use this for cross-platform shortcuts. ### Button tracking The manager tracks currently pressed pointer buttons in a reactive set: ```typescript editor.inputs.buttons.has(0) // Primary button (left click) editor.inputs.buttons.has(1) // Middle button editor.inputs.buttons.has(2) // Secondary button (right click) ``` Buttons are added on `pointer_down` events and removed on `pointer_up` events. ### Keyboard key tracking The manager tracks pressed keyboard keys in a reactive set: ```typescript editor.inputs.keys.has('Space') editor.inputs.keys.has('ShiftLeft') ``` The editor adds keys on `key_down` and removes them on `key_up`. Tools can use this to detect held keys during pointer operations. For example, the editor checks `keys.has('Space')` on pointer up to decide whether to keep spacebar panning active. ## Interaction state flags The manager tracks the current interaction state: ```typescript editor.inputs.getIsPointing() // Pointer button is down editor.inputs.getIsRightPointing() // Right button is down, before the drag threshold editor.inputs.getIsDragging() // Pointer moved beyond drag threshold while pointing editor.inputs.getIsPinching() // Two-finger pinch gesture active editor.inputs.getIsEditing() // Editing text or other content editor.inputs.getIsPanning() // Panning the canvas editor.inputs.getIsSpacebarPanning() // Panning via spacebar (vs. other panning modes) ``` The editor sets these flags during event processing. For example, `isPointing` becomes true on `pointer_down` and false on `pointer_up`. The `isDragging` flag becomes true when the pointer moves beyond the drag distance threshold (`dragDistanceSquared` or `coarseDragDistanceSquared` in the editor's [options](/sdk-features/options)) while pointing. ## Event processing flow When an input event occurs, the editor processes it through these stages: 1. The browser fires a native DOM event 2. The canvas event handlers transform it into a typed event info object and call [Editor#dispatch](?) 3. `pointer_move`, `wheel`, and `pinch` events are queued and flushed once per frame; other events flush immediately 4. The editor emits `before-event`, then updates modifier key state 5. For pointer, pinch, and wheel events, the editor calls `updateFromEvent()` on the `InputsManager` 6. For pointer events, the editor updates buttons and interaction flags, then hands the event to the `ClickManager` for double-click detection 7. The editor sends the event to the state machine via `root.handleEvent()`, which propagates it through active tool states, then emits `event` See [Events](/sdk-features/events) for subscribing to `before-event` and `event`. The typed event info objects are: | Event type | Info type | | --------------- | --------------------- | | Pointer events | `TLPointerEventInfo` | | Click events | `TLClickEventInfo` | | Keyboard events | `TLKeyboardEventInfo` | | Wheel events | `TLWheelEventInfo` | | Pinch events | `TLPinchEventInfo` | In collaborative sessions, `updateFromEvent()` also updates the user's pointer presence record in the store, broadcasting pointer position to other users. ## Input normalization The editor listens to Pointer Events, so mouse, touch, and pen input arrive through the same handlers, and the manager tracks the device type through the `isPen` flag. Pointer positions include a `z` coordinate carrying the pointer event's pressure; synthetic events without a `z` default to 0.5. The manager subtracts the container's screen bounds from client coordinates, so the editor works correctly in nested layouts and scrolled containers. ## State serialization The manager provides a `toJson()` method for debugging: ```typescript const state = editor.inputs.toJson() // Returns all position vectors, modifier key states, interaction flags, // device type, and the contents of the keys and buttons sets ``` We use this serialized form when generating crash reports. ## Related examples - [Reactive inputs](/examples/editor-api/reactive-inputs) - Display pointer positions, velocity, and other input state reactively - [Canvas events](/examples/events/canvas-events) - Log pointer, keyboard, and wheel events to see the event flow