Motion X-Ray

Spring physics · Zero dependencies

Physics,
not timelines.

An interface built on springs you can interrupt

Every animation here is solved as a damped oscillation, and the overlay draws the forces while they happen.

There is an argument further down about why a spring beats a duration. First, three things you can pick up and throw:

Grab any of them while they are still moving. Nothing waits for an animation to finish, because nothing here is playing one.

A spring has no idea how long it has been running.

Where this comes from

Most interface motion is a recording. You give it a start, an end and a number of milliseconds, and it plays that back. Which works beautifully right up to the moment somebody touches the screen while it is still going.

A spring has nothing to play back. It holds a position and a velocity, and each frame it asks one question: how far am I from where I want to be. Move the target halfway through and the answer simply changes. The speed carries over, because the speed was never part of a plan. It was only ever how fast the thing happened to be going.

That is the entire trick. Everything else on this page is bookkeeping around it.

Tweens are excellent, right until someone touches one.

What actually breaks

This is not a case against easing curves. A curve is the right answer for motion nobody is going to reach into: a page load, a splash, a transition that plays once and gets out of the way.

Good at

Motion with a deadline. You name the distance and the time, and it hits both, every time, on every machine.

Hopeless at

Motion that gets grabbed. Retarget one and it restarts from a dead stop, and the hand that was already moving feels the jolt.

Below is the same interaction twice, matched for distance and matched for time. Interrupt both at speed. Only one of them keeps it.

The side by side

Same distance, same duration, one survives contact.

Displacement. Force. Damping. Rest.

One second order equation, solved exactly rather than stepped forward in little pieces. No substepping, no stability guard, no clamp to stop it exploding on a backgrounded tab. There is nothing there to explode.

  1. 01
    Displacement

    How far the current value sits from its target. It is the only input the spring reacts to, and it is why retargeting is free.

  2. 02
    Force

    Stiffness times displacement, pulling back toward the target. Raise it and the whole motion gets quicker and more eager.

  3. 03
    Damping

    Drag proportional to velocity, bleeding energy out of the system. Too little and it rings. Too much and it crawls.

  4. 04
    Rest

    When displacement and velocity both drop under a threshold, the value lands on target and stops asking for frames.

Damping has settings

Like suspension. Soft, sharp, or stuck.

One number decides the character of every spring on this page. The damping ratio, ζ, is the ratio of the drag you have to the drag it would take to stop overshoot entirely. Everything else is scale.

ζ < 1

Underdamped.

Goes past the target and rings back. This is what people mean when they say an interface feels alive.

ζ = 1

Critical.

Arrives as fast as it can without overshooting. Right for anything that should not draw attention to itself.

ζ > 1

Overdamped.

Creeps in. Technically correct, extremely patient, and almost always the wrong call in a UI.

Nothing on this page can be locked. If you can see it moving, you can take hold of it.

Every claim here is drawn on top of itself.

Press X. A canvas covers the document and, for each spring that is currently awake, draws four things.

  1. 01
    Rest target

    A ghost sitting where the value is trying to get to.

  2. 02
    Velocity

    A vector out of the moving element, scaled to speed. Catch something on the way past and watch it flip.

  3. 03
    Energy

    A rolling trace of kinetic plus potential energy. It is the decay you can usually only feel.

  4. 04
    Readout

    Damping ratio, natural frequency and time to rest, live, per spring.

One canvas, one draw pass, under a millisecond a frame. A meter that costs frames would disprove the thing it exists to show.

House rules

Five constraints the code is not allowed to break.

  • No isAnimating flags Retargeting a spring keeps the velocity it already had. There is no state in which a thing on this page refuses to be touched.
  • One write pass a frame Physics integrates first, then every style write on the page happens in a single batch. Layout is never read inside the loop.
  • Transforms and opacity only Nothing that triggers layout is allowed to animate. Geometry is cached on resize and on pointer down, never mid gesture.
  • Reduced motion goes instant Springs collapse to a jump, not to a shorter tween. Half speed motion is still motion, and that was the thing being opted out of.
  • One frame loop for the page Not one per demo and not one per spring. Everything integrates against the same clamped delta, so nothing can drift apart.