case study
Air Canvas
- landmarks
- 21
- brush sizes
- 3
- MediaPipe
- Canvas API
- React
- Vite
- Tailwind CSS
Draw in the air with your finger — hand-tracked painting that runs fully in the browser with no backend. Index finger draws, two fingers lift the pen, and PNG export un-mirrors your writing so it reads correctly.
The entire interface is two finger positions
There is no gesture model in this one. Whether a finger is up is decided by comparing two of its own landmarks (the tip against the middle joint), and the whole control scheme is built from two of those tests: index finger alone draws, index and middle together lift the pen so you can move without leaving a line. That is deliberately the simplest thing that works. A trained classifier would be a heavier dependency, another failure mode, and a worse fit, because what the interface needs is not recognition of a named pose but a binary that flips crisply while your hand is moving.
The fingertip wobbles even when the hand is still
Hand tracking is good enough that the tracking is not the hard part. The fingertip position it returns wobbles by a few pixels frame to frame even when a hand is held still, and a line drawn straight from those coordinates looks like it was drawn during an earthquake. The pen position is therefore an exponential moving average of the raw one, weighted 0.55 toward where it already was. That number is a straight trade: higher and strokes are smoother but the pen lags behind the finger, lower and it tracks tightly but reproduces the shake.
Mirrored to draw, un-mirrored to save
The front-camera view is mirrored so that moving your hand right moves the pen right. Without it, drawing is an exercise in reversing your own movements. But the moment you export the picture, that same flip is wrong: anything you wrote comes out backwards. So the save path draws the canvas through a horizontal flip before encoding the PNG, and the exported file reads correctly while the live view stays intuitive.
The unglamorous half
A camera reports one resolution when the stream opens and often a different one a moment later when it settles, which resizes the canvas. Resizing a canvas clears it, so a stroke drawn in the first second used to vanish. The drawing is now copied into a scratch canvas and drawn back at the new size. Beyond that this project shares its sibling's lessons deliberately rather than accidentally: the GPU delegate falls back to CPU instead of failing setup and blaming the network, the WASM runtime and model are self-hosted so nothing is fetched from a CDN at runtime, and the theme token block is the same shape as the one in the gesture demo and in Lekh Patro so the three read as one family. The colour and brush clusters are grouped and labelled for screen readers, the white swatch gains a border so it stays visible on the light toolbar, and reduced motion is honoured.