JavaScript Animations
Learn JavaScript animations with requestAnimationFrame, easing functions, chaining, the Web Animations API, and performance and accessibility best practices.
JavaScript animations offer a dynamic way to enhance the user experience on web pages. From subtle effects to complex movements, mastering JavaScript animations can significantly elevate the interactivity and visual appeal of your projects. This comprehensive guide will provide an in-depth exploration of JavaScript animations, including fundamental concepts, essential techniques, and practical examples to help you become proficient in creating engaging animations.
Introduction to JavaScript Animations
JavaScript animations manipulate the Document Object Model (DOM) elements to create visual effects. They are used to move, resize, and alter elements in response to user interactions or automatically over time. By understanding the core principles and techniques, you can create animations that are both performant and visually appealing.
Why Use JavaScript for Animations?
JavaScript animations offer greater control and flexibility compared to CSS animations. They allow for more complex sequences and interactions, making it possible to create highly customized effects. JavaScript animations can be dynamically controlled and are often necessary when you need to respond to user inputs or integrate with other JavaScript functionalities.
As a rule of thumb:
- Reach for CSS (
transition/@keyframes) for simple, declarative state changes such as hovers, fades, and toggles. The browser optimizes these on a separate compositor thread, so they tend to be the most performant choice. See CSS animations. - Reach for JavaScript when the animation depends on data, user gestures, physics, or logic that CSS cannot express — for example following the mouse, snapping to dynamic positions, or chaining many steps with branching conditions.
For the lowest-effort JavaScript option, the built-in Web Animations API (covered below) gives you CSS-quality performance with imperative control, while requestAnimationFrame gives you full frame-by-frame control when you need it.
Basic Concepts of JavaScript Animations
Before diving into specific techniques, it's essential to understand some basic concepts that underpin JavaScript animations:
- Frames Per Second (FPS): The rate at which animation frames are displayed. A higher FPS results in smoother animations.
- Easing Functions: These functions control the acceleration and deceleration of animations, adding natural movement.
- Timing Functions: Determine the duration and delay of animations.
- Keyframes: Define the starting, ending, and intermediate states of an animation.
requestAnimationFrame vs. setInterval
Before requestAnimationFrame existed, animations were driven by setInterval and setTimeout, running a callback every few milliseconds. That approach has two problems: the interval is not synchronized with the screen's refresh rate (causing dropped or doubled frames), and the timer keeps firing even when the tab is in the background, wasting CPU and battery.
requestAnimationFrame(callback) solves both. The browser calls your callback right before the next repaint — usually about 60 times per second, matching the display — and automatically pauses when the tab is hidden. The callback receives a high-resolution timestamp argument, which you use to compute progress based on real elapsed time rather than assuming a fixed frame rate.
// setInterval: fixed timer, not synced to the screen, runs in background tabs
let pos = 0;
const id = setInterval(() => {
pos += 2;
if (pos >= 200) clearInterval(id);
}, 16);
// requestAnimationFrame: synced to repaint, pauses in background tabs
function frame(timestamp) {
// use timestamp to drive the animation
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);Use requestAnimationFrame for anything that updates the visuals every frame.
Simple JavaScript Animation Example
Let's start with a simple example where we animate a square moving across the screen.
<!DOCTYPE html>
<html>
<head>
<style>
#square {
width: 50px;
height: 50px;
background-color: red;
position: absolute;
left: 0;
top: 50px;
}
</style>
</head>
<body>
<div id="square"></div>
<script>
const square = document.getElementById('square');
let pos = 0;
function move() {
if (pos < window.innerWidth - 50) {
pos += 2;
square.style.transform = 'translateX(' + pos + 'px)';
requestAnimationFrame(move);
}
}
move();
</script>
</body>
</html>This example demonstrates a basic animation where a red square moves from the left side of the screen to the right. The animation uses the requestAnimationFrame function, which ensures smooth movement by synchronizing the animation with the display's refresh rate. The move function updates the square's position incrementally and continuously calls itself until the square reaches the edge of the window.
While JS animations require frequent style updates, prefer transform and opacity to avoid triggering layout reflows.
Advanced Techniques in JavaScript Animations
Using Easing Functions
Easing functions make animations look more natural by varying the speed of the animation. They typically expect a normalized progress value between 0 and 1. Here’s an example using an easing function to animate the square:
<!DOCTYPE html>
<html>
<head>
<style>
#square {
width: 50px;
height: 50px;
background-color: blue;
position: absolute;
left: 0;
top: 50px;
}
</style>
</head>
<body>
<div id="square"></div>
<script>
const square = document.getElementById('square');
let start = null;
const duration = 2000; // Animation duration in milliseconds
function easeOutQuad(t) {
return t * (2 - t);
}
function animate(timestamp) {
if (!start) start = timestamp;
let elapsed = timestamp - start;
let progress = Math.min(elapsed / duration, 1);
let easedProgress = easeOutQuad(progress);
square.style.transform = 'translateX(' + (window.innerWidth - 50) * easedProgress + 'px)';
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
</script>
</body>
</html>This example enhances the simple animation by incorporating an easing function called easeOutQuad. Easing functions create more natural-looking animations by adjusting the speed of the animated element over time. In this case, the blue square moves from left to right, starting quickly and slowing down as it approaches the end. The animation duration is set to 2000 milliseconds, and the position of the square is updated based on the eased progress.
Chaining Animations
Chaining animations allows you to execute multiple animations sequentially. Here’s an example of a square that first moves right, then down:
<!DOCTYPE html>
<html>
<head>
<style>
#square {
width: 50px;
height: 50px;
background-color: green;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div id="square"></div>
<script>
const square = document.getElementById('square');
let xPos = 0;
let yPos = 0;
function moveRight() {
if (xPos < window.innerWidth - 50) {
xPos += 2;
square.style.transform = 'translateX(' + xPos + 'px)';
requestAnimationFrame(moveRight);
} else {
xPos = 0;
yPos = 0; // Explicitly reset yPos before downward phase
square.style.transform = 'translateX(0px)';
requestAnimationFrame(moveDown);
}
}
function moveDown() {
if (yPos < window.innerHeight - 50) {
yPos += 2;
square.style.transform = 'translateY(' + yPos + 'px)';
requestAnimationFrame(moveDown);
} else {
yPos = 0; // Reset for repeated cycles
requestAnimationFrame(moveRight);
}
}
moveRight();
</script>
</body>
</html>This example demonstrates chaining animations by moving a green square first to the right and then downward. The moveRight function moves the square to the right until it reaches the edge of the window. Once it reaches the right edge, yPos is explicitly reset to 0 before moveDown is called. This sequential execution, combined with proper state management for repeated cycles, showcases how to create more complex animation sequences by chaining multiple animations together.
Animating Multiple Properties
Animating multiple properties simultaneously can create more complex and visually appealing effects. Here’s an example that changes both position and color:
<!DOCTYPE html>
<html>
<head>
<style>
#square {
width: 50px;
height: 50px;
background-color: purple;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div id="square"></div>
<script>
const square = document.getElementById('square');
let pos = 0;
function changeColor(progress) {
let red = Math.floor(255 * progress);
let green = Math.floor(255 * (1 - progress));
return `rgb(${red}, ${green}, 0)`;
}
function animate() {
if (pos < window.innerWidth - 50) {
pos += 2;
let progress = pos / (window.innerWidth - 50);
square.style.transform = 'translateX(' + pos + 'px)';
square.style.backgroundColor = changeColor(progress);
requestAnimationFrame(animate);
}
}
animate();
</script>
</body>
</html>This example animates both the position and the background color of a purple square. As the square moves from left to right, its color transitions from red to green. The changeColor function calculates the color based on the progress of the animation, creating a gradient effect. This demonstrates how to animate multiple properties simultaneously, adding complexity and visual interest to animations.
The Web Animations API
Hand-rolling a requestAnimationFrame loop gives you total control, but for most timeline-based animations the browser already has a built-in engine: the Web Animations API (WAAPI). Calling element.animate(keyframes, options) runs the animation off the main thread when possible (just like CSS), so you get smooth playback without writing a frame loop yourself. It also returns an Animation object you can pause, reverse, seek, or await.
<!DOCTYPE html>
<html>
<head>
<style>
#box {
width: 50px;
height: 50px;
background-color: teal;
position: absolute;
left: 0;
top: 50px;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
const box = document.getElementById('box');
const animation = box.animate(
[
{ transform: 'translateX(0)', opacity: 1 },
{ transform: 'translateX(300px)', opacity: 0.3 }
],
{
duration: 2000,
easing: 'ease-out',
fill: 'forwards'
}
);
// The returned object gives you imperative control:
animation.finished.then(() => console.log('done'));
// animation.pause(); animation.reverse(); animation.currentTime = 1000;
</script>
</body>
</html>The first argument is an array of keyframes (each an object of CSS properties); the second sets duration, easing, and behaviors like fill (keep the final state), iterations, direction, and delay. Because easing accepts the same cubic-bezier() and keyword values as CSS, you often do not need to write easing math by hand at all.
Prefer WAAPI when your animation is a fixed timeline of property changes. Reserve a manual requestAnimationFrame loop for cases where each frame depends on live input or computed state — like physics, dragging, or values that are not known up front.
Stopping and Reversing Animations
A requestAnimationFrame loop keeps running until you stop scheduling new frames. To cancel one immediately, save the ID that requestAnimationFrame returns and pass it to cancelAnimationFrame:
let rafId;
function loop(timestamp) {
// ...update visuals...
rafId = requestAnimationFrame(loop);
}
rafId = requestAnimationFrame(loop);
// Later, e.g. on a button click or when the element is removed:
cancelAnimationFrame(rafId);Always cancel running animations before removing their elements from the DOM, otherwise the loop keeps mutating a detached node and leaks work every frame. With the Web Animations API the equivalents are simply animation.cancel(), animation.pause(), and animation.reverse().
Accessibility: Respect Reduced Motion
Large or fast motion can trigger nausea, dizziness, or migraines for some users. Operating systems expose a "reduce motion" preference, and you should honor it. In JavaScript, read it with matchMedia and either disable the animation or jump straight to the end state:
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
if (prefersReducedMotion) {
// Skip the animation: apply the final state instantly
square.style.transform = 'translateX(300px)';
} else {
requestAnimationFrame(animate);
}This single check makes your interfaces both more inclusive and more pleasant for users who opt out of motion.
Performance Optimization for JavaScript Animations
To ensure smooth and efficient animations, follow these performance optimization tips:
- Use
requestAnimationFrame: This method synchronizes with the display refresh rate, providing smoother animations. - Minimize DOM Manipulations: Batch DOM updates to reduce reflows and repaints. Prefer
transformandopacityfor position changes to leverage GPU acceleration. - Optimize CSS: Use GPU-accelerated CSS properties like
transformandopacity. - Throttle Events: Limit the frequency of events like
resizeorscrollusing throttling or debouncing techniques. - Stop Animations Properly: Use
cancelAnimationFrameto stop animations when they are no longer needed. Store the animation ID and callcancelAnimationFrame(id)to prevent memory leaks. Example:let animationId = requestAnimationFrame(animate); // ... later ... cancelAnimationFrame(animationId); - Avoid Layout Thrashing: Do not read layout properties (e.g.,
offsetWidth,getBoundingClientRect) inside the animation loop, as this forces the browser to recalculate styles mid-frame and can cause jank.
You can also make animations with just CSS (no JavaScript), and it will usually lead to a better performance. See CSS animations.
Conclusion
Mastering JavaScript animations involves understanding basic principles, implementing advanced techniques, and optimizing performance. By following this guide, you can create engaging and responsive animations that enhance the user experience. Experiment with different easing functions, chaining, and property animations to unlock the full potential of JavaScript in your web projects.
To go further, explore these related topics:
- CSS animations — the declarative, often more performant alternative.
- Scheduling: setTimeout and setInterval — timer-based scheduling and why
requestAnimationFrameis preferred for visuals. - Introduction to browser events — trigger animations in response to user interaction.
- JavaScript HTML DOM — selecting and manipulating the elements you animate.