Sign In

Access the AI Animation Generator and premium effects

or
Don't have an account? Create one

You've Hit Your Limit

Upgrade your plan to keep generating animations and unlock premium effects.

Monthly Yearly Save 17%
Regular
$12/mo
For individual developers
  • 50 AI generations per month
  • All text animation effects
  • Code export & copy
Deep Dive Guide

GSAP Timeline

Master the art of sequencing complex animations. Timelines are the foundation of sophisticated motion graphics, letting you orchestrate multiple animations with precise control and callbacks.

Chapter 1

What is a Timeline?

A timeline is a container that orchestrates the playback of multiple tweens (animations). Instead of managing individual animations with manual delays, timelines provide a unified interface for sequencing, controlling, and coordinating complex animation sequences. They're essential for building sophisticated motion graphics where multiple elements need to move in precise coordination.

When you create a timeline with gsap.timeline(), you get a powerful animation orchestrator. You can add tweens with .to(), .from(), or .fromTo(), and by default they're added sequentially at the end of the previous animation. But timelines give you far more control: you can insert animations at specific times, play multiple animations in parallel, add labels, include callbacks at precise moments, and control playback with play/pause/reverse/seek.

JavaScript
// Create a timeline
const tl = gsap.timeline();

// Add animations in sequence (each waits for previous to end)
tl.to('.box-1', { duration: 0.8, x: 100, ease: 'power2.out' })
  .to('.box-2', { duration: 0.8, y: -50, ease: 'power2.out' })
  .to('.box-3', { duration: 0.8, rotation: 360, ease: 'back.out' });

// Or use position parameter for parallel animations
tl.to('.box-1', { duration: 1, x: 50 })
  .to('.box-2', { duration: 1, y: 50 }, '<');  // '<' = start at same time as previous

// Timeline controls
tl.play();
tl.pause();
tl.reverse();
tl.restart();

Why Use Timelines?

  • Sequencing: Automatically handle timing between animations—no manual delay calculations
  • Reusability: Create animation sequences that can be played, paused, reversed, or scrubbed
  • Callbacks: Execute functions at specific points in the sequence
  • Control: Unified interface for managing complex multi-element animations
  • Organization: Group related animations together for cleaner code

Timelines are the difference between animating individual elements independently and orchestrating a coordinated sequence. Once you master timelines, you unlock the ability to create professional-quality motion graphics with elegant, readable code.

Every professional GSAP animation typically uses timelines. They're so fundamental that learning them well is essential to becoming proficient with GSAP. This guide covers everything you need to know.

Chapter 2

The Position Parameter

The position parameter is the most critical and powerful feature of timelines. It determines where in the timeline an animation gets inserted—allowing you to create sequential animations, parallel animations, overlapping animations, or precise timing. Understanding the position parameter is the key to unlocking timeline mastery.

The position parameter is passed as the third argument to timeline methods. It can be an absolute time, a relative offset, a label reference, or a special symbol. Each type gives you different control over the timing relationship between animations.

JavaScript
const tl = gsap.timeline();

// 1. ABSOLUTE TIME - Add at exactly 1 second into timeline
tl.to('.box', { duration: 0.5, x: 100 }, 1);

// 2. RELATIVE TO END - Add after previous ends
tl.to('.box', { duration: 0.5, x: 100 })
  .to('.box', { duration: 0.5, y: 50 }, '+=0.5'); // Wait 0.5s after previous ends

// 3. RELATIVE TO START OF PREVIOUS - Overlap animations
tl.to('.box', { duration: 1, x: 100 })
  .to('.box', { duration: 1, y: 50 }, '<0.5'); // Start 0.5s before previous ends

// 4. AT START OF PREVIOUS (Parallel)
tl.to('.box', { duration: 1, x: 100 })
  .to('.box', { duration: 1, rotate: 360 }, '<'); // Start at same time

// 5. LABEL-BASED - Jump to labeled positions
tl.addLabel('halfway');
tl.to('.box', { duration: 0.5, x: 100 })
  .to('.box', { duration: 0.5, y: 50 }, 'halfway+=0.2');

// 6. RELATIVE TO END OF PREVIOUS - Before it ends (negative)
tl.to('.box', { duration: 1, x: 100 })
  .to('.box', { duration: 1, scale: 1.2 }, '-=0.5'); // Start 0.5s before previous ends

Position Parameter Reference

Type Example Description
Absolute 1.5 Insert at exactly 1.5 seconds into timeline
After previous '>' or '>=' Add after previous animation ends (default)
After offset '+=0.5' Add 0.5 seconds after previous ends
At start of previous '<' Start at same time as previous (parallel)
Before offset '<0.3' Start 0.3s before previous ends (overlap)
Overlap (negative) '-=0.5' Start 0.5s before previous ends
Label 'myLabel' Jump to a labeled position in timeline
Label offset 'myLabel+=0.3' Start 0.3s after a label

The position parameter is passed as the third argument to .to(), .from(), and .fromTo(). It's the key to creating everything from simple sequences to complex overlapping animations. Master this, and you can orchestrate virtually any animation scenario.

Pro tip: Use relative positioning ('<', '<0.2', '-=0.3') for flexibility. This makes your timelines more maintainable—if you change animation durations, the timing automatically adjusts. Absolute times work, but relative positioning creates more resilient code.

Chapter 3

Labels & Positioning

Labels are named waypoints in your timeline that make positioning easier and more readable. Instead of calculating exact times, you name important moments in your animation sequence and reference them by name. This makes your code more maintainable and self-documenting.

Use addLabel() to create a label at the current position in the timeline, then reference that label when adding new animations. You can also offset labels with += and -= syntax, allowing precise positioning relative to named markers.

Start
Midpoint
EndPhase
JavaScript
const tl = gsap.timeline();

// Add animations
tl.to('.box', { duration: 1, x: 100, ease: 'power2.out' })

// Add a label at current timeline position
  .addLabel('midpoint')

// Continue animating, using the label for positioning
  .to('.box', { duration: 1, y: 50, ease: 'power2.out' })
  .addLabel('endPhase')
  .to('.box', { duration: 1, rotation: 360, ease: 'back.out' });

// Reference labels with offset
tl.to('.box', { duration: 0.5, scale: 1.2 }, 'midpoint+=0.2');

// Jump to labeled positions
tl.seek('midpoint');      // Jump to midpoint
tl.seek('endPhase');      // Jump to endPhase
tl.seek('midpoint+=0.5'); // Jump to 0.5s after midpoint

// Get time of a label
const midpointTime = tl.getLabelTime('midpoint');
console.log(midpointTime); // Output: 1 (the time in seconds)

Labels Best Practices

  • Use descriptive label names that describe the animation phase (e.g., 'entrance', 'transition', 'exit')
  • Labels create checkpoints you can jump to or reference for new animations
  • Combine labels with offsets ('myLabel+=0.5') for precise positioning
  • Use getLabelTime() to get the exact time of a label for calculations
  • Labels make your code more maintainable than absolute times or relative calculations
Chapter 4

Timeline Controls

Timelines aren't just for one-shot animations—they give you complete playback control. You can play, pause, resume, reverse, restart, seek to specific points, adjust playback speed, and check progress. These controls make timelines perfect for interactive animations where users need to trigger, pause, or manipulate animations.

The most commonly used methods are .play(), .pause(), .reverse(), .restart(), .seek(), .progress(), and .timeScale(). You'll use these constantly in interactive applications.

0.0s / 3.0s
JavaScript
const tl = gsap.timeline({ paused: true }); // Start paused

tl.to('.box', { duration: 1, x: 100 })
  .to('.box', { duration: 1, y: 50 })
  .to('.box', { duration: 1, rotation: 360 });

// Playback controls
tl.play();           // Start playing
tl.pause();          // Pause at current position
tl.resume();         // Resume from pause (same as play when paused)
tl.reverse();        // Play in reverse
tl.restart();        // Jump to start and play
tl.paused(true);     // Pause (setter)
tl.paused();         // Get paused state (getter)

// Seek to specific time
tl.seek(0.5);        // Jump to 0.5 seconds
tl.seek(1.5, false); // Jump without triggering callbacks

// Progress (0-1 scale)
tl.progress(0.5);    // Jump to 50% through timeline
console.log(tl.progress()); // Get current progress

// Playback speed
tl.timeScale(1);     // Normal speed
tl.timeScale(2);     // 2x speed (fast-forward)
tl.timeScale(0.5);   // 0.5x speed (slow-motion)

// Duration and time
console.log(tl.duration()); // Total duration in seconds
console.log(tl.time());     // Current playhead position

// Jump to label
tl.seek('labelName');
tl.seek('labelName+=0.5');

Control Methods Reference

Playback:

  • .play() - Start playing
  • .pause() - Pause at current point
  • .reverse() - Play backwards
  • .restart() - Jump to start and play

Position:

  • .seek(time) - Jump to specific time
  • .progress(value) - Set position 0-1 scale
  • .time() - Get current playhead time

Speed:

  • .timeScale(speed) - Adjust playback speed
  • .duration() - Get total timeline length

State:

  • .paused() - Check if paused
  • .isActive() - Check if animating
Chapter 5

Defaults & Configuration

Timeline configuration lets you set defaults for all animations added to that timeline, reducing repetition and making your code cleaner. You can specify default duration, easing, delay, and other properties that apply to all tweens unless explicitly overridden. You can also configure repeat, yoyo, onComplete callbacks, and more.

Pass a configuration object when creating the timeline with gsap.timeline({ ... }). Any properties you set as defaults will apply to all animations added to that timeline, but individual animations can override defaults by specifying their own values.

JavaScript
// Timeline WITH defaults applied
const tlWithDefaults = gsap.timeline({
  defaults: {
    duration: 1,           // All tweens default to 1s
    ease: 'power2.out',    // All tweens use power2.out
    stagger: 0.2           // Stagger all animations
  },
  repeat: 1,               // Repeat entire timeline once
  yoyo: true               // Reverse after playing
});

// Add animations - they inherit defaults
tlWithDefaults
  .to('.box1', { x: 100 })           // Uses default duration and ease
  .to('.box2', { y: 50 })            // Uses default duration and ease
  .to('.box3', { rotation: 360 });   // Uses default duration and ease

// Override defaults for specific tween
tlWithDefaults.to('.box4', {
  x: 200,
  duration: 2,  // Override duration
  ease: 'elastic.out'  // Override ease
});

// Timeline configuration options
gsap.timeline({
  delay: 0.5,              // Delay before timeline starts
  paused: true,            // Start paused
  repeat: -1,              // Repeat infinitely
  yoyo: true,              // Reverse animations
  repeatDelay: 0.5,        // Delay between repeats
  defaults: {
    duration: 1,
    ease: 'power2.inOut'
  },
  onStart: () => console.log('Timeline started'),
  onComplete: () => console.log('Timeline finished'),
  onUpdate: () => console.log('Timeline updated')
});

Configuration Options

  • defaults: Object of properties applied to all tweens (duration, ease, stagger, etc.)
  • delay: Delay before timeline starts (in seconds)
  • paused: Start paused if true (default: false)
  • repeat: Number of times to repeat the entire timeline (-1 for infinite)
  • yoyo: Reverse animations after each play (requires repeat)
  • repeatDelay: Delay between each repeat cycle
  • onStart/onComplete/onUpdate: Callback functions at specific points
Chapter 6

Nested Timelines

Nested timelines let you add a timeline as an animation within another timeline. This is incredibly powerful for organizing complex sequences into reusable components. You can create a timeline for a specific animation sequence, then add that entire timeline to a master timeline, treating it as a single unit that respects the parent timeline's position parameter and timing.

This pattern is essential for building sophisticated animation systems where you want to create reusable, composable animation sequences. A button click animation, a menu open animation, a page transition—each can be built as its own timeline and then orchestrated into a larger sequence.

JavaScript
// Create child timeline 1
const tl1 = gsap.timeline();
tl1.to('.box1', { duration: 0.5, x: 100, ease: 'power2.out' })
   .to('.box1', { duration: 0.5, y: 50, ease: 'power2.out' });

// Create child timeline 2
const tl2 = gsap.timeline();
tl2.to('.box2', { duration: 0.5, rotation: 180 })
   .to('.box2', { duration: 0.5, scale: 1.2 });

// Create master timeline
const master = gsap.timeline();

// Add child timelines (they animate as units)
master.add(tl1)              // Add tl1, then tl2 sequentially
      .add(tl2)              // This waits for tl1 to finish
      .add(tl1, '<');        // Add tl1 again, in parallel with tl2

// You can also use position parameters
master.add(tl1, 0)           // Add at 0 seconds
      .add(tl2, '+=0.2')     // Add 0.2s after tl1 ends
      .add(tl1, 'label');    // Add at a labeled position

// Access child timeline properties
console.log(tl1.duration());      // Get duration of child
console.log(master.duration());   // Get total master duration

// Reverse entire structure
master.reverse(); // Reverses all nested animations

// Control master timeline
master.play();
master.pause();
master.seek(2);  // Seek through entire nested structure

Nested Timeline Patterns

  • Reusable components: Build animations as standalone timelines, add to master when needed
  • Better organization: Group related animations together in separate timelines
  • Easier testing: Test child timelines independently before combining
  • Dynamic sequencing: Add timelines conditionally based on user interaction
  • Unified control: Control entire nested structure through master timeline
Chapter 7

Callbacks & Events

Callbacks let you execute functions at specific points during timeline playback—onStart (when animation begins), onUpdate (every frame), onComplete (when animation finishes), and onRepeat (when repeating). This is essential for synchronizing animations with other code, triggering next steps, or logging events.

Callbacks can be attached to the timeline itself or to individual tweens. Timeline callbacks are global to the entire sequence, while tween callbacks fire only for that specific animation. You can also use eventCallback() to add or modify callbacks after the timeline is created.

JavaScript
// Create timeline with callbacks
const tl = gsap.timeline({
  onStart: () => console.log('Timeline started'),
  onUpdate: () => console.log('Frame update'),
  onComplete: () => console.log('Timeline complete'),
  onRepeat: () => console.log('Timeline repeated')
});

// Add animations
tl.to('.box', {
  duration: 1,
  x: 100,
  // Tween-level callbacks
  onStart: () => console.log('Box animation started'),
  onComplete: () => console.log('Box animation complete')
});

// Add callback at specific timeline position
tl.call(() => console.log('Halfway through!'), 'halfway');
tl.addLabel('halfway', 0.5);

// Modify callbacks after timeline creation
tl.eventCallback('onComplete', () => {
  console.log('New completion callback');
});

// Get current callback
const currentCallback = tl.eventCallback('onComplete');

// Remove callback
tl.eventCallback('onComplete', null);

// Callback context (this)
tl.eventCallback('onComplete', function() {
  console.log(this.targets()); // Get animated elements
}, null, tl);

// Useful callback patterns
const tl2 = gsap.timeline({
  onComplete: () => {
    // Play next timeline
    tl2.restart();
    // Or trigger other actions
    // updateUI();
    // loadNextContent();
  }
});

Callback Types

Timeline Callbacks:

  • onStart - When timeline begins
  • onUpdate - Every frame (performance warning!)
  • onComplete - When timeline finishes
  • onRepeat - When repeating

Timeline Methods:

  • .call(fn, position) - Execute at position
  • .eventCallback(type, fn) - Set/get callback
  • .getTweensOf(target) - Get tweens targeting element
Chapter 8

ScrollTrigger Integration

ScrollTrigger integration allows you to tie entire timelines to scroll position. Instead of animations playing on load or on command, they trigger when elements enter the viewport. You can even create "scrubbing" effects where scroll position directly controls timeline progress, creating interactive scroll stories and parallax effects.

Combine timelines with ScrollTrigger by passing a scrollTrigger object to any tween, or to the timeline itself. This creates powerful scroll-driven animations that feel responsive and connected to user interaction. Learn more in our complete ScrollTrigger guide.

Scroll down to trigger animation

JavaScript
// Timeline triggered by scroll
const tl = gsap.timeline({
  scrollTrigger: {
    trigger: '.trigger-element',
    start: 'top 80%',           // When top of element hits 80% of viewport
    end: 'top 20%',             // Animation end point
    scrub: 1,                   // Tie to scroll (1 = 1s smoothing)
    markers: true,              // Debug markers
    toggleActions: 'play none none none'
  }
});

tl.to('.box', { duration: 2, x: 100 })
  .to('.box', { duration: 2, y: 50 })
  .to('.box', { duration: 2, rotation: 360 });

// Scrubbing: Link timeline progress directly to scroll
gsap.timeline({
  scrollTrigger: {
    trigger: '.section',
    start: 'top top',
    end: 'bottom bottom',
    scrub: true,     // Instant (no smoothing)
    // or scrub: 2   // 2 seconds of smoothing
  }
}).to('.element', { x: 500 });

// Pin element while timeline plays
const tl2 = gsap.timeline({
  scrollTrigger: {
    trigger: '.hero',
    pin: true,       // Pin element to viewport
    start: 'top top',
    end: '+=1500',   // Duration of pin (1500px of scroll)
    scrub: 1,
    snap: {
      snapTo: 0.5,   // Snap to nearest 0.5 increment
      duration: 0.3,
      delay: 0
    }
  }
});

tl2.to('.hero-content', { opacity: 0 });

// Multiple timelines with ScrollTrigger
document.querySelectorAll('.section').forEach((section, i) => {
  gsap.timeline({
    scrollTrigger: {
      trigger: section,
      start: 'top 75%'
    }
  }).from(section, { opacity: 0, y: 50 });
});

ScrollTrigger + Timeline Patterns

  • Trigger animations on scroll: Play timeline when element enters viewport
  • Scrub for control: Link timeline progress to scroll position for interactive effects
  • Pin + scrub: Keep element visible while timeline plays, advancing with scroll
  • Snap: Snap to specific timeline positions as user scrolls
  • Multiple timelines: Create separate timelines for different sections, all scroll-triggered
  • Performance: ScrollTrigger automatically optimizes with GPU acceleration

For comprehensive ScrollTrigger coverage including markers, advanced positioning, and complex scenarios, see our ScrollTrigger guide.

Timeline Best Practices

Use Defaults

Set timeline defaults for duration and easing to reduce repetition and make timelines more maintainable. Override only when necessary for specific tweens.

Label Wisely

Use labels for important timeline positions instead of absolute times. This makes your code more maintainable when you adjust animation durations.

Compose Reusable

Build animations as functions that return timelines. Nest them into master timelines. This enables reusable, testable animation components.

Use Relative Positioning

Prefer relative position parameters ('<', '+=0.2') over absolute times. They're more flexible when timings change.

Kill on Unmount

In frameworks (React, Vue), kill timelines when components unmount: tl.kill(). Prevents memory leaks and unexpected animations.

Respect Motion Settings

Check prefers-reduced-motion and disable animations for users with motion sensitivity. Animations are enhancements, not requirements.

Timeline Questions

A tween position is where that specific animation inserts into the timeline. A timeline position is where a child timeline inserts into its parent. Both use the same syntax (absolute time, relative offsets, labels), but apply at different scales. The tween position is relative to the timeline; the child timeline position is relative to the master timeline.

Set the repeat option in the timeline config: gsap.timeline({ repeat: 1 }) repeats once (plays twice total). Use repeat: -1 for infinite repeat. Add yoyo: true to reverse between repeats. You can also set repeatDelay for gaps between cycles.

Yes! Use the .progress(value) method on mouse events. Listen to click or drag on a progress bar, calculate the percentage (0-1), and call tl.progress(percentage). This lets users scrub through the timeline by clicking or dragging a progress indicator.

Use the onComplete callback in the timeline config or set it with eventCallback('onComplete', fn). This function fires when the timeline finishes playing. Useful for triggering next steps, updating UI, or playing another timeline.

Both work, but they mean different things. '-=0.5' means start 0.5s before the previous animation ends (overlap). '+=0.5' means start 0.5s after it ends (gap). '<' means start at the same time. Choose based on the timing relationship you want.

Not directly, but you can use .seek() and .reverse() together, or create a separate timeline for the section you want to control. For precise control, use callbacks and .progress() to position the timeline at specific points.

Timelines are sealed once created, but you can keep adding to them with .to(), .from(), etc. If you need to change existing animations, kill the timeline and recreate it, or design your code to accept parameters that adjust animation values before creation.

Timelines and tweens have similar performance—GSAP is highly optimized. The real benefit of timelines is code organization and manageability. Use timelines because they make complex sequences easier to write, maintain, and debug. Performance is the same.

Ready to Master Timelines?

You now have all the knowledge to create sophisticated, professional animation sequences. From simple animations to complex nested timelines with scroll integration, you can build anything. The key is practice—start building, experiment with position parameters, and create amazing animations.