The whole workshop rests on one idea: three coordinate spaces, connected by two homographies.
corner pin (lesson 02) camera calibration (lesson 04)
TABLE mm ───────────────────────► PROJECTOR px CAMERA px ───────────────────► TABLE mm
(where you (what the (what the (back where
draw) projector shows) webcam sees) you draw)
| Space | Units | Origin | Who lives here |
|---|---|---|---|
| Table | millimetres | table’s top-left corner | your sketch, touches |
| Projector | pixels (e.g. 1920×1080) | top-left of the projector image | the browser window |
| Camera | pixels (e.g. 1280×720) | top-left of the webcam frame | tracking |
Your code only ever thinks in table space. The projector and camera are adapters at the edges.
When a pinhole device (projector or camera) looks at a flat surface, the mapping between surface coordinates and device pixels is a homography: a 3×3 matrix applied in homogeneous coordinates:
[x'] [h0 h1 h2] [x]
[y'] = [h3 h4 h5] [y] → (x'/w, y'/w)
[w ] [h6 h7 h8] [1]
h8 = 1 → 8 unknowns.That’s why every calibration step is “four corners”. lib/homography.js builds the 8×8 system and solves it with Gaussian elimination.
A homography maps straight lines to straight lines. It cannot correct lens distortion (barrel/fisheye bending) or non-flat surfaces. Those need more advanced calibration (see Going further).
CSS transform: matrix3d(...) is a 4×4 projective matrix. Our 3×3 homography slots into it (see toCSSMatrix3d), so the GPU warps the canvas for free, at full frame rate, with no pixel loops.
Built-in keystone correction usually handles only vertical tilt (sometimes horizontal), not arbitrary 4-corner placement. It also doesn’t tell your software where the table is. Software corner-pinning gives you exact millimetres and a mapping you can invert.
| Background difference: | frame − empty table | > threshold |
The camera sees the projector’s light. Anything you project changes the camera image, and the tracker can mistake it for an object. A projected ripple can trigger a new ripple, which triggers another…
| Approach | Cost | How it helps |
|---|---|---|
| Dim, sparse graphics + higher threshold | free | less projected change |
| Colour tracking | free | track a colour you never project |
| Re-capture background when scene is static | free | projected scene becomes “background” |
| Infrared camera + IR-pass filter + IR illuminator | ~¥3–10k | camera can’t see projector light at all |
| Depth camera | ~¥30–60k | measures height, not colour: true touch vs hover |
| Sync camera to projector blanking | hard | look only when the projector is dark |
This is a great discussion point. Every interactive projection system makes a choice here.
calibrateCamera) before the homography.