The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Create an animationconst animation = element.animate(keyframes, { duration: 300, easing: 'ease-out' })View examples
Use property-indexed keyframeselement.animate({ opacity: [0, 1], transform: ['scale(.95)', 'scale(1)'] }, 200)View examples
Set keyframe offsets[{ opacity: 0, offset: 0 }, { opacity: 1, offset: 0.4 }, { opacity: 1, offset: 1 }]View examples
Pause playbackanimation.pause()View examples
Reverse safelyanimation.reverse()View examples
Change playback rateanimation.updatePlaybackRate(0.5)View examples
Seek to a timeanimation.currentTime = 150View examples
Await completionawait animation.finishedView examples
Await pending operationsawait animation.readyView examples
Commit computed valuesanimation.commitStyles(); animation.cancel()View examples
Inspect element animationsconst animations = element.getAnimations({ subtree: true })View examples
Read computed timingconst timing = animation.effect.getComputedTiming()View examples
Change effect timinganimation.effect.updateTiming({ duration: 500, iterations: 2 })View examples
Detect reduced motionconst reduce = matchMedia('(prefers-reduced-motion: reduce)').matchesView examples

The Web Animations API exposes the timing model shared by CSS animations and transitions. It can create effects, control playback, inspect running animations, and await lifecycle milestones without frame-by-frame style writes. Preserve meaningful final state outside the animation, respect reduced-motion preferences, cancel orphaned effects, and feature-test newer methods.

Step by step

Detailed examples

01

Create effects from valid keyframes and explicit timing

Element.animate() constructs a KeyframeEffect and Animation bound to the document timeline. Keyframes may be an array of objects or property-indexed arrays. Property names use JavaScript form such as backgroundColor; custom properties retain their -- name. Missing offsets are distributed, while easing on an individual keyframe applies from that keyframe to the next. Animate transform and opacity when they express the design, but do not claim every such animation is guaranteed compositor-only.

Animate an entry with explicit endpoints
const animation = card.animate([
  { opacity: 0, transform: 'translateY(12px)', offset: 0 },
  { opacity: 1, transform: 'translateY(0)', offset: 1 },
], { duration: 240, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'both' });
Back to quick reference ↑
02

Control playback through the animation timeline

play(), pause(), reverse(), finish(), cancel(), playbackRate, and currentTime modify timing rather than repeatedly writing styles. updatePlaybackRate() preserves current position more predictably than assigning playbackRate during running playback. currentTime can be null when unresolved. finish() can throw InvalidStateError for an infinite-duration animation or unusable playback rate. UI controls should reflect playState rather than assuming a command took effect synchronously.

Toggle playback and preserve progress
async function toggle(animation) {
  if (animation.playState === 'running') animation.pause();
  else animation.play();
  await animation.ready;
  status.textContent = animation.playState;
}
function setSpeed(animation, rate) {
  if (Number.isFinite(rate) && rate !== 0) animation.updatePlaybackRate(rate);
}
Back to quick reference ↑
03

Handle promises, cancellation, and durable end state

ready represents the current pending play or pause task. finished is replaced whenever an animation leaves the finished state and rejects with AbortError when cancel() runs, so catch expected cancellation. Fill modes can retain an effect indefinitely and keep it in the cascade. Prefer writing the semantic final class or state, then canceling. commitStyles() can preserve the current computed effect in inline styles where supported, but it changes author styles and should be used intentionally.

Finish by committing application state
async function removeWithAnimation(element) {
  const animation = element.animate({ opacity: [1, 0] }, { duration: 180 });
  try {
    await animation.finished;
    element.hidden = true;
  } catch (error) {
    if (error.name !== 'AbortError') throw error;
  } finally {
    animation.cancel();
  }
}
Back to quick reference ↑
04

Inspect both CSS-created and script-created animations

Element.getAnimations() returns animations affecting the element, and subtree true includes descendants; Document.getAnimations() covers the document. Results include CSS animations and transitions as well as WAAPI effects. KeyframeEffect.getKeyframes() returns computed keyframes, while getComputedTiming() reports resolved timing and progress. Inspection is a snapshot; animations may finish or be replaced afterward. Avoid polling large subtrees continuously.

Wait for current subtree animations
async function waitForMotion(container) {
  const animations = container.getAnimations({ subtree: true });
  await Promise.allSettled(animations.map(animation => animation.finished));
}
for (const animation of document.getAnimations()) {
  console.debug(animation.playState, animation.effect?.getComputedTiming().progress);
}
Back to quick reference ↑
05

Update timing and understand effect composition

KeyframeEffect.updateTiming() changes delay, duration, iterations, direction, fill, and easing. Composite operations determine whether an effect replaces, adds to, or accumulates with lower-priority values, but additive behavior depends on the property's animation type and implementation support. Multiple effects participate in a defined composite order. Prefer simple replace composition for portable UI and feature-test options such as composite or newer timeline types before relying on them.

Retune one existing effect
const effect = animation.effect;
if (!(effect instanceof KeyframeEffect)) throw new TypeError('Expected a keyframe effect');
effect.updateTiming({ duration: 500, iterations: 2, direction: 'alternate' });
const frames = effect.getKeyframes();
console.log(frames.map(({ offset, computedOffset }) => ({ offset, computedOffset })));
Back to quick reference ↑
06

Respect reduced motion and preserve interaction semantics

Motion can trigger vestibular symptoms or obscure focus and state changes. Query prefers-reduced-motion and listen for changes when the setting should affect a long-lived application. Remove nonessential translations, zooms, parallax, and looping motion; a near-zero duration is not always equivalent because timing events and flashes may remain. Never use animation as the only state signal, never steal focus at completion, and keep controls operable while an animation is canceled or skipped.

Choose a stable reduced-motion path
const motionPreference = matchMedia('(prefers-reduced-motion: reduce)');
function reveal(element) {
  if (motionPreference.matches) {
    element.hidden = false;
    return null;
  }
  element.hidden = false;
  return element.animate({ opacity: [0, 1] }, { duration: 200 });
}
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumWeb Animationsw3.org
  2. CSS Working GroupWeb Animations Level 1: Editors' Draftdrafts.csswg.org
  3. World Wide Web ConsortiumCSS Animations Level 1w3.org
  4. World Wide Web ConsortiumCSS Transitions Level 2w3.org
  5. World Wide Web ConsortiumMedia Queries Level 5w3.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback