Back to Learn SVG Animation

GSAP SVG Animation: A Practical Beginner’s Guide

By Published Updated tools and workflows

GSAP is a strong fit when an SVG needs coordinated steps, precise timing, or reusable playback controls. Start with an inline SVG and the core library, then build from one tween to a scoped, accessible timeline.

That progression matters because GSAP can animate SVG elements with the same core model it uses elsewhere: choose a target, describe a change, and control when it happens. A tween handles one change; a timeline coordinates several tweens as one sequence.

The examples below use current GSAP 3.15 syntax and the core library only. They focus on the parts that make a first SVG animation production-ready: clear targets, deliberate timing, reliable SVG origins, lifecycle cleanup, reduced motion, and useful debugging.

When GSAP is the right tool

GSAP is not automatically the best choice for every moving SVG. Choose the smallest tool that keeps the result understandable:

  • Use CSS for a simple hover, focus transition, loader, or short decorative loop with only a few states.
  • Use the Web Animations API when you want native JavaScript playback controls for a contained effect without adding a library. Its Element.animate() method returns an animation object that can be played, paused, reversed, or canceled.
  • Use GSAP when several elements must overlap, stagger, repeat, reverse, respond to interaction, or share one controllable timeline.

GSAP also earns its place when SVG transform behavior or optional plugins such as MotionPath, DrawSVG, MorphSVG, or ScrollTrigger simplify work that would otherwise become custom timing code. For a deeper decision framework, read CSS vs GSAP for SVG. Keep scroll-specific implementation in the separate guide to scroll-triggered SVG animation.

Install or load GSAP

For a bundled project, run npm install gsap, commit the resulting lockfile, and import the core library in the module that owns the animation:

import { gsap } from "gsap";

For a small static page, load a reviewed, version-pinned CDN build before the script that uses it:

<script
  src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"
  defer></script>
<script src="/js/orbit-demo.js" defer></script>

Use one loading path, not both. A pinned CDN URL remains stable but will not receive later fixes automatically, so review the version periodically. The official installation guide also explains plugin imports. Load or import each optional plugin separately and register it with gsap.registerPlugin(); explicit registration also protects plugins from over-aggressive tree-shaking.

Current GSAP 3.15 note: the official installation documentation says the former private npm registry is retired and the core library and plugins are available from the public npm package. If an older project still references npm.greensock.com, migrate that configuration before upgrading. GSAP 3.15 also adds easeReverse for controlling easing when a tween reverses; it is useful for reversible UI motion but is not required for the beginner patterns below.

Prepare an SVG with clear targets

Inline SVG is the most direct starting point because its groups and shapes are part of the page DOM. Give animation targets descriptive classes instead of relying on export-generated IDs:

<svg
  class="orbit-demo"
  viewBox="0 0 240 120"
  role="img"
  aria-labelledby="orbit-title orbit-description">
  <title id="orbit-title">Rocket crossing a star field</title>
  <desc id="orbit-description">
    A small rocket moves from left to right.
  </desc>

  <line
    class="orbit-demo__trail"
    x1="28"
    y1="72"
    x2="150"
    y2="72"
    stroke="currentColor"
    stroke-width="4" />

  <circle
    class="orbit-demo__star"
    cx="92"
    cy="28"
    r="4"
    fill="currentColor" />
  <circle
    class="orbit-demo__star"
    cx="152"
    cy="42"
    r="3"
    fill="currentColor" />

  <g class="orbit-demo__ship">
    <path
      d="M30 60 L58 72 L30 84 Z"
      fill="currentColor" />
  </g>
</svg>

If an SVG is loaded through an <img>, GSAP can animate the image element as a whole, but page JavaScript cannot select the paths inside that separate image document. Inline the artwork when its internal parts need individual motion. Also decide whether the SVG is meaningful or decorative before animating it; this guide’s accessible SVG explanation covers names, descriptions, and aria-hidden.

Learn to, from, and fromTo

A GSAP target can be selector text, a DOM element, a NodeList, an array, or a plain object. For reusable work, start from a root element and query within it:

const scene = document.querySelector(".orbit-demo");
const ship = scene.querySelector(".orbit-demo__ship");
const stars = scene.querySelectorAll(".orbit-demo__star");
const trail = scene.querySelector(".orbit-demo__trail");

gsap.to(ship, {
  x: 96,
  rotation: 8,
  duration: 0.9,
  ease: "power2.out"
});

gsap.from(stars, {
  opacity: 0,
  scale: 0,
  duration: 0.45,
  stagger: 0.08
});

gsap.fromTo(
  trail,
  { scaleX: 0 },
  {
    scaleX: 1,
    transformOrigin: "left center",
    duration: 0.7,
    ease: "power1.inOut"
  }
);

gsap.to() reads the current state and animates to the declared destination. gsap.from() supplies a temporary starting state and returns to the element’s current state. gsap.fromTo() makes both ends explicit, which is helpful when runtime styles are uncertain. Duration, easing, callbacks, and other control properties belong in the final object of fromTo().

From-type tweens render their starting values immediately by default. That is usually convenient, but delayed or overlapping from-tweens can appear too early; use immediateRender: false only when that behavior is genuinely causing a conflict. The official tween documentation details the available controls.

Build sequences with timelines, staggers, and easing

Manual delays become fragile as a sequence changes. A timeline keeps its child tweens together, supplies shared defaults, and exposes one playhead for pause, replay, reverse, progress, and speed:

const intro = gsap.timeline({
  paused: true,
  defaults: {
    duration: 0.6,
    ease: "power2.out"
  }
});

intro
  .fromTo(
    trail,
    { scaleX: 0 },
    {
      scaleX: 1,
      transformOrigin: "left center"
    }
  )
  .from(
    stars,
    {
      opacity: 0,
      scale: 0,
      stagger: {
        each: 0.08,
        from: "center"
      }
    },
    "-=0.25"
  )
  .from(
    ship,
    {
      opacity: 0,
      x: -18
    },
    "<"
  )
  .to(ship, {
    x: 96,
    rotation: 8,
    duration: 0.8
  });

intro.play();

Without a position value, each child starts at the timeline’s end. "-=0.25" overlaps that end by a quarter second, while "<" aligns with the start of the most recently inserted animation. A simple stagger value creates a fixed gap between targets; a stagger object adds control over origin and distribution.

Easing changes how motion accelerates, not how long it lasts. power2.out is a useful restrained entrance, while power1.inOut suits motion that should accelerate and decelerate evenly. Set repeated choices in timeline defaults, then override only the exceptional steps. See the official timeline, stagger, and easing references for the full syntax.

Set SVG transform origins deliberately

An unexpected pivot is one of the most common SVG animation problems. Native SVG origins depend on the applicable reference box and differ from the assumptions developers often bring from HTML. GSAP normalizes transformOrigin for SVG, but the intended pivot should still be explicit:

const needle = document.querySelector(".gauge__needle");
const orbitingParts = document.querySelectorAll(
  ".orbit__part"
);

gsap.set(needle, {
  transformOrigin: "50% 100%"
});

gsap.to(needle, {
  rotation: 45,
  duration: 0.6,
  ease: "power2.out"
});

gsap.to(orbitingParts, {
  rotation: 180,
  svgOrigin: "120 60",
  duration: 1
});

transformOrigin describes a point relative to an element’s box. svgOrigin uses the SVG canvas’s global coordinates, which is useful when several parts orbit one shared point. Use one origin system per element, not both. GSAP’s CSS and SVG transform reference explains both, while MDN documents the underlying SVG transform-origin behavior.

Scope animations and clean them up

A global selector may accidentally animate every component instance. It can also leave animations and event listeners alive after a component is removed. gsap.context() scopes selector text to a root and records the GSAP work created inside it:

export function mountStatusIcon(root) {
  let timeline;
  const replayButton = root.querySelector(
    "[data-replay]"
  );

  const context = gsap.context(() => {
    timeline = gsap.timeline({ paused: true })
      .from(".status-icon__part", {
        opacity: 0,
        scale: 0.85,
        stagger: 0.06,
        duration: 0.35
      });

    const replay = () => timeline.restart();
    replayButton?.addEventListener("click", replay);

    return () => {
      replayButton?.removeEventListener(
        "click",
        replay
      );
    };
  }, root);

  timeline.play();

  return () => context.revert();
}

Call the returned teardown function when the component unmounts or the page view is replaced. context.revert() kills recorded animations and restores their pre-animation state, including relevant inline styles. The cleanup returned inside the context handles non-GSAP work such as event listeners. Use a timeline for playback control and a context for scope and teardown; they solve different problems. See the official context documentation.

Respect reduced motion and preserve meaning

Reduced motion is part of the animation design, not a final CSS patch. gsap.matchMedia() accepts normal media queries, scopes selectors, and automatically reverts and rebuilds its recorded animations when a condition changes:

export function mountMotionAwareScene(root) {
  const media = gsap.matchMedia(root);

  media.add(
    {
      reduceMotion:
        "(prefers-reduced-motion: reduce)",
      motionOK:
        "(prefers-reduced-motion: no-preference)"
    },
    ({ conditions }) => {
      if (conditions.reduceMotion) {
        gsap.set(".scene__part", {
          opacity: 1
        });
        return;
      }

      gsap.from(".scene__part", {
        opacity: 0,
        y: 24,
        rotation: -4,
        duration: 0.65,
        stagger: 0.08,
        ease: "power2.out"
      });
    }
  );

  return () => media.revert();
}

This reduced-motion branch keeps the complete scene visible without making the user watch its entrance. For meaningful state changes, an immediate update or restrained fade may be better than removing all feedback. Decorative movement can simply be omitted. GSAP’s matchMedia documentation and MDN’s prefers-reduced-motion reference provide the underlying behavior. The site’s focused guide shows more ways to respect prefers-reduced-motion.

Motion also needs ordinary accessibility checks. Keep essential text and controls available before, during, and after the animation. Do not rely on movement alone to announce a result. Make replay or pause controls real keyboard-operable buttons with clear names. Avoid endless decorative loops; under WCAG 2.2.2, automatically moving content that lasts more than five seconds alongside other content generally needs a pause, stop, or hide mechanism unless it is essential. W3C also requires a way to disable non-essential motion triggered by interaction under Animation from Interactions.

Keep SVG animation efficient

GSAP cannot make expensive artwork cheap, so optimize the scene as well as the code:

  • Start with x, y, rotation, scale, and opacity when they can express the effect.
  • Animate a parent group when several paths share the same movement.
  • Avoid layout measurements inside every update; measure before playback or only when layout changes.
  • Limit simultaneous filters, blurs, masks, path morphs, and large numbers of independently moving nodes.
  • Do not apply will-change broadly. Extra compositor layers consume memory and are not a universal SVG performance fix.
  • Clean up inactive timelines and test representative mobile hardware, not only a desktop development machine.

Transforms and opacity are sensible starting points, not guarantees. SVG complexity, paint work, the browser, and surrounding page activity all affect the result. MDN’s animation performance guidance explains why code-based motion can still consume CPU or produce jank.

Debug the common failures

  • Nothing moves: confirm the script loads after GSAP and the markup. Log gsap.version and inspect the target value.
  • The wrong elements move: count matches with gsap.utils.toArray() and scope repeated components with a root or context.
  • Internal paths cannot be selected: check whether the SVG is an external <img> rather than inline markup.
  • An element jumps while rotating: inspect the SVG viewBox, then set transformOrigin or svgOrigin explicitly.
  • A sequence is hard to inspect: start its timeline with paused: true, set a progress value, or play it slowly with timeScale().
  • Animations duplicate after navigation: verify that every mount has one matching context.revert() or matchMedia.revert() teardown.
  • Content stays hidden: confirm a from-tween actually ran and that CSS did not make its hidden starting state permanent when JavaScript failed.

During debugging, let one system own each animated property. Competing CSS animations, transitions, SVG attributes, and GSAP tweens can all write different values to the same element.

Production checklist

  • Use one current, locked or version-pinned GSAP installation.
  • Keep SVG parts inline when their descendants need to be targeted.
  • Choose stable, descriptive selectors and scope component instances.
  • Use a timeline instead of accumulating manual delays.
  • Set SVG transform origins intentionally.
  • Provide a complete static or reduced-motion state.
  • Label meaningful SVGs and hide purely decorative ones appropriately.
  • Add controls for persistent non-essential motion.
  • Revert animations and remove custom listeners during teardown.
  • Test performance, keyboard use, reduced motion, and failure without JavaScript.

Quick answers

Which GSAP method should a beginner learn first?

Start with gsap.to(). It matches the natural instruction “move this element to that state.” Add from() for entrances, fromTo() when both endpoints must be explicit, and a timeline as soon as two or more steps need coordination.

Does GSAP need an SVG plugin?

No. GSAP core handles selectors, transforms, opacity, timelines, staggers, easing, and many SVG attributes. Add a plugin only for a capability such as path motion, sophisticated morphing, stroke drawing, or scroll orchestration.

Can GSAP animate an SVG loaded with an img element?

It can animate the outer image element, including its position, scale, rotation, and opacity. It cannot directly select the paths inside that separate SVG document. Inline the SVG when internal groups or shapes need independent animation.

Is GSAP better than the Web Animations API?

Not universally. The Web Animations API is a strong native choice for contained keyframe effects and direct playback control. GSAP becomes more useful when the work needs multi-element choreography, flexible overlaps, reusable timelines, SVG transform normalization, or its plugin ecosystem.

Is a GSAP animation accessible automatically?

No animation library decides whether motion is essential, whether an SVG has a useful text alternative, or what reduced-motion users should see. Build a meaningful static state first, then add motion as an enhancement with appropriate controls and teardown.

What should I build after this tutorial?

Try a short entrance with three SVG groups, then turn it into a replayable component. After that, build a logo reveal with SVG or browse SVG animation examples for a small pattern to reproduce. Keep the first project short enough that timing, accessibility, and cleanup remain easy to inspect.

Start with one clear motion

A good first GSAP SVG animation does not need a plugin or a large scene. Load the core library, target one inline SVG part, animate it with gsap.to(), and add complexity only when the motion has a clear purpose. Timelines, scoped cleanup, reduced motion, and testing are what turn that first tween into a technique you can ship.

Sources and further reading

Continue learning
Guide

SVG Animation Not Working? A Production Debugging Playbook

Fix broken SVG animations with a systematic checklist for embedding, CSS, JavaScript, paths, IDs, reduced motion, browser timing, and performance.

Guide

SVG Animation Testing: Visual, Accessibility, and Performance CI

Test SVG animation with Playwright screenshots, reduced-motion checks, axe, ARIA snapshots, runtime assertions, browser coverage, and performance review.