# How to Create Complex Timeline Animations with Precise Control Using Anime.js

> Master complex timeline animations with Anime.js. Learn precise control using absolute values, relative offsets, and labeled markers for seamless sequencing.

- Repository: [Julian Garnier/anime](https://github.com/juliangarnier/anime)
- Tags: how-to-guide
- Published: 2026-03-04

---

**Anime.js provides a specialized Timeline class that extends Timer to sequence tweens, timers, and nested timelines with millisecond-precision positioning through absolute values, relative offsets, and labeled markers.**

Anime.js is a lightweight JavaScript animation engine that excels at orchestrating complex timeline animations with frame-level accuracy. The library's **Timeline** class, defined in [`src/timeline/timeline.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/timeline.js), serves as the foundation for sequencing multiple animation phases while maintaining precise control over start times, overlaps, and staggering effects according to the juliangarnier/anime source code.

## Understanding the Timeline Core Architecture

The **Timeline** class extends **Timer** (from [`src/timer/timer.js`](https://github.com/juliangarnier/anime/blob/main/src/timer/timer.js)) and functions as a specialized container that tracks children's offsets, total duration, labels, and looping state. When you invoke the **`createTimeline`** factory function (exported in [`src/index.js`](https://github.com/juliangarnier/anime/blob/main/src/index.js)), it returns a fully initialized Timeline instance ready for method chaining.

The constructor initializes critical timing properties including `iterationDuration`, `duration`, and playback easing defaults. As children are added, the private helper **`addTlChild`** instantiates `JSAnimation` or `Timer` objects and triggers **`getTimelineTotalDuration`** to automatically recalculate the timeline's length without manual bookkeeping.

## Adding Children with Timeline.add

The primary method for building sequences is **`Timeline.add`**, located at line 79 of [`src/timeline/timeline.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/timeline.js). This method accepts three distinct overloads:

- **Animation configuration** – Accepts CSS selectors or DOM nodes with animation parameters
- **Timer or Timeline instances** – Allows nesting of complex subsequences  
- **Stagger functions** – Applies per-target offset calculations for distributed start times

When the third argument is a function, Anime.js treats it as a **stagger callback**. The timeline stores its current duration, iterates over parsed targets, and creates child tweens with positions computed by `parseTimelinePosition`. This architecture ensures that adding new children automatically adjusts the parent timeline's total duration.

## Achieving Precise Position Control

Precise positioning distinguishes professional animation sequences from basic chained effects. The parser in **[`src/timeline/position.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/position.js)** (specifically `parseTimelinePosition` at line 56) resolves three positioning strategies:

**Absolute positioning** specifies exact millisecond values relative to the timeline start. **Relative expressions** use string syntax like `'+=200'` (200ms after the previous child ends) or `'<-50'` (50ms before the previous child starts). **Label-based positioning** references named markers inserted via the `.label()` method, supporting compound offsets like `'pulseStart+=100'`.

The parser maintains a registry of label positions and handles "previous-child" shortcuts (`'<'`), enabling surgical control over overlap and sequencing without hardcoding millisecond values throughout your codebase.

## Staggering Animations with Per-Target Offsets

For complex timeline animations involving multiple elements, the **`stagger`** utility in [`src/utils/stagger.js`](https://github.com/juliangarnier/anime/blob/main/src/utils/stagger.js) generates position functions that calculate individual offsets based on element index, grid position, or custom ordering algorithms.

When passed as the third argument to `Timeline.add`, the timeline iterates over every target, invokes the stagger function to determine each element's offset, and inserts children at those computed positions via `parseTimelinePosition`. Stagger supports advanced features including grid-based delays, axis selection, custom easing curves, and spring physics, allowing you to create cascading effects that maintain precise temporal relationships within the broader timeline context.

## Nesting Timelines and Syncing External Animations

Production-grade animations often require composing multiple independent sequences. The Timeline class supports **nesting** through the `.add()` method—you can pass another Timeline instance as a child, which then respects the parent timeline's playback controls and positioning system.

For integration with external systems, **`Timeline.sync`** (line 56 of [`src/timeline/timeline.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/timeline.js)) attaches Web Animations API objects or custom callbacks to the timeline's playback position. This synchronization ensures that native WAAPI animations or canvas renderers remain frame-locked to the Anime.js timeline state.

## Practical Implementation Examples

This example demonstrates staggered entrances, labeled sections, relative positioning, and callback integration:

```javascript
import { createTimeline, stagger, utils } from 'animejs';

// A simple timeline with three distinct phases
const tl = createTimeline({ loop: false })
  // Phase 1 – staggered entrance of boxes
  .add('div.box', {
    opacity: [0, 1],
    translateY: [20, 0],
    duration: 800,
    easing: 'easeOutQuad',
  }, stagger(150))                 // stagger creates a 150ms offset per element

  // Phase 2 – label to mark the start of the "pulse" section
  .label('pulseStart')

  // Phase 3 – pulse animation using a relative position
  .add('div.box', {
    scale: [1, 1.2, 1],
    duration: 600,
    easing: 'easeInOutSine',
  }, '+=200')                      // start 200ms after the previous child finishes

  // Phase 4 – a callback executed at a custom label
  .add(() => console.log('All done!'), 'pulseStart+=600'); // runs 600ms after the label

// Start the timeline
tl.play();

```

For advanced compositions involving nested timelines and external animation synchronization:

```javascript
import { createTimeline, sync } from 'animejs';
import { animate } from 'animejs';

// Create a sub-timeline that animates a logo
const logoTl = createTimeline()
  .add('#logo', { rotate: 360, duration: 2000, easing: 'linear' });

// Main timeline that runs the logo animation and syncs a native WAAPI animation
const main = createTimeline({ loop: true })
  .add(logoTl)                                    // nesting
  .sync(animate('#title', {                    // sync external animation
    opacity: [0, 1],
    duration: 500,
  }), '+=100');                                  // start 100ms after logo starts

main.play();

```

## Summary

- The **Timeline** class in [`src/timeline/timeline.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/timeline.js) extends Timer and manages children through `addTlChild` and automatic duration recalculation via `getTimelineTotalDuration`.
- **`Timeline.add`** accepts animations, timers, or stagger functions, with `parseTimelinePosition` in [`src/timeline/position.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/position.js) handling absolute values, relative offsets (`+=`, `-=`), and label references.
- **`stagger`** in [`src/utils/stagger.js`](https://github.com/juliangarnier/anime/blob/main/src/utils/stagger.js) generates per-target offset functions that integrate seamlessly with timeline positioning for complex sequential effects.
- **Nesting** and **`Timeline.sync`** enable composition of sub-timelines and synchronization with external Web Animations API objects or custom render loops.
- **`createTimeline`** provides a factory method for instantiating chainable timeline instances with full playback control.

## Frequently Asked Questions

### How do I overlap animations instead of sequencing them sequentially?

Use relative positioning strings in the third parameter of `.add()`. The syntax `'-=200'` starts the new animation 200ms before the previous child ends, while `'+=0'` starts it immediately at the same time as the previous animation begins. The `parseTimelinePosition` function in [`src/timeline/position.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/position.js) handles these calculations automatically.

### Can I jump to a specific point in a complex timeline animation?

Yes. Because Timeline extends the base Timer class from [`src/timer/timer.js`](https://github.com/juliangarnier/anime/blob/main/src/timer/timer.js), you can call `.seek(time)` with a millisecond value or label string to jump to any position. The timeline updates all children to their appropriate states based on the requested global time, including handling nested timelines and synchronized external animations.

### What is the difference between stagger and absolute positioning when animating multiple elements?

**Stagger** (from [`src/utils/stagger.js`](https://github.com/juliangarnier/anime/blob/main/src/utils/stagger.js)) generates a function that calculates unique start offsets for each target based on index, grid position, or easing curves. **Absolute positioning** places every target at the same temporal location. When you pass a stagger function to `Timeline.add`, the engine iterates through targets individually, computes each offset via `parseTimelinePosition`, and inserts children at those distributed positions, creating cascading effects without manual iteration.

### How do I synchronize non-Anime.js animations with my timeline?

Use the **`Timeline.sync`** method, which accepts any object implementing the Web Animations API interface or a custom object with `currentTime` properties. The timeline will drive the external animation's playback position, ensuring it stays frame-locked during seeks, reverses, or speed changes. This is implemented in [`src/timeline/timeline.js`](https://github.com/juliangarnier/anime/blob/main/src/timeline/timeline.js) and allows integration with CSS animations, canvas renderers, or third-party libraries.