Tilt to explore
A motion layer for the whole site: the page reacts to how you are holding it. Mouse on a desktop, the phone’s gyroscope in a hand, and a shake that strips the finish off and shows the frame underneath.
What it is
Design and build, one file of JavaScript and one of CSS, no dependencies. Two evenings. It runs on every page of this site, including the one you are reading.
Three inputs — cursor, gyroscope, shake — normalise to one vector between −1 and 1. Everything downstream reads that vector and nothing else, so adding a fourth input later changes one function, not the site.
The constraint that shaped it
Almost nobody who visits this site is holding a phone. Over 21 July – 12 August 2026 the site’s own analytics counted 12% mobile — so a motion idea that only exists on a phone is a motion idea almost nobody sees.
- The desktop had to get the real thing, not a downgrade. The cursor is a tilt source, not a consolation prize.
- iOS will not give you the gyroscope without a tap. Since Safari 13 it needs an explicit permission call from a user gesture — so there is one small button, and it only appears on devices that actually have a sensor to ask about.
- Some people have asked their operating system for less movement. When they have, the module never initialises: no listeners, no loop, no buttons.
- A camera on arrival scares people off. Face tracking would have made a better demo and a worse website.
Depth, and the ceiling
Elements carry a depth between 0.2 and 1. The shift is the vector times the depth times twenty-two pixels — and twenty-two is a hard ceiling, not a starting point. Past it, “responsive” turns into “loose”.
Move the cursor across this page, or tilt the phone. The three cards have different depths; the numbers are the live vector.
Paragraph text never moves. Copy that drifts away from the cursor cannot be read, and the whole point of the layer is that every gesture does something useful. Covers, headings and decorative layers move. Sentences stay where they were put.
What the gestures do
Light
The vector is published as two CSS variables, and shadows read them directly — the shadow travels away from the tilt, as it would from a real light. No shadow is ever recalculated in JavaScript; that would repaint every card, every frame.
Focus
On the home page, holding a tilt to one side for 700 ms moves through the four role versions of the page. The gesture re-arms only when you come back to centre, so a cursor parked near the edge of a screen doesn’t sit there cycling the page forever.
Before and after
Where a case has the old screen as well as the new one, the tilt drives the seam between them. Below is the real Orange account screen against the rebuild.
Wireframe
Shake the phone, or press W. Images become grey boxes labelled with the size of the file that actually sits in them, components name themselves, and the padding shows as dashed lines. Shake again to put the finish back.
The seam
The full write-up is in Idea 002.
What I threw out
The camera
Face tracking as the default first screen. A permission prompt for the camera is where a portfolio visit ends.
Moving text
Parallax on paragraphs. It demos well and reads badly.
A 3D character
An avatar that follows the cursor. It would have meant a 3D library, and it would have been about me rather than about the work.
Libraries
No Three.js, no GSAP, no build step. The whole layer is smaller than one animation library’s licence header.
On a phone
Recorded on the device, because a desktop screen recording cannot show a gesture that is made of holding something.
Hold the phone still, then tilt it left and right: the covers lag behind the frame and the shadow runs the other way. Hold the tilt and the home page changes who it is written for. Then shake the phone — the site drops its finish and shows the frame underneath.
Where it stops
- Motion sickness is real. Twelve pixels and a heavy smoothing curve are the answer, plus a hard exit for anyone whose system asks for less movement. Neither is optional.
- Battery. The orientation sensor runs while the page is open. The loop stops the layers during scrolling and does no work when nothing has moved, but a sensor that is on is a sensor that costs.
- Reach. No gyroscope on a desktop, a permission prompt on iOS, nothing at all on a laptop trackpad. Every effect degrades to something that still works, and the site is complete without any of it.