Medium Pool · every medium your app could speak
$npx skills add funclosure/medium-pool

Drawing

Marks a hand makes
messageThe hand thinks, and the app is the surface it thinks on.
instance SketchpadDraw, annotate, whiteboard

Drawing is the freeform medium: sketching, annotation, whiteboards, signatures. The input is a pen, a finger, or a mouse, and the output is whatever you render. It is the medium that feels most like a physical tool, so latency and pressure fidelity decide whether it feels good.

The 2D canvas API is small, but the input layer around it (Pointer Events, coalesced events, pressure, palm rejection) is where the craft is.

Reach for it when

  • people need to point at or mark up something spatially
  • the output is a picture, a sketch, or a signature
  • structure would get in the way of a quick gesture
direction
in (the person gives it)
substrate
Pointer Events with pressure · 2D canvas

Where it bites

  • Use Pointer Events, not mouse or touch events, so pen, finger, and mouse share one code path.
  • Set touch-action: none on the canvas or the page will scroll instead of drawing.
  • Scale the backing store by devicePixelRatio or strokes look blurry on every modern screen.
  • Read getCoalescedEvents() during fast strokes or you get polygons instead of curves.
  • Keep strokes as data (points, pressure, color) so undo, resize, and export are replays, not pixel surgery.

Instances of this medium

Each is one way to build it. None of them is the medium.

Sketchpadin the pool

Draw, annotate, whiteboard. reference/Sketchpad.ts

Excalidrawopen source

Hand-drawn style whiteboard; the state of the art for collaborative sketching.

perfect-freehandopen source

Pressure-sensitive stroke outlines from pointer input. The math behind brushes that feel right.

tldrawopen source

Infinite canvas SDK with a full editor on top.

$npx skills add funclosure/medium-pool --skill medium-drawing
skills/medium-drawing/SKILL.md
---
name: medium-drawing
description: "Build freeform drawing, annotation, and whiteboard surfaces on the web with Pointer Events and 2D canvas. Use when adding sketching, markup, or signatures. For data-driven node-link graphs and charts use medium-visualization instead."
---

# Drawing as a medium

## What this medium does to the person
Drawing makes the app a surface the hand thinks on. Latency and pressure fidelity decide whether it feels like a tool or a form; structure gets out of the way of the gesture.

## When to reach for this medium
Choose drawing when people need to mark, point, sketch, or sign, and when structure would slow the gesture down. If the picture is computed from data rather than drawn by a hand, that is visualization, not drawing.

## Core browser APIs
- Pointer Events (`pointerdown`/`move`/`up`) with `setPointerCapture` and `getCoalescedEvents()`
- `pressure`, `tiltX`/`tiltY`, and `pointerType` for pen-aware rendering
- 2D canvas with a `devicePixelRatio`-scaled backing store; `ResizeObserver` to refit

## UX rules that always apply
- Strokes appear under the pointer with no visible lag; render incrementally, redraw fully only on undo or resize.
- Pressure changes the width on pen and touch; mouse gets a steady width.
- Undo and Clear are always visible. Undo removes the last stroke, not the last pixel.
- Keep strokes as data so resize, export, and sync are replays.

## Reference instances
Each is one way to speak this medium, cited as evidence. None of them is the medium.

`reference/Sketchpad.ts` — pressure-aware brush, coalesced-event smoothing, swatches and size, undo and clear from a stroke list, DPR-correct backing store. It uses a few layout class names from the pool's stylesheet (`row`, `btn`, `status`, `frame`); without that stylesheet the logic still runs and the controls are unstyled.

## Gotchas
- `touch-action: none` on the surface or the page scrolls.
- Without DPR scaling everything looks blurry.
- Never store only pixels; you lose undo and resolution.