Web Animations API Cheat Sheet
Native browser Element.animate() syntax, keyframes, timing options, and playback control without a CSS or JS animation library.
Element.animate() Basics
Animate an element directly via JS with keyframes and options.
const el = document.querySelector('.box')const animation = el.animate( [ { transform: 'translateX(0)', opacity: 1 }, { transform: 'translateX(200px)', opacity: 0.5 }, ], { duration: 500, easing: 'ease-in-out', fill: 'forwards', iterations: 1, })
Controlling Playback
Pause, reverse, and react to animation lifecycle events.
animation.pause()animation.playbackRate = 2 // 2x speedanimation.reverse()animation.play()animation.finished.then(() => { console.log('animation complete')})animation.onfinish = () => el.classList.add('done')animation.oncancel = () => console.log('cancelled')
Property-Indexed Keyframes
Shorthand syntax when each property's values simply interpolate in order.
el.animate( { transform: ['scale(1)', 'scale(1.2)', 'scale(1)'], offset: [0, 0.5, 1], // explicit keyframe offsets }, { duration: 800, iterations: Infinity, easing: 'ease-in-out' })
KeyframeEffectOptions Reference
The timing dictionary passed as the second argument to animate().
- duration- length of one iteration in ms
- easing- timing function, e.g. 'linear', 'ease', 'cubic-bezier(.17,.67,.83,.67)'
- delay / endDelay- ms before start / after end before the animation is considered finished
- iterations- number of repeats, or Infinity
- direction- 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'
- fill- 'none' | 'forwards' | 'backwards' | 'both' — whether styles persist outside active phase
- composite- 'replace' | 'add' | 'accumulate' — how this animation combines with others on the same property
Reusable KeyframeEffect Objects
Construct an effect independently of an animation to share the same motion definition across multiple elements.
const pulse = new KeyframeEffect( null, [{ transform: 'scale(1)' }, { transform: 'scale(1.08)' }, { transform: 'scale(1)' }], { duration: 600, iterations: Infinity, easing: 'ease-in-out' })document.querySelectorAll('.badge').forEach((el) => { const effect = pulse.clone() effect.target = el new Animation(effect, document.timeline).play()})
document.getAnimations() to Interrogate/Cancel
Enumerate every running animation on the page (or a subtree) to pause, cancel, or await them in bulk.
// cancel every animation on a subtree, e.g. before removing itconst node = document.querySelector('.modal')node.getAnimations({ subtree: true }).forEach((a) => a.cancel())// wait for every animation on the page to settle before a screenshot/test assertionawait Promise.all(document.getAnimations().map((a) => a.finished))
Sequencing Animations with .finished
Chain or fan-out animations declaratively by awaiting each Animation's finished promise instead of using setTimeout.
async function sequence(el) { await el.animate({ opacity: [0, 1] }, { duration: 200, fill: 'forwards' }).finished await el.animate({ transform: ['translateY(20px)', 'translateY(0)'] }, { duration: 300, easing: 'ease-out', fill: 'forwards' }).finished el.animate({ transform: ['scale(1)', 'scale(1.05)', 'scale(1)'] }, { duration: 400 })}
Cumulative Motion with iterationComposite
Make each successive iteration build on the previous one's end state instead of resetting, e.g. for an ever-rotating dial.
document.querySelector('.dial').animate( [{ transform: 'rotate(0deg)' }, { transform: 'rotate(45deg)' }], { duration: 1000, iterations: 8, iterationComposite: 'accumulate', // each loop adds 45deg to the last composite: 'add', })
Advanced Animation Object Reference
Members beyond basic play/pause you'll need for orchestration and diagnostics.
- animation.pending- true while the animation is waiting to start/pause due to a pending task (e.g. before first paint)
- animation.ready- promise resolving once a pending play/pause/reverse actually takes effect
- animation.updatePlaybackRate(rate)- smoothly ramps to a new speed over the current iteration instead of snapping like `playbackRate =`
- animation.persist()- keeps a transition/CSS-animation-backed Animation object alive after it would otherwise be auto-removed
- animation.replaceState- 'active' | 'persisted' | 'removed' — whether the browser auto-removed a finished, replaced animation
- KeyframeEffect.getKeyframes() / setKeyframes()- read or rewrite an effect's keyframes after construction
- document.timeline- the default DocumentTimeline; pass a custom AnimationTimeline (e.g. ScrollTimeline) to `new Animation(effect, timeline)`
Use `animation.commitStyles()` plus `animation.cancel()` instead of leaving `fill: 'forwards'` animations running indefinitely — committed styles move the final state into the actual CSS, freeing the browser from holding an active animation object in memory.