Back to Tldraw

Pen mode

apps/docs/content/sdk-features/pen-mode.mdx

5.4.03.8 KB
Original Source

Pen mode restricts canvas input to stylus events. When a stylus touches a touch-capable screen, like an Apple Pencil on an iPad, the editor turns on pen mode and ignores other pointer input. Users can rest their palm on the screen while drawing without triggering accidental touches.

tsx
import { Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'

export default function App() {
	return (
		<div style={{ position: 'fixed', inset: 0 }}>
			<Tldraw
				onMount={(editor) => {
					if (editor.getInstanceState().isPenMode) {
						editor.updateInstanceState({ isPenMode: false })
					}
				}}
			/>
		</div>
	)
}

How pen mode activates

The editor detects stylus input from the browser's pointer events: any pointer with pointerType: 'pen' is treated as a pen. Pen mode only turns on for direct-display pens, though. When a pen touches the canvas on a touch-capable device, the editor sets isPenMode to true on the instance state and interrupts any touch interaction in progress. A stylus on a desktop graphics tablet still draws as a pen but doesn't enable pen mode, since there's no palm to reject.

Pen mode is session state. It doesn't persist across reloads, and it isn't synced between collaborators. There's no option to disable auto-enabling; if you don't want it, turn it back off with Editor#updateInstanceState.

Touch rejection

While pen mode is on, the editor drops pointer down, move, and up events from any non-pen input. Finger touches and mouse clicks on the canvas do nothing. This is what makes palm rejection work: a hand resting on a tablet screen produces touch events, and pen mode filters them all out.

The rest of the UI still works normally. Pen mode only filters events on the canvas, so users can still tap toolbar buttons and menus with a finger. Click detection follows the same rule: in pen mode only pen events produce double-clicks.

Exiting pen mode

While pen mode is active, the default UI shows an "Exit pen mode" helper button over the canvas. Clicking it runs the exit-pen-mode action, which sets isPenMode back to false.

You can do the same from code:

tsx
editor.updateInstanceState({ isPenMode: false })

There's no way to turn pen mode off with a finger on the canvas itself, since that input is rejected. If your custom UI replaces the default one, keep the helper button or an equivalent control available.

Pressure sensitivity

Pen pressure arrives on pointer events as the z component of the pointer's position, taken from the browser's PointerEvent.pressure value (0 to 1).

The draw tool uses this pressure to vary stroke thickness. When the input reports real pressure (a pen pointer with non-zero pressure, or any pointer whose pressure isn't a constant 0, 0.5, or 1), the resulting draw shape records isPen: true in its props and stores scaled pressure in its points. Strokes drawn with a mouse or finger use simulated pressure instead. The highlighter records isPen the same way but renders at constant width.

tsx
// Read the current pointer's pressure
const { z } = editor.inputs.getCurrentPagePoint()

// Check whether the most recent pointer event came from a pen
const isPen = editor.inputs.getIsPen()

Stylus erasers

Some styluses, like the Surface Pen or Wacom pens, have a hardware eraser. When the editor receives a pointer down from an eraser button (button 5), it switches to the eraser tool, then restores the previous tool when the eraser lifts. This works whether or not pen mode is on.

  • Instance state — Where isPenMode lives, along with other session state
  • Input handling — How the editor processes pointer events
  • Draw shape — The freehand shape that records pen pressure