Morph SVG Icons — Free GSAP MorphSVG Plugin Snippet

Morph SVG Icons · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Any-to-any morphing
Point counts are resampled to compatibility.
Auto point mapping
shapeIndex 'auto' minimizes point travel.
Defs-based targets
Shapes stay in markup, referenced by selector.
Synchronized fill
Color tweens inside the same animation.
Layered easing
Elastic squash rides over the smooth morph.
Designer-friendly
Re-export shapes without touching JS.
Active-state buttons
The control bar tracks the current shape.
Drop-shadow glow
A CSS filter follows every fill color.

About this UI Snippet

Morph SVG Icons — Fluidly Reshape One Path Into Any Other

Screenshot of the Morph SVG Icons snippet rendered live

Icon morphing — a play button flowing into a heart, a star melting into a lightning bolt — is the kind of polish users notice without knowing why. CSS can't do it (paths with different point counts aren't interpolatable), but GSAP's MorphSVGPlugin (free on the CDN since 3.13) solves the hard geometry: this snippet keeps four target shapes in <defs> and morphs one visible path between them, with color and a squash-settle riding along.

Why path morphing is genuinely hard

Interpolating d attributes only works when both paths have identical command counts and types — a 3-point triangle and a 10-point star simply can't be lerped natively, which is why CSS d: path() transitions fail on real icons. MorphSVG resamples both shapes into compatible point sets behind the scenes, adding points where needed and matching segments, so *any* path can morph into *any other*. That preprocessing is the entire value of the plugin — the tween that follows is ordinary GSAP.

shapeIndex: 'auto' picks the least-weird correspondence

Even with compatible point sets, morphs can look like taffy if point #1 of the triangle maps to the wrong corner of the star — the shape turns itself inside out in transit. shapeIndex controls the rotational offset of that mapping, and 'auto' tries the candidates and picks the one with the least total point travel. When a specific morph still looks odd, setting an explicit integer (or using GSAP's findShapeIndex utility) is the tuning knob.

Targets live in defs, referenced by selector

The four icon shapes sit as <path> elements inside <defs> — parsed and addressable but never rendered. morphSVG: { shape: '#msi-heart' } accepts that selector directly, so adding a fifth icon is one path plus one button; no path strings in JavaScript. Keeping geometry in markup also means designers can re-export shapes from Figma or Illustrator without touching code.

Fill tweens in the same animation

Each button carries a data-color, and fill interpolates alongside the morph in the same tween, so shape and color arrive together. GSAP tweens SVG presentation attributes natively — no CSS transition needed, no split timing to reconcile.

The elastic squash is separate on purpose

A second fromTo scales the icon from 0.92 with elastic.out(1, 0.45) while the morph runs at power2.inOut. Layering two tweens with different eases on one element gives the transformation weight — the geometry flows while the whole glyph bounces once, and each can be retuned independently.

One caveat: morphs need paths, not primitives

MorphSVG animates <path> elements; <circle>, <rect>, and <polygon> targets must be converted first (the plugin ships MorphSVGPlugin.convertToPath() for exactly this). All shapes here are authored as paths to keep the demo dependency-free of that step.

Customizing it

Add shapes (a path in defs plus a button), auto-cycle the icons on a timer, or morph in response to app state — play/pause is the canonical pair. Related: self-drawing strokes in scroll text draw and draw svg success, blob-style morphing without SVG in scroll shape morph, and animated icon sets in animated svg icons.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than guessing why some morphs look twisted, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly what shapeIndex: 'auto' is doing when it picks a point-mapping offset between the triangle and the star, or why the target shapes have to sit inside defs rather than just being referenced from strings in the JS. The same assistant is useful for optimizing it, for instance asking whether running the fill tween and the elastic squash as two separate GSAP calls on every click could be consolidated into one timeline for cleaner sequencing, or whether morphing very high-point-count paths would need a lower duration to stay smooth. It's also a good way to extend the effect: ask it to auto-cycle through all four shapes on a timer, add a fifth custom shape traced from an uploaded SVG, or drive the morph from real app state like a play/pause toggle. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "morphing SVG icon" control in plain HTML, CSS, and JavaScript using GSAP and its MorphSVGPlugin (load both from a CDN) — no build step, no other animation library.

Requirements:
- One visible SVG path element that starts as a simple shape (e.g. a play-button triangle), plus several other target shapes (e.g. a heart, a star, a lightning bolt) authored as path elements inside an SVG defs block so they are parsed but never rendered.
- A row of buttons, one per target shape, each carrying a data attribute referencing its target path's id selector and a data attribute for a fill color to switch to.
- Clicking a button must morph the one visible path into the clicked button's target shape using GSAP's morphSVG property, referencing the target by its defs selector string (not a hardcoded path string duplicated in JS), and must use shapeIndex set to auto so the point correspondence with the least total travel is chosen automatically.
- The same click must also tween the visible path's fill color to the button's target color, synchronized in duration with the shape morph so color and geometry complete together.
- Layer a second, independent tween on the same click that briefly scales the icon down and then springs it back up with an elastic ease, so the morph reads as having physical weight, decoupled from the morph's own easing curve.
- Track and visually mark (e.g. with an active class) which shape button is currently selected, updating it on every click.
- Explain in a comment why a plain CSS d: path() transition could not achieve this morph given the differing point counts between the shapes.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsInclude gsap and MorphSVGPlugin from the CDN panel.
  2. 2
    Paste HTML, CSS, and JSA play icon renders with four shape buttons.
  3. 3
    Click HeartThe triangle flows into a heart and turns rose.
  4. 4
    Cycle the shapesStar and bolt morph with matched point mapping.
  5. 5
    Watch the settleAn elastic squash lands each transformation.
  6. 6
    Add your own shapeDrop a path in defs and a button with its selector.

Real-world uses

Common Use Cases

Play/pause controls
The canonical morph pair for a music player or video player.
Reaction pickers
Morph between like, heart, and star in an emoji reaction bar.
State icons
Bell to checkmark on subscribe, near a notification bell.
Brand moments
Logo marks flowing between forms, like scroll shape morph with real geometry.
Weather and status
Sun to cloud to bolt for a weather widget.
Menu toggles
Hamburger to X with true path flow, beyond animated hamburger.

Got questions?

Frequently Asked Questions

CSS d: path() transitions require both paths to have identical command counts and types — a 3-point triangle can't interpolate to a 10-point star. MorphSVG resamples both shapes into compatible point sets, inserting and matching points automatically, so any path morphs into any other. That geometry preprocessing is what the plugin fundamentally adds.

It sets which point of the start shape maps to which point of the end shape — the rotational offset of the correspondence. A bad mapping makes shapes turn inside-out in transit. 'auto' tests candidate offsets and picks the one with the least total point travel; if a particular pair still looks like taffy, an explicit integer index is the manual override.

They live inside <defs>, which the browser parses (making them queryable by id) but never paints. morphSVG accepts the selector string directly — { shape: '#msi-heart' } — so geometry stays in markup where designers can replace it from Figma exports, and JavaScript holds zero path data.

Not directly — MorphSVG operates on <path> elements. The plugin includes MorphSVGPlugin.convertToPath('circle, rect, polygon') which swaps primitives for equivalent paths in place, after which they morph normally. This demo authors everything as paths up front to skip that step entirely.

They're properties of the same tween: morphSVG handles the geometry while fill interpolates alongside it, sharing the one duration and ease. The elastic squash is deliberately a second tween with its own ease — layering two differently-eased animations on one element is what gives the morph its weight without complicating either.

Register the plugin at module scope, inline the SVG in your template, and run tweens from event handlers against a ref to the live path — no effect needed for click-driven morphs, though initial state setup belongs in useEffect/onMounted/ngAfterViewInit. Keep defs ids unique per component instance (suffix with an id) so multiple copies don't cross-reference. Buttons and layout translate to Tailwind directly.