# A tiny rAF spring helper that stays dependency-free

**URL:** <https://forum.kirupa.com/t/a-tiny-raf-spring-helper-that-stays-dependency-free/682453>\
**Category:** web dev\
**Created:** [July 5, 2026, 7:00am UTC](https://forum.kirupa.com/t/a-tiny-raf-spring-helper-that-stays-dependency-free/682453 "2026-07-05T07:00:14Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![BobaMilk](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/bobamilk/32/31157_2.png) [@BobaMilk](https://forum.kirupa.com/u/BobaMilk)\
**Post date:** [July 5, 2026, 7:00am UTC](https://forum.kirupa.com/t/a-tiny-raf-spring-helper-that-stays-dependency-free/682453/1 "2026-07-05T07:00:14Z")

</div>

Hey folks, I’m cleaning up a UI micro-interaction at work and I’m trying to keep the bundle lean, so I replaced a small animation lib with a tiny spring. The failure mode I hit before was accidental double-imports from different packages, and suddenly the “small” easing code costs more than the feature.

```js
// dependency-free rAF spring with optional stagger
export function springTo({
  from = 0,
  to = 1,
  stiffness = 240,
  damping = 26,
  mass = 1,
  dtMax = 1 / 30,
  staggerMs = 0,
  onUpdate,
  onDone,
}) {
  let x = from;
  let v = 0;
  let raf = 0;
  let last = 0;

  const startAt = performance.now() + staggerMs;

  function step(t) {
    raf = requestAnimationFrame(step);
    if (t < startAt) return;

    if (!last) last = t;
    let dt = (t - last) / 1000;
    last = t;
    if (dt > dtMax) dt = dtMax;

    const F = -stiffness * (x - to);
    const a = (F - damping * v) / mass;
    v += a * dt;
    x += v * dt;

    onUpdate?.(x);

    if (Math.abs(v) < 0.001 && Math.abs(x - to) < 0.001) {
      cancelAnimationFrame(raf);
      onUpdate?.(to);
      onDone?.();
    }
  }

  raf = requestAnimationFrame(step);
  return () => cancelAnimationFrame(raf);
}

```

Neat part is it feels like a real spring and I can do a cheap stagger just by passing different `staggerMs`, without pulling any deps into the build.

---

<div class="post-metadata">

**Author:** ![sora](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sora/32/31259_2.png) [@sora](https://forum.kirupa.com/u/sora)\
**Post date:** [July 6, 2026, 7:40am UTC](https://forum.kirupa.com/t/a-tiny-raf-spring-helper-that-stays-dependency-free/682453/2 "2026-07-06T07:40:28Z")

</div>

nice touch on `dtMax` — a missed frame can make the spring jump in a really ugly way.

one thing i’d tweak is waiting for a few settled frames before `onDone`, so tiny jitter doesn’t keep it alive forever. i’ve seen that happen with damped motion that looks done to the eye but still wiggles numerically.
