CAIRN
An invented insulated flask brand. One scroll drives a 3D product story, and the same page holds a working bag.
The problem it answers
Scroll-driven product pages usually run several animations at once and spend their life fighting to stay in sync. Stop halfway through a section and the camera says one thing while the copy says another. This page cannot do that, because everything on it is a function of a single number.
Decisions worth explaining
One timeline, not five animations. Scroll position becomes one number in act units — act index plus progress through that act. Camera, explode distance, spin, scale, copy opacity and the temperature readouts are all functions of it. Acts hand off at whole numbers, which is why nothing snaps at a boundary.
The flask is geometry, not a photograph. Four lathe profiles, a torus for the seal, and a canvas gradient turned into an environment map at runtime. No model file, no image sequence, nothing to download but the library. Changing colourway lerps the material and sweeps the key light, so the switch reads as a relight rather than a repaint.
The cooling act is a model, not a canned animation. Newton's law of cooling with two time constants, one for the vacuum wall and one for a single-wall bottle. Scrub the hours and both numbers are computed live — which is exactly why the same function can be asserted in a test.
Commerce rules are pure and tested. Stock per variant, quantity capped at what is really left including what is already in the bag, sold-out colourways struck through rather than removed, money formatted through Intl.NumberFormat. Thirty-nine assertions cover it, in the browser or under Node.
Craft notes
- Reduced-motion visitors get a static layout with the same content and the same bag.
- No build step. three.js loads from a CDN; everything else is three files and some maths.
