Skip to content
GSAP Timelines Explained — Step-by-Step Guide to Sequencing Animations

GSAP Timelines Explained — Step-by-Step Guide to Sequencing Animations

DodaTech Updated Jun 6, 2026 11 min read
    %%{init: {"flowchart": {"htmlLabels": true}} }%%
flowchart LR
  A["Getting Started"] --> B["You Are Here: Timelines & Sequencing"]
  B --> C["ScrollTrigger"]
  B --> D["Easing & Motion Paths"]
  A --> B
  click A "/frontend/libraries/gsap/gsap-getting-started"
  click B "/frontend/libraries/gsap/gsap-timelines-sequencing"
  click C "/frontend/libraries/gsap/gsap-scrolltrigger"
  click D "/frontend/libraries/gsap/gsap-easing-motion"
  

GSAP timelines give you a container to sequence, overlap, and orchestrate multiple animations with millisecond precision — replacing messy setTimeout chains with a clean, declarative API.

In this tutorial, you’ll learn how to build multi-step animation sequences, control timing with the position parameter, use labels as bookmarks, nest timelines for modular code, and build a full interactive choreography tool. This is the approach DodaTech engineers use to create the polished multi-step animations in Doda Browser and Durga Antivirus Pro.

What You’ll Learn

  • Create timelines and append tweens in sequence
  • Use the position parameter to overlap and delay tweens
  • Add named labels as bookmark references
  • Nest timelines inside other timelines for modular code
  • Control playback speed with timeScale()
  • Reverse, seek, and jump to specific points
  • Avoid the 6 most common timeline mistakes

Why Sequencing Matters

In real JavaScript applications, animations rarely involve a single movement. Think about a modal opening:

Security note: Understanding Gsap Timelines Sequencing helps build more secure applications — a core principle at DodaTech, where tools like Durga Antivirus Pro and Doda Browser rely on solid implementation practices.

  1. The backdrop fades in
  2. The modal scales up from the center
  3. Content fades in with a stagger
  4. A close button bounces in at the end

Without a timeline, you’d nest setTimeout calls or chain promises. It gets messy fast. A timeline gives you a single object that controls the entire sequence. You can pause it, reverse it, speed it up, or jump to any point.

The same principle applies to DodaZIP’s extraction workflow — progress bar fills, file icons drop in, completion badge bounces. Each step is a tween in a timeline, orchestrated with precision.

Prerequisites

Before diving in, make sure you’re comfortable with GSAP — gsap.to(), gsap.from(), duration, and delay. If those terms are new, start with the Getting Started guide.

Creating a Timeline — Your First Sequence

Think of a timeline as a playlist. Each tween is a track. By default, tracks play one after another in order.

// Create an empty timeline
const tl = gsap.timeline();

// Add tweens — they'll play in sequence
tl.to(".box", { x: 200, duration: 1 });   // 1. Move right
tl.to(".box", { y: 100, duration: 0.8 }); // 2. Move down
tl.to(".box", { rotation: 360, duration: 0.6 }); // 3. Spin

What happens step by step:

  1. The timeline starts with no tweens
  2. .to(".box", { x: 200 }) appends a tween — it plays immediately when the timeline starts
  3. The second .to() appends after the first — it waits for the first to finish
  4. The third waits for the second

Why this is better than setTimeout:

// Messy — hard to read, hard to maintain
setTimeout(() => gsap.to(".box", { x: 200 }), 0);
setTimeout(() => gsap.to(".box", { y: 100 }), 1000);
setTimeout(() => gsap.to(".box", { rotation: 360 }), 1800);

// Clean — everything in one place
const tl = gsap.timeline()
  .to(".box", { x: 200, duration: 1 })
  .to(".box", { y: 100, duration: 0.8 })
  .to(".box", { rotation: 360, duration: 0.6 });

Adding Tweens — All Three Methods Work

Timelines accept to(), from(), fromTo(), and set():

const tl = gsap.timeline();

// Start — hero fades in from above
tl.from(".hero", { opacity: 0, y: -40, duration: 0.8 });

// Next — title slides up
tl.to(".title", { y: 0, opacity: 1, duration: 0.6 });

// Next — button bounces in
tl.fromTo(".btn",
  { scale: 0 },
  { scale: 1, duration: 0.5, ease: "back.out(2)" }
);

// Set an immediate property (no animation)
tl.set(".badge", { opacity: 1 });

The Position Parameter — Your Precision Tool

This is the most powerful feature of timelines. The position parameter controls when a tween starts. By default, it’s "+=0" — right after the previous tween ends. But you can change it to create overlaps, gaps, or simultaneous animations.

const tl = gsap.timeline();

tl.to(".a", { x: 100, duration: 1 });
// .b starts at the SAME time as .a (position 0)
tl.to(".b", { x: 100, duration: 1 }, 0);
// .c starts 0.5s AFTER .a ends
tl.to(".c", { x: 100, duration: 1 }, "+=0.5");
// .d starts 0.3s BEFORE .a ends (overlap!)
tl.to(".d", { x: 100, duration: 1 }, "-=0.3");

Position Syntax Reference

PositionMeaning
0Start immediately (at timeline’s beginning)
1Start 1 second into the timeline
"+=1"Start 1 second after the previous tween ends
"-=0.5"Start 0.5 seconds before the previous tween ends (overlap)
"label"Start at a named label
"label+=0.5"Start 0.5 seconds after a named label
"<"Start at the same time as the previous tween’s start
"<0.3"Start 0.3 seconds after the previous tween’s start
tl.to(".red", { x: 200, duration: 1 });
tl.to(".blue", { x: 200, duration: 1 }, "-=0.5"); // overlaps by 0.5s
tl.to(".green", { x: 200, duration: 1 }, 0.2);    // starts 0.2s into timeline

Why relative positions beat absolute: If you change a tween’s duration earlier in the sequence, absolute positions break. Relative positions ("+=0.5", "-=0.3") automatically adjust.

Labels — Bookmarking Your Timeline

Labels are named markers you can jump to or use as position references.

const tl = gsap.timeline();

tl.to(".intro", { opacity: 1, duration: 0.5 });
tl.addLabel("content"); // bookmark right here
tl.from(".card", { y: 40, opacity: 0, stagger: 0.1, duration: 0.5 });
tl.to(".cta", { scale: 1.1, duration: 0.3 }, "content+=0.2");
// The CTA animation starts 0.2s after the "content" label

What labels let you do:

  • Start tweens at known points: tl.to(".x", {}, "mylabel+=0.5")
  • Seek to a spot: tl.seek("mylabel") — jumps the timeline there
  • Insert new tweens at exact locations later without recalculating timing

Nesting Timelines — Modular Animation Building

Timelines can contain other timelines. Think of it like folders organizing files.

// Create reusable sub-timelines
const fadeIn = gsap.timeline();
fadeIn.from(".card", { opacity: 0, y: 20, stagger: 0.1, duration: 0.4 });

const slideIn = gsap.timeline();
slideIn.from(".sidebar", { x: -200, duration: 0.5 });
slideIn.from(".main", { x: 100, opacity: 0, duration: 0.4 });

// Nest them in a master timeline
const master = gsap.timeline();
master.add(fadeIn);
master.add(slideIn, "-=0.2"); // overlap by 0.2s

Why nest? Each sub-timeline can be:

  • Tested independently
  • Reused across different scenes
  • Treated as a single unit in the parent

Time Scaling — Speed Control

timeScale() controls the playback speed of the entire timeline. This is incredibly useful for debugging or creating slow-motion effects.

const tl = gsap.timeline();
tl.to(".box", { x: 300, duration: 2 });
tl.to(".box", { y: 100, duration: 1 });

// Slow motion — half speed
tl.timeScale(0.5);

// Fast forward — double speed
tl.timeScale(2);

// Pause — effectively zero speed
tl.timeScale(0);

Why this matters: In Durga Antivirus Pro’s scan results, the animation might play at 1× normally, but at 2× when the user has seen it before. timeScale() makes this trivial.

Reversing & Seeking — Full Navigation Control

Timelines aren’t locked to forward-only playback. You can reverse, jump, or scrub through them.

const tl = gsap.timeline();
tl.to(".box", { x: 200, duration: 1 });
tl.to(".box", { rotation: 180, duration: 0.6 });
tl.to(".box", { scale: 1.5, duration: 0.4 });

// Play in reverse
tl.reverse();

// Jump to 50% progress
tl.progress(0.5);

// Jump to an exact time
tl.seek(1.2);

// Toggle direction
if (tl.reversed()) {
  tl.play();
} else {
  tl.reverse();
}

Callbacks — Per-Tween and Per-Timeline

Each tween inside a timeline keeps its own callbacks:

const tl = gsap.timeline();

tl.to(".box", {
  x: 200, duration: 1,
  onStart: () => console.log("Box starts moving"),
  onComplete: () => console.log("Box reached 200px")
});

tl.to(".box", {
  y: 100, duration: 1,
  onComplete: () => console.log("Box dropped")
});

You can also set timeline-level callbacks:

const tl = gsap.timeline({
  onStart: () => console.log("Full sequence started"),
  onComplete: () => console.log("Full sequence complete")
});

totalDuration() — Timeline Length

totalDuration() returns the total length of the timeline including all tweens, repeats, and yoyo. You can also stretch or shrink it.

const tl = gsap.timeline();
tl.to(".a", { x: 100, duration: 1 });
tl.to(".b", { x: 200, duration: 1.5 });
tl.to(".c", { x: 50, duration: 0.8 });

console.log(tl.totalDuration()); // 3.3 seconds

// Stretch the entire timeline to exactly 5 seconds
tl.totalDuration(5); // each tween scales proportionally

Common Mistakes

1. Chaining methods incorrectly

Each timeline method returns the timeline itself (fluent API). But if you forget to chain properly:

// Correct — chain returns the timeline
const tl = gsap.timeline()
  .to(".a", { x: 100, duration: 1 })
  .to(".b", { x: 200, duration: 1 }, "-=0.5");

// Wrong — lose reference
gsap.timeline().to(".a", { x: 100 }); // not assigned to anything!

2. Using absolute time when relative is better

Hardcoded absolute positions like 3.5 break if you edit earlier tweens.

// Fragile
tl.to(".a", { x: 100, duration: 1 });
tl.to(".b", { x: 200, duration: 1.5 }, 1.5); // 1.5 hardcoded

// Robust — using relative position
tl.to(".a", { x: 100, duration: 1 });
tl.to(".b", { x: 200, duration: 1.5 }, "+=0.2"); // adjusts automatically

3. Forgetting that tl.to() defaults to the end

If you want two tweens to play simultaneously, you must specify a position.

const tl = gsap.timeline();
tl.to(".a", { x: 100, duration: 1 });
tl.to(".b", { x: 200, duration: 1 }); // plays AFTER .a, not with it

// Fix: add position 0
tl.to(".b", { x: 200, duration: 1 }, 0);

4. Nesting without managing parent-child timing

A nested timeline’s timeScale() or reverse() does not affect the parent unless you control both.

const child = gsap.timeline();
child.to(".inner", { x: 100, duration: 2 });
const parent = gsap.timeline();
parent.add(child);

child.reverse(); // reverses child only, parent keeps going

5. Overlapping positions that create gaps

Double-check with tl.duration() to avoid unintended pauses.

// Gap: tween1 (1s) ends at 1s, tween2 starts at 1.5 — 0.5s gap
const tl = gsap.timeline();
tl.to(".a", { x: 100, duration: 1 });
tl.to(".b", { opacity: 0, duration: 0.5 }, 1.5);

6. Killing a timeline without stopping children

tl.kill() stops the parent but doesn’t guarantee child timelines stop cleanly. Use tl.pause() + tl.progress(0) for full cleanup.

Practice Questions

  1. What is the default position when adding a tween to a timeline?

    "+=0" — it starts immediately after the previous tween ends. Use an explicit position to overlap or delay.

  2. How do you make two tweens play at the same time in a timeline?

    Pass position 0 or "<" to the second tween. Example: tl.to(".a", {x:100}); tl.to(".b", {x:100}, 0);

  3. Why should you prefer relative positions ("+=0.5") over absolute values (1.5)?

    Relative positions automatically adjust when you change the duration of earlier tweens. Absolute positions require manual recalculation of every value downstream.

  4. What does tl.timeScale(2) do?

    It doubles the playback speed of the entire timeline — all tweens play twice as fast. 0.5 gives half-speed (slow motion).

  5. How do you loop a timeline forever?

    Set repeat: -1 when creating the timeline: gsap.timeline({repeat: -1, yoyo: true}). The yoyo makes it reverse on each repeat for a seamless loop.

Challenge

Create a timeline that animates a loading sequence: three dots that appear one after another (stagger), then pulse together, then fade out. Each dot should be a different color. Use labels and relative positions.

Solution
const tl = gsap.timeline({ repeat: -1 });

tl.from(".dot", {
  scale: 0, opacity: 0,
  duration: 0.3, stagger: 0.15,
  ease: "back.out(2)"
})
.addLabel("dotsIn")
.to(".dot", {
  scale: 1.3,
  duration: 0.3, yoyo: true, repeat: 1,
  ease: "power1.inOut"
})
.addLabel("dotsPulse")
.to(".dot", {
  opacity: 0, scale: 0,
  duration: 0.3, stagger: 0.1,
  ease: "power2.in"
});

FAQ

What is the default position when adding a tween to a timeline?
The default is "+=0" — the tween starts immediately after the previous tween ends. Use explicit position values to overlap or delay.
How do I make two tweens play at the same time?
Pass a position of 0 or "<" to the second and subsequent tweens. Example: tl.to(".a", {x:100}); tl.to(".b", {x:100}, 0);
Can I add a pause or gap in the timeline?
Use tl.to({}, {duration: 0.5}) with an empty target, or use tl.call(() => {}, [], "+=0.5") to insert a callback with a gap.
How do I remove a tween from a timeline after adding it?
Call tween.kill() on the specific tween instance. Store references when adding: const tw = tl.to(...).
What happens if I set totalDuration shorter than the current duration?
GSAP compresses the timeline proportionally. All tweens are scaled down to fit the new total duration.
How do I loop a timeline?
Set repeat: -1 when creating the timeline: gsap.timeline({repeat: -1, yoyo: true}).

Try It Yourself — Timeline Choreographer

Build an interactive tool with play/pause/reverse controls, speed slider, visual progress bar, and label markers. This is the kind of tool DodaTech engineers use when prototyping multi-step UI animations for Doda Browser features.

▶ Try It Yourself Edit the code and click Run

What’s Next

Now that you can choreograph multi-step sequences, learn how to tie them to scroll position.

TutorialWhat You’ll Learn
https://tutorials.dodatech.com/frontend/libraries/gsap/gsap-scrolltrigger/Scroll-driven animations with ScrollTrigger
https://tutorials.dodatech.com/frontend/libraries/gsap/gsap-easing-motion/Natural easing and motion paths
https://tutorials.dodatech.com/frontend/libraries/gsap/gsap-performance/Performance optimization at 60fps

Related topics: React Animations, Vue Animations, JavaScript Events

What’s Next

Congratulations on completing this Gsap Timelines Sequencing tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro