Damping makes animated motion lose energy over time. In an interface, it is the reason a panel can slow as it arrives, a spring can stop bouncing, and a released draggable item can coast before coming to rest. Without it, motion can feel mechanical or continue indefinitely; with it, movement better matches everyday expectations of friction and momentum. section 12.0 section 12.1.2 section 12.1.1
In a frame-by-frame JavaScript animation, damping is usually modeled by multiplying velocity by a coefficient between 0 and 1. A value slightly below 1 preserves most motion while reducing it every frame. For example, a coefficient of 0.95 cuts speed by 5% roughly every 16 milliseconds, producing a quick initial slowdown followed by a gentler stop. A value of 1 applies no damping at all. section 12.4.2 section 12.5 section 12.5
A basic damping loop has four connected steps:
- Track the element’s current position and velocity. section 12.4.2
- Update velocity from an applied force or the user’s latest drag movement. section 12.4.1 section 12.4.2
- Multiply velocity by the damping or friction coefficient on each animation frame. section 12.4.1 section 12.4.2
- Add the reduced velocity to position, then render that position with a CSS transform. section 12.4.1 section 12.4.2
Settling a springy button
A spring animation first calculates displacement from a target, then uses Hooke’s law to turn that displacement into a restoring force. That force changes velocity. Multiplying the velocity by a damping value such as 0.9 reduces the energy after each update, so a clicked button can compress, overshoot slightly, oscillate, and finally settle at its intended scale. Stopping when both displacement and velocity are very small avoids imperceptible extra frames. section 12.4.1
Listing 12.10 Applying simulated spring motion when clicking a button
HTML:
<button id="springBtn">Click Me</button>
JavaScript:
const button = document.getElementById("springBtn");
let scale = 1; #1
let velocity = 0;
const target = 1; #2
const k = 0.1; #3
const damping = 0.9; #4
let animating = false;
function animate() {
if (!animating) return;
let x = scale - target; #5
let force = -k * x; #6
velocity += force; #7
velocity *= damping; #8
scale += velocity; #9
#9
button.style.transform = `scale(${scale})`; #9
if (Math.abs(scale - target) < 0.001 && #10
Math.abs(velocity) < 0.001) { #10
scale = target; #10
velocity = 0; #10
animating = false; #10
button.style.transform = `scale(${scale})`; #10
return; #10
} #10
requestAnimationFrame(animate);
}
button.addEventListener("click", () => {
scale = 0.85; #11
velocity = 0;
animating = true;
requestAnimationFrame(animate);
});
Inspect the update order: calculate spring force, add it to velocity, damp the velocity, update scale, and stop once scale and velocity are close enough to their targets. section 12.4.1
Inertial dragging
Damping also simulates friction after a drag ends. Record horizontal and vertical velocity while the pointer moves. On release, keep advancing the element by that velocity, but multiply both velocity components by a friction value such as 0.95 each frame. The item initially continues in the release direction, then slows naturally instead of stopping instantly. This pattern suits draggable game pieces, swipeable galleries, and scroll-like interactions. section 12.4.2
Listing 12.11 Applying inertia to a draggable element
HTML:
<div id="draggable">Drag Me!</div>
JavaScript:
const draggable = document.getElementById('draggable');
let isDragging = false;
let startX, startY;
let x = 0, y = 0;
let vx = 0, vy = 0; #1
let lastX, lastY;
const friction = 0.95; #2
let inertiaAnimation;
draggable.addEventListener('mousedown', (e) => { #3
isDragging = true;
draggable.style.cursor = 'grabbing';
startX = e.clientX - x;
startY = e.clientY - y;
vx = 0;
vy = 0;
lastX = e.clientX;
lastY = e.clientY;
cancelAnimationFrame(inertiaAnimation);
});
window.addEventListener('mousemove', (e) => { #4
if (!isDragging) return;
x = e.clientX - startX;
y = e.clientY - startY;
vx = e.clientX - lastX;
vy = e.clientY - lastY;
lastX = e.clientX;
lastY = e.clientY;
draggable.style.transform = `translate(${x}px, ${y}px)`;
});
window.addEventListener('mouseup', () => { #5
if (!isDragging) return;
isDragging = false;
draggable.style.cursor = 'grab';
applyInertia(); #6
});
function applyInertia() {
vx *= friction; #7
vy *= friction; #7
x += vx; #8
y += vy; #8
draggable.style.transform = `translate(${x}px, ${y}px)`; #8
if (Math.abs(vx) > 0.1 || Math.abs(vy) > 0.1) {
inertiaAnimation = requestAnimationFrame(applyInertia);
}
}
Use the draggable-element listing to trace how pointer movement produces velocity and how repeated friction scaling turns that velocity into a coasting stop. section 12.4.2
Damping controls settling, not the spring’s pull. In spring motion, stiffness determines how strongly displacement accelerates an element toward its target; greater stiffness can make motion faster and produce more overshoot. Damping then removes energy from that motion. Keeping the two roles separate makes tuning clearer: adjust stiffness for responsiveness and damping for how quickly oscillation fades. section 12.4.1 section 12.5
For a practical check, watch for a velocity multiplier of 1. It leaves velocity unchanged, so an animation intended to slow down will keep moving at a constant speed. Choose a coefficient below 1 when the intended result is friction, deceleration, or a spring that settles. section 12.5
