Skip to main content

Search rules

Search all UI Guides rules by name, category, or source

animationsEmil Kowalski

Decompose 2D Motion Into X and Y Springs

MUST

MUST: Decompose 2D motion into two independent springs — one on `x`, one on `y`, each seeded with its OWN release velocity. A single spring on the 2D distance (`Math.hypot(x, y)` → 0) can hold only one velocity, so both axes arrive together, the card slides home along a rigid radial line, and the tangential half of the throw is discarded.

A gesture that moves in two dimensions needs two springs, not one spring on the distance

animate(el, { x: 0 }, { type: "spring", velocity: vx });
animate(el, { y: 0 }, { type: "spring", velocity: vy }); // not one spring on hypot(x, y)

Bad

Good

Why it matters

This is the answer to a question the corpus has not asked before: not how to configure a spring, but HOW MANY springs a gesture needs. animations-spring-physics argues for springs over tweens because they carry velocity through an interruption; animations-emil-subtle-bounce bounds the bounce. Neither says anything about dimensionality — and a 2D drag is where the naive implementation quietly breaks.

The tempting shortcut is to spring the scalar distance home: `Math.hypot(x, y)` → 0, one spring, one velocity. But one spring can only hold one velocity, so both axes are forced to share it and to arrive at the same instant. Throw a card diagonally with more speed on X than Y and the faster axis drags the slower one along with it; whatever direction you actually flicked, the card slides home along a rigid radial line, and the tangential half of your gesture is simply discarded.

Two springs — one on x, one on y, each seeded with its OWN release velocity — let each axis settle on its own clock. The emergent 2D path curves: it continues the way you threw the thing and hooks back, which is what "it followed my finger" actually means. This is Apple's point from Designing Fluid Interfaces, and it also explains why Motion's `x`/`y` values are separate animatable channels rather than a single vector.

Built by Gleb Stroganov, design engineer at Evil Martians.

The rules come from other people's skills and guidelines — Vercel, Rauno Freiberg, @Ibelick, impeccable, Emil Kowalski, Tailwind, RAMS — each one credited on the Sources page. The work here is extraction and wiring: every rule is pulled into one corpus, given a good and a bad example you can operate, a MUST/SHOULD/NEVER rule an agent can paste, and a link back to where it came from.