Web Animations API: Building Interactive Browser Animations with JavaScript

Web DevelopmentWeb Animations API: Building Interactive Browser Animations with JavaScript

Want smoother, controllable animations without loading a library?
The Web Animations API is built into the browser and gives you a video-like playback interface for CSS properties.
You define keyframes and timing in JavaScript, then play, pause, reverse, or scrub like a timeline.
No requestAnimationFrame loops, no hidden magic.
This post walks you through keyframes, timing options, playback controls, and performance tips so you can build interactive browser animations with JavaScript that feel snappy and stay in sync.

Core Overview of the Web Animations API

W6c5ZQw6Xm6tREx6brv_sg

The Web Animations API gives you a way to build and control animations in JavaScript. It’s native to the browser, so you’re not loading extra libraries or writing requestAnimationFrame loops. You define what changes, when it changes, and how long it takes. Then you start, stop, or jump to any point you want.

The API combines keyframe-based animation (like CSS @keyframes) with real-time controls (think video playback). You can pause an animation mid-flight, reverse it, slow it down, or read the current time to sync it with another event. That’s why developers reach for the Web Animations API when CSS transitions fall short, especially when you need to coordinate timing across multiple elements or respond to user input on the fly.

Here’s a minimal example. When you call animate() on an element, you pass two arguments: an array of keyframes and a timing configuration. The browser returns an Animation object that starts playing immediately.

const box = document.querySelector('.box');
box.animate([
  { transform: 'translateX(0px)', opacity: 1 },
  { transform: 'translateX(200px)', opacity: 0.5 }
], 1000);

This code moves a box 200 pixels to the right and fades it to 50% opacity over one second (1000 milliseconds). The animation runs once and stops. You just built a timed animation with two lines of JavaScript.

Syntax and Structure of the Web Animations API

3lsVFie0XwmHXoWqG8f3yA

The primary method is animate(), which lives on every Element object. You call it by passing an array of keyframes (objects describing property values at different points in time) and a timing object. Or just a single number for duration. Each keyframe is a plain JavaScript object containing CSS property-value pairs. The browser interpolates between those values over the animation’s duration.

The timing object holds properties like duration (milliseconds), iterations (how many loops), easing (the timing curve), delay (milliseconds before start), and fill (what to do after the animation ends). If you only pass a number, say 1000, the browser treats it as the duration in milliseconds and uses defaults for everything else. When the animation starts, animate() returns an Animation object. That object exposes methods and properties you use to control playback, inspect current time, or attach event listeners.

The API supports the same CSS properties you’d use in a stylesheet. Transforms, opacity, colors, and layout properties. Transform composition and timing details match the CSS spec, so the results stay consistent whether you animate via CSS or JavaScript.

Key syntax components:

  • Keyframes array: A list of objects, each defining property values at a specific moment in the animation.
  • Property/value pairs: Standard CSS properties written in camelCase (like backgroundColor) or kebab-case strings (like 'background-color').
  • Timing object: Contains duration, iterations, easing, delay, and fill mode to control how the animation progresses.
  • Returned Animation object: Provides methods (play, pause, reverse) and properties (currentTime, playbackRate) for runtime control.
  • Supported properties: Nearly all animatable CSS properties, including transforms, filters, opacity, dimensions, and colors.

Working With Keyframes

S7y_uwlLVWmq9TaTviFcTw

Keyframes tell the browser which property values to apply at different points during the animation. Each keyframe sits at a specific spot on the timeline, either defined by offset or inferred from its position in the array. When you don’t specify an offset, the browser distributes keyframes evenly. An array with three keyframes? They land at 0%, 50%, and 100% by default.

If you need finer control, add an offset property to each keyframe. Offset is a decimal between 0 and 1, where 0 is the start and 1 is the end. An offset of 0.2 positions that keyframe at 20% of the animation duration. You can cluster multiple keyframes near the start or space them unevenly to create pauses or rapid transitions. The browser interpolates smoothly between offsets, so you control pacing by shifting those breakpoints.

Keyframe capabilities:

  • Multiple transforms: Combine translate, scale, and rotate in one keyframe object, like { transform: 'translateX(50px) scale(1.2)' }.
  • Composite options: Use composite: 'add' to layer transforms instead of replacing them, letting you stack animations without conflicts.
  • Offset sequencing: Specify exact timing for each keyframe by setting offset: 0.1, offset: 0.7, and so on.
  • Mix of property types: Animate layout properties, colors, filters, and transforms all in the same keyframe array.

Timing Options and Easing Controls

TD44r_76WS2rMUBkCrrc-w

The timing object is where you define how long the animation runs, how many times it repeats, and how it accelerates or decelerates. Duration is always in milliseconds. 1000 means one second. If you set iterations: 2, the animation plays through twice. Use Infinity (the JavaScript keyword, no quotes) to loop forever.

Easing controls the rate of change between keyframes. The default is linear, which means constant speed. CSS animations default to ease, so if you want that familiar ease-in-out curve, you’ll need to specify easing: 'ease' in the timing object. You can also use 'ease-in', 'ease-out', 'ease-in-out', or a custom cubic-bezier curve like 'cubic-bezier(0.42, 0, 0.58, 1)'.

Delay and fill mode round out the timing controls. Delay (in milliseconds) pauses before the animation starts. Fill mode determines what happens after the animation finishes. 'none' resets to the original state, 'forwards' holds the final keyframe, 'backwards' applies the first keyframe during the delay, and 'both' combines backwards and forwards behavior. These options let you chain animations or keep elements in their animated state without writing extra CSS.

Controlling Playback Programmatically

Z3jY7s7JUYulkbzG_h1Xbw

When you call animate(), you get back an Animation object. That object behaves like a video player interface. You can start, stop, rewind, and scrub through the timeline. The play() method starts or resumes playback. The pause() method freezes the animation at its current position. The reverse() method flips the direction and plays backward from the current time.

The currentTime property reads or sets the animation’s position in milliseconds. If your animation is 2000 milliseconds long and you set myAnimation.currentTime = 1000, the animation jumps to the halfway point. You can use this to scrub animations in response to scroll position, slider input, or any event that should drive the timeline manually.

Playback methods:

  • play(): Starts the animation from the current time, or from the beginning if it’s finished.
  • pause(): Stops playback at the current time without resetting.
  • reverse(): Reverses the playback direction and starts playing immediately.
  • cancel(): Resets the animation and removes its effects from the element.
  • finish(): Jumps to the end of the animation and resolves the finished promise.
  • updatePlaybackRate(): Changes the speed multiplier while the animation is running (e.g., 2 for double speed).

The Animation object also fires lifecycle events. The onfinish event handler runs when the animation completes. You can chain actions or clean up resources by listening for that event. The API also exposes a finished promise, which resolves when the animation ends. Use Promise.all on an array of finished promises to wait for multiple animations at once.

Browser Support and Compatibility

yP58s5RoVViPUzpH4N8_6Q

The Web Animations API works in current versions of Chrome, Firefox, Edge, and Safari. Support is native and stable in these browsers, so you can use the API without feature flags or experimental builds. Older browsers, especially Internet Explorer, don’t support the Web Animations API at all.

If you need to support legacy browsers, a polyfill is available. The polyfill replicates the core API surface and makes it safe to use in production. Performance may vary in polyfilled environments, but the syntax remains consistent. Always test animations on the lowest browser version your project targets to catch any rendering or timing issues early.

Browser Support Level Notes
Chrome, Edge Full support Stable since Chrome 36 and Edge 79.
Firefox, Safari Full support Firefox 48+, Safari 13.1+; some older features may require polyfill.
Internet Explorer Not supported Polyfill required for any WAAPI usage.

Performance Compared to CSS Animations

nYo-wN04XNOpRFJuuf69lA

CSS animations are optimized by the browser and often run on the compositor thread, which keeps them smooth even when the main thread is busy. The Web Animations API can hit the same performance when used correctly, because the browser can offload rendering work to the GPU just like it does with CSS. The difference is control. The Web Animations API gives you runtime access to playback state, timing, and synchronization.

Performance depends on what you animate. Transform and opacity changes are compositor-friendly and run fast in both CSS and the Web Animations API. Layout properties (width, height, top, left) force reflows and can cause jank regardless of whether you’re using CSS or JavaScript. The Web Animations API doesn’t magically make slow animations fast, but it does let you pause, reverse, or coordinate animations in ways CSS can’t handle on its own.

Differences between CSS animations and the Web Animations API:

  • Control: CSS animations run automatically once defined; WAAPI lets you pause, seek, and reverse at any moment.
  • Performance: Both can run on the compositor thread if you animate transform and opacity; layout-heavy animations are slow in both.
  • Use cases: CSS is simpler for fire-and-forget transitions; WAAPI fits interactive scenarios like scrubbing, syncing, or user-driven timing.
  • Thread behavior: WAAPI animations can be offloaded to the GPU just like CSS animations, but you may still need will-change hints for layering in some browsers.

Integrating the Web Animations API With Frameworks

EILmV4AMV5GC9_f8C0QQYg

You can use the Web Animations API inside any JavaScript framework. The API operates on native DOM elements, so you just need a reference to the element you want to animate. Frameworks like React and Vue provide ways to grab element references during the component lifecycle, then you call animate() directly on those elements.

Using WAAPI in React

In React, use a ref to get a reference to a DOM element. Create the ref with useRef(), attach it to the element via the ref attribute, and then call animate() inside a useEffect hook once the component mounts. The hook runs after the element exists in the DOM, so the ref is guaranteed to point to a real node.

import { useRef, useEffect } from 'react';

function AnimatedBox() {
  const boxRef = useRef(null);

  useEffect(() => {
    if (boxRef.current) {
      boxRef.current.animate([
        { opacity: 0 },
        { opacity: 1 }
      ], 500);
    }
  }, []);

  return <div ref={boxRef} className="box">Fades in</div>;
}

This pattern keeps animation logic tied to the component lifecycle. You can store the returned Animation object in a ref if you need to pause, reverse, or inspect it later based on state changes or user interaction.

Using WAAPI in Vue

Vue provides template refs for accessing elements. In the <script setup> syntax, declare a ref variable, bind it to an element using the ref attribute, and run animate() inside the onMounted lifecycle hook. The element is ready at that point, so the ref points to the actual DOM node.

<template>
  <div ref="box" class="box">Slides in</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const box = ref(null);

onMounted(() => {
  box.value.animate([
    { transform: 'translateX(-100px)' },
    { transform: 'translateX(0)' }
  ], 600);
});
</script>

You can also trigger animations in response to reactive state changes by wrapping animate() calls inside a watch or computed property. The Web Animations API integrates cleanly because it’s just a method call on a DOM element. No special framework wrappers required.

Best Practices and Common Patterns

DuPxp9MSWCSOLZsF2664ZQ

Start by keeping animations short and purposeful. Long-running animations can bog down performance and frustrate users, especially on slower devices. Group related animations together and trigger them as a unit, so you minimize the number of reflows and repaints. Use transform and opacity properties whenever possible, because those changes can run on the compositor thread without blocking the main JavaScript thread.

Avoid animating layout properties like width, height, top, or left unless absolutely necessary. Every change to those properties forces the browser to recalculate layout for surrounding elements, which creates jank. If you must animate layout, batch changes and keep the affected area small. Use the will-change CSS property sparingly to hint the browser about upcoming animations, but remove it after the animation finishes to free up GPU memory.

Best-practice points:

  • Group animations: Start related animations together using a single timing object or by storing Animation objects in an array and calling play() on each.
  • Throttle updates: If you’re scrubbing currentTime based on scroll or mouse input, throttle or debounce the updates to avoid overwhelming the main thread.
  • Accessibility: Respect prefers-reduced-motion by checking the media query and skipping or simplifying animations for users who request reduced motion.
  • Consistent timing: Use round numbers for duration and delay (like 300, 500, 1000) to keep animations predictable and easy to sync.
  • Reduce repaints: Animate transform and opacity instead of layout properties; transforms don’t trigger reflow and can run off the main thread.

Final Words

We jumped straight into how the Web Animations API controls keyframes, timing, and playback, then covered syntax, keyframe patterns, easing, and performance trade-offs.

You also saw playback methods, browser support, framework tips for React and Vue, and best practices to keep animations smooth and accessible.

Now try a tiny animate() example in your editor. Use the web animations api to build one clear effect—once it works, you’ve taken a solid step forward.

FAQ

Q: What is waapi used for?

A: The Web Animations API (WAAPI) is used to create and control element animations with JavaScript, enabling starts, pauses, reversals, timing, and complex timeline-driven UI motion like transitions and micro-interactions.

Q: What are the 4 types of animation?

A: The four main types of animation are traditional (hand-drawn), stop-motion, 2D digital, and 3D CGI, each using different tools and techniques for motion, style, and storytelling.

Q: What is the best website to animate?

A: The best website to animate depends on your goal: CodePen for code-based web prototypes, Canva or Animaker for quick template-driven clips, and Adobe Animate for professional 2D projects.

Check out our other content

Check out other tags: