There is a difference between a website that animates and a website that moves. The first kind tells you it is alive. The second kind makes you forget it isn't.
Most product UIs sit firmly in the first camp. Things slide, things fade, things bounce — usually for 200 milliseconds, usually with an ease-out — and the result is competent and forgettable. Cinema has spent a hundred years figuring out how to make motion feel inevitable. Almost none of that has reached the web. This piece is an attempt to import a little of it.
Three things cinema gets right
- Duration follows weight. A door closes faster than a curtain falls. Heavier elements take longer; lighter ones snap.
- Easing follows physics, not fashion. Real things accelerate from rest and settle into rest. Linear motion is for machines that have failed.
- Nothing moves alone. A camera push reveals a character. A character turn reveals a room. Every motion is part of a sentence.
If you internalize only those three, your product will feel different next Monday. Let's translate them into code.
Duration: stop using 200ms for everything
Pick durations from a small scale, and tie them to the visual weight of the thing moving. Here is the scale we use inside Luminax — six values, no more:
tsexport const dur = { instant: 80, // hover affordances brisk: 180, // small toggles, chips natural: 320, // panels, sheets, drawers considered: 520, // hero entrances cinematic: 780, // page transitions long: 1200, // ambient backgrounds } as const;
When a chip toggles, use brisk. When a side panel slides in, use natural. When the hero of a landing page composes itself, use considered. The point is not the exact numbers — it's that you have a vocabulary, and you stop typing 200 from muscle memory.
Easing: one curve to rule them
We default everything to a single cubic-bezier — the one designers at Apple have been quietly reusing for years:
ts// 'cinematic' — slow start, generous settle export const ease = [0.22, 1, 0.36, 1] as const;
It accelerates gently, overshoots almost imperceptibly, and settles. Used at 320ms it feels considered. Used at 780ms it feels deliberate. Used at 1200ms it feels alive.
Choreography: nothing moves alone
When more than one element animates in the same moment, stagger them. Not for delight — for legibility. The eye can only track one thing at a time, and a 40-millisecond delay between siblings turns a wall of motion into a sentence the eye can read.
tsx<motion.ul> {items.map((item, i) => ( <motion.li key={item.id} initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.04 * i, duration: 0.32, ease: [0.22, 1, 0.36, 1], }} > {item.label} </motion.li> ))} </motion.ul>
What to remove
Most cinematic UIs are made by removing things, not adding them. Remove every spinner that lasts less than 400ms. Remove every micro-bounce. Remove the parallax. Remove the second hover effect. Then watch what you have left and add nothing back.
"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away."
Try it on one screen this week. Pick the most ordinary surface in your product — the settings page, probably — and give it the treatment. Pick durations from a real scale. Use one easing. Stagger. Remove three things. Then sit with it for an hour before shipping. The difference will be small, and you will not be able to stop noticing it.
Ada Okonkwo
Design engineerDesigns interfaces that move the way real things move. Previously at Linear and Framer. Believes most apps would be better with one fewer animation and one more well-set heading.