duration + easing → physics
A tween moves a value from A to B over a fixed duration along an easing curve. A spring has no duration — it simulates a mass on a spring. Each frame the solver computes the restoring force from Hooke's law plus a damper, integrates, and repeats until the system settles. Because it carries velocity, a spring overshoots, settles naturally, and survives interruptions (a new target just re-aims the same moving mass).
F = -k·x - c·v (Hooke's law + damping force) k = stiffness (spring strength — pull back toward target) x = displacement (position - target) c = damping (friction — bleeds energy each frame) v = velocity acceleration = F / mass velocity += acceleration · dt // semi-implicit Euler (stable) position += velocity · dt repeat until |v| ≈ 0 AND |position - target| ≈ 0
| parameter | unit | raise it → | effect |
|---|---|---|---|
stiffness (k) |
N/m | stronger pull | faster, snappier, more oscillation |
damping (c) |
N·s/m | more friction | less overshoot, settles sooner (c critical = no bounce) |
mass (m) |
kg | heavier object | slower acceleration, sluggish feel |
1 · the spring solver you write (edit me)
2 · Babel compiles JSX → element tree
<SpringDemo/> becomes React.createElement(SpringDemo).
The effect's step() loop mutates the ball via ballRef — no
React re-render per frame, so 60fps stays cheap.
// (hit "compile & render" to see Babel's output)
3 · live React (the solver, proven)
Drag the sliders, click a preset, or click anywhere on the track to re-aim the target.
The gold-check does this automatically: it asserts the ball
starts at ~50%, clicks Set Target: 80%, waits for the spring to settle, then
asserts the ball landed near 80% — proving F = -kx - cv drives real motion.
ball pos: — · gold: start ~50% → click 80% → settle near 80% (moved > 20%)
spring vs tween · presets
| intent | spring | tween (duration + easing) |
|---|---|---|
| model | physics: F = -kx - cv |
time-mapped curve (linear/ease-out/cubic-bezier) |
| duration | emergent — settles when |v| ≈ 0 |
fixed (300ms) |
| interruption | keeps velocity → smooth re-aim | restarts from a new curve (can jump) |
| overshoot | physical (underdamped only) | only if easing allows |
| best for | gestures, drag, swipe, UI | choreographed sequences, loops |
| preset | stiffness | damping | feel |
|---|---|---|---|
Gentle | 100 | 12 | soft, natural (≈ Framer default) |
Wobbly | 180 | 12 | playful bounce |
Stiff | 210 | 20 | snappy |
Slow | 280 | 60 | heavy, slow |
Molasses | 280 | 120 | very slow, no overshoot |