Chromium 84 expands Web Animations API support

Chromium 84 brings a significant update to the Web Animations API in Chrome and Edge, closing the feature gap with Firefox and Safari. The release adds promise-based orchestration, composite modes for smoother effects, and performance-oriented cleanup of finished animations. This matters for developers building complex, interactive animations that need to work consistently across browsers.

The Web Animations API is the JavaScript foundation beneath CSS animations and transitions, and it's designed to let developers compose and time effects imperatively. While element.animate() has been available since Chromium 39, the API's full feature set was previously missing in Chromium-based browsers. That changes with this release.

Beyond element.animate()

The API is no longer limited to animations you create with JavaScript. The getAnimations() method now returns all animations on an element, whether they were created via element.animate() or defined with CSS animation and transition rules. One practical use: fetching the keyframes of an existing CSS transition to determine the current transitioning state, then creating two new opacity animations to produce a cross-fade effect against a copied element.

Getting started with the API follows familiar patterns from CSS @keyframe rules. A keyframe object replaces the CSS rule:

// CSS: @keyframes slide { from { transform: translateX(0); } to { transform: translateX(100px); } }
const keyframes = [
  { transform: 'translateX(0)' },
  { transform: 'translateX(100px)' }
];

Animation timing parameters that would go into a CSS shorthand are passed as an options object:

// CSS: animation: slide 1s ease-out 1s both;
element.animate(keyframes, {
  duration: 1000,
  easing: 'ease-out',
  delay: 1000,
  fill: 'both'
});

Promise-based animation chaining

Chromium 84 adds two promise-based methods: animation.ready and animation.finished. The ready promise waits for pending playback state changes (like switching between play and pause) to take effect; finished resolves when the animation completes.

The finished promise simplifies sequencing. Consider a chain of three distinct animations: a vertical scaleY transform, a horizontal scaleX transform, and an opacity change on a child element. Using animation.finished.then(), each animation waits for the previous one to complete before starting. This approach works even when each step targets a different element and uses different options for duration or easing. Recreating that kind of ordered, multi-element sequence in CSS would require manual animation-delay calculations and careful timing-percentage management.

Play, pause, and reverse for UI interactions

Animations created with the API support .play(), .pause(), and .reverse(), which is useful for modal dialogs or expandable menus. To build a toggle interaction: create two play-pending animations (for instance, an openModal slide and an opacity fade), pause the fade, and use promises to wait for the first to finish before playing the second. When the user closes the modal, check a flag and call .reverse() on each animation to smooth the return path.

Partial keyframes for dynamic targets

You can define an animation with a single end keyframe and omit the start position entirely. The API infers the start from the element's current underlying style. This enables dynamic interactions: a mouse handler can set a new end location on each event and trigger a new animation, while an existing animation on the same element is still running. The current transition is interrupted and replaced by the new one, creating responsive, sub-frame-controlled motion.

Automatic cleanup with replaceable animations

Event-driven animation creation—like triggering a new animation on every 'mousemove' for a comet-trail effect—accumulates memory and degrades performance. Chromium 83 introduced replaceable animations to handle this automatically. A finished animation is flagged as replaceable and removed when three conditions are met:

  1. The animation is finished.
  2. One or more finished animations exist higher in composite ordering.
  3. The newer animations operate on the same properties.

You can track removals with anim.onremove, incrementing a counter each time an animation is cleaned up, to understand the impact. Additional control comes from three new members:

  • animation.replaceState — read the animation's current state: active, persisted, or removed.
  • animation.commitStyles() — write the current computed style (underlying style plus all animations in composite order) back to the element.
  • animation.persist() — mark an animation as non-replaceable to prevent its removal.

Composite modes for additive effects

The API now supports three composite modes, controlling how an animation's effect combines with the element's underlying transform or other animations: 'replace' (default), 'add', and 'accumulate'.

With replace, a final state of rotate(360deg) scale(1.4) simply overwrites the underlying transform. Additive compositing combines rotation angles and multiplies scales, yielding rotate(720deg) scale(1.96) when a rotate(360deg) scale(1.4) animation is applied. Accumulate sums both transformations component-wise, producing rotate(720deg) scale(1.8) in that same scenario.

This enables compositing distinct animations into one smooth result. A common pattern: a macro-animation moves a drop-down menu the full height of the menu, while a micro-animation applies a subtle bounce at the bottom of the slide. With the 'add' mode, the two top-position animations combine naturally.

Looking ahead

The Web Animations API work in Chromium isn't finished. Upcoming development targets include scroll-linked animation timelines via the Houdini API, mutable timelines in Web Animations Level 2, and group effects with synchronized playback. For now, Chromium 84 aligns Chrome and Edge with other modern browsers on the core API surface, making cross-browser animation code more practical.