Complex Animation Sequences

Lesson 14

A complex animation sequence means multiple elements moving in a coordinated order — a logo appears, a headline slides in, features stagger up, an image scales in, a button arrives last. The whole thing feels choreographed. GSAP timelines make this precise and controllable.

The prompting approach

Don’t describe everything at once. Write a numbered list of what happens when — Claude Code reads it like a script and translates each step into a timeline tween.

Prompt:
"페이지 로드 시 다음 순서로 나타나는 GSAP timeline을 만들어줘.
paused: true로 시작하고 Play 버튼으로 실행되게 해줘.

1. .brand (로고) — scale 0.8→1, opacity 0→1, 0.5s
2. .headline span (두 줄 제목) — y 60→0, opacity 0→1, stagger 0.15s, 0.7s, -=0.2로 겹치기
3. .feature-item (4개 항목) — x -20→0, opacity 0→1, stagger 0.12s, -=0.3
4. .mockup (이미지 플레이스홀더) — scale 0.92→1, opacity 0→1, 0.6s, 3번과 동시에 시작
5. .cta 버튼 — y 16→0, opacity 0→1, 0.4s, -=0.2
ease는 모두 power2.out"

The position parameter

The second argument to tl.from() controls when a tween starts relative to the timeline:

PositionMeaning
'-=0.3'Start 0.3s before the previous tween ends
'+=0.1'Start 0.1s after the previous tween ends
'<'Start at the same time as the previous tween
'<0.3'Start 0.3s after the previous tween started
'myLabel'Jump to a named label

These overlapping positions are what make sequences feel fluid instead of choppy.

Controlling timelines

Once you have a timeline, you can wire up controls:

tl.play();      // Start from current position
tl.pause();     // Freeze in place
tl.reverse();   // Play backwards from current position
tl.restart();   // Jump to start and play forward
tl.seek(0.5);   // Jump to 0.5s mark (does not play)

Ask Claude Code to add these controls with a simple prompt:

"Play / Pause / Reverse / Restart 버튼 4개를 추가하고
각각 tl.play(), tl.pause(), tl.reverse(), tl.restart()에 연결해줘."

Timeline progress bar

To show animation progress visually, use onUpdate:

const tl = gsap.timeline({
  onUpdate: () => {
    bar.style.width = (tl.progress() * 100) + '%';
  }
});

What the demo shows

The demo is a product showcase landing page hero. A logo, headline, feature list, mockup image, and CTA button all appear through a coordinated GSAP timeline. Four control buttons let you play, pause, reverse, and restart the sequence. A thin progress bar below the controls tracks animation progress in real time.

Source Code script.js
// ── Timeline setup ────────────────────────────────────────────
// paused: true → user controls playback via the buttons below
const progressFill = document.getElementById('progressFill');
const progressText = document.getElementById('progressText');
const hint = document.getElementById('hint');

const tl = gsap.timeline({
  paused: true,
  defaults: { ease: 'power2.out' },
  onUpdate: () => {
    const pct = Math.round(tl.progress() * 100);
    progressFill.style.width = pct + '%';
    progressText.textContent = pct + '%';
  },
  onStart: () => {
    hint.classList.add('hidden');
  },
});

// Step 1 — Brand mark scales in
tl.from('.brand', {
  scale: 0.8,
  opacity: 0,
  duration: 0.5,
})

// Step 2 — Headline lines slide up with stagger, overlaps step 1 by 0.2s
.from('.headline span', {
  y: 60,
  opacity: 0,
  stagger: 0.15,
  duration: 0.7,
}, '-=0.2')

// Step 3 — Feature list items come in from the left, overlaps step 2 by 0.3s
.from('.feature-item', {
  x: -20,
  opacity: 0,
  stagger: 0.12,
  duration: 0.5,
}, '-=0.3')

// Step 4 — Mockup scales in, starts at the same time as step 3 + 0.3s offset
.from('.mockup', {
  scale: 0.92,
  opacity: 0,
  duration: 0.6,
}, '<0.3')

// Step 5 — CTA slides up, overlaps step 4 by 0.2s
.from('.cta', {
  y: 16,
  opacity: 0,
  duration: 0.4,
}, '-=0.2');

// ── Controls ──────────────────────────────────────────────────
document.getElementById('btnPlay').addEventListener('click', () => {
  tl.play();
});

document.getElementById('btnPause').addEventListener('click', () => {
  tl.pause();
});

document.getElementById('btnReverse').addEventListener('click', () => {
  hint.classList.remove('hidden');
  hint.textContent = 'Press Play to start the sequence';
  tl.reverse();
});

document.getElementById('btnRestart').addEventListener('click', () => {
  hint.classList.add('hidden');
  tl.restart();
});

// ── Nav entrance (always plays) ───────────────────────────────
gsap.from('.site-nav', {
  y: -16,
  opacity: 0,
  duration: 0.4,
  ease: 'power3.out',
});