Controlling CSS Animations and Transitions with JavaScript

Master the art of programmatic animation control. Learn to pause, play, reverse, and synchronize animations for engaging, performant web experiences.

Understanding CSS Transitions and Animations

Modern web development increasingly relies on smooth, performant animations to create engaging user experiences. While CSS provides declarative ways to define animations and transitions, JavaScript offers precise control over animation playback, timing, and state. This guide explores how to leverage JavaScript to control CSS animations and transitions, enabling sophisticated interactive experiences while maintaining optimal performance.

CSS animations form the foundation of motion design on the web. Understanding the difference between transitions and animations--and how JavaScript enhances both--is essential for creating polished, performant interfaces. Transitions handle simple state changes between two values, while animations support complex multi-step sequences with keyframes. Both can be enhanced with JavaScript for dynamic control, enabling features like pause-and-play functionality, synchronized animations, and user-driven interaction.

For developers building modern web applications, mastering animation control is essential for creating responsive, polished user interfaces that feel alive and engaging.

CSS Transitions vs Animations: Key Differences

CSS Transitions: Simple State Changes

CSS transitions provide the simplest way to animate between two states. When a CSS property changes, the browser interpolates the values over a specified duration, creating a smooth effect. Transitions are ideal for hover states, focus indicators, and any interaction where an element moves between two defined states. The transition property accepts four values: the properties to animate, the duration, the timing function, and an optional delay.

Key transition properties:

  • transition-property: The CSS properties to animate
  • transition-duration: How long the transition takes (e.g., 300ms)
  • transition-timing-function: The acceleration curve (ease, linear, cubic-bezier)
  • transition-delay: Optional wait before starting

The power of transitions lies in their simplicity and browser optimization. Because transitions are declarative in CSS, the browser can anticipate and prepare for the animation, often resulting in better performance than JavaScript-driven alternatives. Transitions work particularly well for small, frequent animations like button hover effects, menu open/close states, and form input focus rings.

CSS Animations: Complex Sequences with Keyframes

CSS animations extend beyond simple two-state transitions by supporting multi-step sequences defined in @keyframes rules. An animation can have any number of keyframes, each specifying different property values at specific percentages of the animation duration. This capability enables complex effects like bouncing balls, loading spinners, and elaborate entrance animations that would be impractical with transitions alone.

Key animation properties:

  • animation-name: Links to @keyframes definition
  • animation-duration: How long one cycle takes
  • animation-timing-function: How the animation progresses over time
  • animation-delay: Wait before starting
  • animation-iteration-count: How many times to repeat
  • animation-direction: normal, reverse, alternate, alternate-reverse
  • animation-play-state: running or paused

Unlike transitions, animations can repeat indefinitely with animation-iteration-count: infinite, making them suitable for continuous motion effects like pulsing indicators or rotating loaders. The animation-play-state property is particularly valuable for JavaScript control, as toggling between "running" and "paused" states allows users to halt animations for better readability or to conserve resources.

Learn more about CSS animations in MDN's comprehensive guide.

CSS Animation vs Transition Syntax
1/* CSS Transition - simple state change */2.button {3 background-color: #3b82f6;4 transition: background-color 300ms ease-out;5}6 7.button:hover {8 background-color: #1d4ed8;9}10 11/* CSS Animation - complex multi-step sequence */12@keyframes bounce {13 0%, 100% {14 transform: translateY(0);15 }16 50% {17 transform: translateY(-20px);18 }19}20 21.bouncing-element {22 animation: bounce 1s ease-in-out infinite;23 animation-play-state: running;24}

JavaScript Control: The animation-play-state Property

The animation-play-state property provides the most direct JavaScript control over CSS animations, allowing developers to pause and resume animation playback. By default, animations run in the "running" state; setting this property to "paused" freezes the animation at its current position without resetting it. This capability enables interactive features like hover-to-pause functionality, where users can examine an animated element by hovering over it, or pause buttons that halt all animations on a page for improved accessibility.

Practical implementations of animation-play-state often combine it with event listeners for user interactions. A common pattern adds a .paused class to animated elements when the user mouses over them, setting animation-play-state: paused, and removes the class on mouseout to resume playback. For page-wide animation control, JavaScript can query all animated elements and toggle their play state collectively. This approach is particularly valuable for accessibility, allowing users who are sensitive to motion to pause all animations through a preferences toggle. The animation-play-state property represents the simplest entry point for JavaScript-enhanced animation control.

Common patterns for animation-play-state control:

  • Toggle between running and paused on click events
  • Pause animations when the user scrolls past certain elements
  • Provide a global pause button for accessibility
  • Use with Intersection Observer to pause off-screen animations
Controlling CSS Animations with JavaScript
1// Hover to pause animation2const animatedElement = document.querySelector('.animated');3 4animatedElement.addEventListener('mouseenter', () => {5 animatedElement.style.animationPlayState = 'paused';6});7 8animatedElement.addEventListener('mouseleave', () => {9 animatedElement.style.animationPlayState = 'running';10});11 12// Toggle animation on click13let isPaused = false;14animatedElement.addEventListener('click', () => {15 isPaused = !isPaused;16 animatedElement.style.animationPlayState = isPaused ? 'paused' : 'running';17});18 19// Pause all animations (accessibility)20function pauseAllAnimations() {21 const animatedElements = document.querySelectorAll('[style*="animation"]');22 animatedElements.forEach(el => {23 el.style.animationPlayState = 'paused';24 });25}
Animation Control Techniques

Essential JavaScript methods for controlling CSS animations

animation-play-state

Toggle between 'running' and 'paused' states. Freezes animation at current position without resetting.

Class-based Control

Add/remove CSS classes with !important to override animation properties for reliable state management.

Computed Style Access

Read animation properties via getComputedStyle() to synchronize multiple animations programmatically.

The Web Animations API: Advanced JavaScript Control

The Web Animations API provides a JavaScript-native way to create and control animations, offering capabilities beyond CSS-based approaches. Unlike CSS animations, which are declarative and limited to predefined keyframes, the Web Animations API allows developers to construct animations dynamically, specifying keyframes and timing options as JavaScript objects.

The core method, Element.animate(), accepts an array of keyframe objects and a timing options object, returning an Animation object that provides extensive control over playback. This programmatic approach enables animations to be created in response to runtime conditions, user input, or data that is not known at stylesheet authoring time. By combining this API with modern JavaScript techniques, developers can create sophisticated, data-driven animation experiences.

Key Animation Object Methods:

MethodPurpose
play()Start or resume animation playback
pause()Halt animation at current position
reverse()Play animation backward
finish()Jump to end of animation
cancel()Stop and reset animation

Animation Object Properties:

PropertyPurpose
currentTimeGet/set animation position (in milliseconds)
playbackStateCurrent state: running, paused, finished
finishedPromise that resolves when animation completes

The returned Animation object provides methods like play(), pause(), reverse(), and finish(), as well as properties for currentTime and playbackState, enabling precise control that would be impossible with CSS alone. Explore more in the MDN Web Animations API guide.

Web Animations API Example
1// Create an animation using the Web Animations API2const element = document.querySelector('.animated-element');3 4const animation = element.animate(5 [6 { transform: 'translateX(0)', opacity: 1 },7 { transform: 'translateX(200px)', opacity: 0.5 },8 { transform: 'translateX(0)', opacity: 1 }9 ],10 {11 duration: 1000,12 easing: 'ease-in-out',13 iterations: Infinity14 }15);16 17// Control methods18document.getElementById('play').addEventListener('click', () => animation.play());19document.getElementById('pause').addEventListener('click', () => animation.pause());20document.getElementById('reverse').addEventListener('click', () => animation.reverse());21document.getElementById('finish').addEventListener('click', () => animation.finish());22 23// Using promises for sequencing24animation.finished.then(() => {25 console.log('Animation completed!');26});27 28// Scrubbing through animation29const slider = document.getElementById('progress');30slider.addEventListener('input', (e) => {31 animation.currentTime = (e.target.value / 100) * animation.effect.getTiming().duration;32});

Performance Optimization for Smooth Animations

Performance optimization for web animations centers on a fundamental principle: animate only transform and opacity properties. These properties can be handled entirely by the compositor thread, which operates independently of the main JavaScript thread and GPU-accelerated rendering. This optimization is critical for high-performance web applications that aim to deliver smooth 60fps experiences across all devices.

Properties to Animate (GPU-accelerated):

  • transform - translate, rotate, scale, skew
  • opacity - transparency level

Properties to Avoid (CPU-heavy):

  • width, height, margin, padding - trigger layout
  • background-color, border-color - trigger repaint
  • box-shadow, filter - expensive composite operations

The transform property supports translation, rotation, scaling, and skew through efficient matrix operations that the GPU handles natively. Combining multiple transforms in a single property, such as transform: translateX(100px) rotate(45deg) scale(1.5), avoids intermediate renders and keeps animation smooth. Together, transform and opacity can create a wide range of visual effects while maintaining consistent 60fps performance even on lower-powered devices.

Following Google's performance guidelines for animations, any animation requiring property changes beyond these two should consider whether the visual impact justifies the performance cost.

Animation Performance Guidelines

60fps

Target frames per second

2

Properties to animate (transform, opacity)

0

Layout-triggering properties

16ms

Budget per frame

Hardware Acceleration with will-change

Modern browsers support hardware acceleration for animations through the will-change CSS property. This hints to the browser that an element will be animated, allowing it to prepare by creating separate compositor layers.

.element {
 will-change: transform, opacity;
}

Best practices for will-change:

  • Apply it only to elements about to be animated
  • Remove it after animation completes
  • Avoid applying to too many elements (memory cost)
  • For most cases, browser auto-optimization is sufficient

Reducing Paint and Layout Operations

Beyond animating only transform and opacity, additional strategies minimize animation impact:

  1. Stagger animations - Use delays to spread work across frames
  2. Limit scope - Animate fewer elements at once
  3. Use containment - contain: layout paint isolates elements
  4. Consider transforms - Scale instead of width, translate instead of left/top

For animations that require layout changes, using transforms to simulate those changes can maintain performance. Instead of animating width to expand an element, animate transform: scaleX() for a similar visual effect with better performance. These optimizations become increasingly important on mobile devices with limited processing power and battery constraints.

Practical Examples: Common Animation Control Patterns

Synchronized Animation Sequences

// Create staggered entrance animations
const createEntrance = async () => {
 const items = document.querySelectorAll('.list-item');
 
 const animations = Array.from(items).map((item, index) => {
 return item.animate(
 [
 { transform: 'translateY(20px)', opacity: 0 },
 { transform: 'translateY(0)', opacity: 1 }
 ],
 {
 duration: 300,
 delay: index * 100, // Stagger by 100ms
 easing: 'ease-out',
 fill: 'forwards'
 }
 );
 });
 
 // Wait for all to complete
 await Promise.all(animations.map(a => a.finished));
 console.log('Entrance complete!');
};

Accessibility: Respects Reduced Motion

// Check for reduced motion preference
const prefersReducedMotion = window.matchMedia(
 '(prefers-reduced-motion: reduce)'
).matches;

// Disable or reduce animations based on preference
const createAnimation = (element) => {
 if (prefersReducedMotion) {
 // Instant change instead of animation
 element.style.opacity = '1';
 return null;
 }
 
 return element.animate([
 { opacity: 0 },
 { opacity: 1 }
 ], { duration: 200 });
};

Dynamic Property Modification

// Adjust animation speed based on user interaction
const slider = document.getElementById('speed-control');
const animatedElement = document.querySelector('.animated');

// Get the animation's current timing
const currentAnimation = animatedElement.getAnimations()[0];

slider.addEventListener('input', (e) => {
 const speed = e.target.value;
 const baseDuration = 1000;
 const newDuration = baseDuration / speed;
 
 // Update animation timing
 currentAnimation.effect.updateTiming({
 duration: newDuration
 });
});

These patterns demonstrate how to build sophisticated animation experiences in modern web applications. By combining the Web Animations API with accessibility considerations and user interaction, you can create interfaces that are both engaging and respectful of user preferences.

Frequently Asked Questions

What's the difference between CSS animations and the Web Animations API?

CSS animations are declarative, defined in stylesheets with @keyframes, and offer limited runtime control. The Web Animations API is JavaScript-based, allowing dynamic animation creation, precise timing control, and full playback management including seeking, pausing, and reversing.

How do I pause a CSS animation mid-play?

Set `animation-play-state: paused` on the element. You can do this via inline styles (`element.style.animationPlayState = 'paused'`) or by toggling a CSS class. The animation freezes at its current position and resumes from the same point when set back to 'running'.

Can I change animation properties dynamically with JavaScript?

Yes. For CSS animations, you can modify animation-duration, animation-delay, and other properties through the element's style. For the Web Animations API, use `animation.effect.updateTiming()` to change duration, delay, and easing without recreating the animation.

How do I make animations accessible?

Check `window.matchMedia('(prefers-reduced-motion: reduce)')` and either disable animations or use subtle effects. Provide user controls to pause animations. Avoid flashing animations (more than 3 flashes per second). Consider cognitive load and ensure animations serve a purpose beyond decoration.

What causes animation lag and how do I fix it?

Animation lag usually comes from animating layout-triggering properties (width, height, margin, etc.). Fix by animating only `transform` and `opacity`. Use `will-change` to hint at upcoming animations. Limit the number of simultaneously animating elements and stagger animations with delays.

Build Performant, Interactive Web Experiences

Master modern web animation techniques to create engaging user interfaces that perform smoothly across all devices.

Sources

  1. MDN Web Docs - Using CSS Animations - Comprehensive official documentation on CSS animation syntax, @keyframes, and animation properties
  2. MDN Web Docs - Web Animations API - JavaScript API for controlling CSS animations programmatically
  3. web.dev - High-Performance CSS Animations - Google's performance optimization guide focusing on transform and opacity for 60fps animations