Examples

Timeline storyboard

Independent timelines synced into one composition.

Four cards each perform their own signature move, then join for a closing wave. The whole thing plays, pauses, restarts and scrubs from a single control, the way an editor cuts separately-shot clips onto one timeline.

01
02
03
04
paused · 0%
View on GitHub

What's happening

Each card plays its own short move, like four dancers who each learned a separate routine. Those routines are lined up on one shared timeline, the way a video editor lines up clips on one track, and the four finish together with a rippling bow. Drag the scrub bar and you move through all of it at once, so halfway through you might catch Card 02 mid-flip while Card 03 hasn't started yet.

How it's built

The goal was to write and tune each card's routine without worrying about the other three, so each one gets its own useAnimeTimeline, built with autoplay: false so it sits ready but doesn't play on its own. The closing wave is a fifth timeline of the same kind, staggered across all four boxes at once.

Those five get dropped onto a sixth, master timeline with .sync(childTimeline, offsetMs), where offsetMs is how far into the master that routine starts. The offsets climb (0ms, 350ms, 850ms, 1450ms), so each card starts a beat after the last one, and the wave lands at 2300ms once the solos are done.

From there, the play, pause, and scrub controls only ever talk to that one master timeline. Calling .seek(progress * duration) on it is enough to scrub every card at once, because they're all riding the same clock.

Building it yourself

  1. Give each card its own useAnimeTimeline instance, created with autoplay: false, and author its keyframes independently of the others.
  2. Optionally split each card into two nested elements: an outer "mover" div for y (position), and an inner "rotator" div for scale and rotate. This isn't required. Anime.js keeps one shared transform cache per element, so two separate .add() calls for position and rotation would still merge cleanly into one transform even on a single div. The split is there for organization: position steps and rotation steps rarely share the same step count or duration, so giving each its own element keeps the two .add() calls independent and easy to read, instead of forcing one keyframe schedule to fit both.
  3. Add one more autoplay: false timeline for a closing move that covers every card at once, using stagger() on the shared selector rather than per-card offsets.
  4. Create a final useAnimeTimeline, also autoplay: false, to act as the master. Give it onUpdate for progress reporting and onBegin / onPause / onComplete for play state.
  5. Call .sync(childTimeline, offsetMs) on the master for each card and for the closing move, staggering the offsets so the routines play in sequence and the closing move lands after the solos.
  6. Wire Play, Pause, and Restart buttons to the master's .play(), .pause(), and .restart(), and a range slider to .seek(value * master.duration).
Copyright © 2026