Liquid✨ Premium

SVG Path Morph

A vector SVG path, filled with a violet-pink-cyan gradient, that reshapes on every click from star to heart to circle in 0.8 s: all three shapes are drawn with the same sequence of commands (a start, ten curves, a close), so every point glides to its match. Sharp at any size, a real button for the keyboard, the shape's name is announced; the shape is set without morphing when the user reduces motion.

SVGJSPath

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Liquid has 31 effects, including 6 free. Explore the category →

3 usage examples

Loyalty stamp — a florist's digital loyalty card — SVG Path Morph example 1

① Loyalty stamp — a florist's digital loyalty card

WhenOn the ten-box card, seven stamps are in; today's box, when tapped, moves from one stamp shape to the next: star for the free bouquet, heart for the 5th purchase, circle for a purchase.
WhyThe vector path stays sharp in a small box and morphs from one state to the next: the reward visibly changes, instead of one image swapped for another.
SettingsSVG scaled down to 78 px in a 92 px box; scene-specific gradient (id="c407-g1", pink #f472b6, raspberry #e11d48, gold #f59e0b); the button itself is the 8th box of the grid, the label takes the row below.
Cake shape — the "Shape" step of a pastry shop's online order — SVG Path Morph example 2

② Cake shape — the "Shape" step of a pastry shop's online order

WhenAt step 2 of 4, the strawberry cake preview goes from star to heart to round on each click, with "Heart — 8 servings" below and the price updated alongside.
WhyThe three shapes in the code match common cake molds; a single path that morphs shows the same cake in another mold.
SettingsSVG enlarged to 170 px in a white card; vertical strawberry #fb7185, cream #fecdd3, sponge #fde68a gradient, 1.2 #e11d48 outline; price read by the page whenever the label changes.
A child's opinion of their drawing — a drawing app for ages 4-7 — SVG Path Morph example 3

③ A child's opinion of their drawing — a drawing app for ages 4-7

WhenNext to the finished drawing, a big pictogram; the child taps it to choose: star "my favorite", heart "I love it", circle "done".
WhyA single pictogram that transforms is simpler for a 5-year-old than a row of three buttons.
Settings140 px SVG in a 164 px round white button; yellow #facc15, orange #fb923c, pink #ec4899 gradient; 20 px label, without capitals.

How it works

The .svg-morph-btn button holds a 150 px <svg> (viewBox="0 0 100 100"), a diagonal linear gradient and a <path> whose d attribute is animated by a SMIL element: <animate attributeName="d" dur="0.8s" fill="freeze" begin="indefinite">. On click, the script sets from to the shape on screen, to to the next one, then calls beginElement(): the browser glides every number of the path from one to the other, at constant speed, and fill="freeze" holds the end shape. The .svg-morph-label text takes the next name from data-names ("Star,Heart,Circle").

For the interpolation to exist, the three paths have exactly the same structure: one M, ten C curves and a Z, starting at the top and going the same way around. The star's straight sides are curves whose control points sit on the segment, at one third and two thirds; the heart is cut into ten curve pieces, its landmarks lined up with the star's (the top dip on the top tip, the bottom tip on the bottom notch); the circle is made of ten 36° arcs. Two paths with different structures, straight lines on one side and curves on the other, do not interpolate: the shape jumps halfway.

The script remembers the shape on screen (idx) instead of reading it back from the d attribute: the SMIL animation does not change that attribute, it covers it; reading it would restart every click from the shape loaded with the page. The d attribute is still updated on each click, so a browser without SMIL shows the right shape. During the 0.8 s morph, clicks are ignored: the in-between value of a SMIL animation cannot be read, and restarting from a finished shape would make the path jump.

Accessibility

  • Keyboard and screen readers: the SVG sits in a real <button> named "Next shape" (the SVG itself is hidden with aria-hidden). Measured in Chromium 151 and Firefox 153: Tab reaches it, Enter and Space move to the next shape. The label has aria-live="polite": "Heart", "Circle" are announced. In Safari, Tab only stops on buttons if the keyboard setting asks for it (or with Option+Tab).
  • Reduced motion: under prefers-reduced-motion: reduce, the script gives from and to the same value: the new shape appears at once. Measured in all three engines: no in-between frame.
  • The shape must not be the only information: the label says in text what the shape means ("Heart: 5th purchase", "I love it"), as in the examples.
  • Focus ring: 2 px pink, 4 px offset around the button, visible for keyboard users only (:focus-visible).

Browser compatibility

SVG + SMIL (<animate> on the d attribute) + 2.7 KB of JavaScript, mostly the text of the three paths; no dependencies. Measured in real time in Chromium 151, Firefox 153 and WebKit 26: every click starts from the shape on screen, the path changes on every frame for 0.8 s, with no jump. SMIL animation of d works in all current browsers. The CSS d: path() property, which would allow a CSS transition, only exists in Chromium and Firefox (missing from WebKit, measured): that is why the code goes through SMIL.

Chrome 60+✓ Full
Firefox 60+✓ Full
Safari 12+✓ Full (Tab depends on the keyboard setting)
Edge 79+✓ Full
Mobile iOS 12+✓ Full (touch)
Android Chrome 60+✓ Full (touch)

Without JavaScript, the star stays on screen. Without SMIL, the script still writes the new shape into the d attribute: it switches at once instead of morphing.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0407-root">
  <div class="svg-morph-container">
    <button type="button" class="svg-morph-btn" aria-label="Next shape" data-names="Star,Heart,Circle">
      <svg width="150" height="150" viewBox="0 0 100 100" aria-hidden="true" focusable="false">
        <defs><linearGradient id="fx0407-grad">… 3 colors</linearGradient></defs>
        <path fill="url(#fx0407-grad)" d="M50 5 C… (10 curves) Z">
          <animate attributeName="d" dur="0.8s" fill="freeze" begin="indefinite"></animate>
        </path>
      </svg>
    </button>
    <span class="svg-morph-label" aria-live="polite">Star</span>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Duration (HTML — dur on <animate>) 0.8s The script reads this value to know how long to ignore clicks: change it in one place only. Beyond 1.2 s, the button feels slow.
Easing (HTML — calcMode, keyTimes, keySplines on <animate>) constant speed calcMode="spline" keyTimes="0;1" keySplines=".45 0 .2 1" makes the morph start and end smoothly.
Colors (HTML — the gradient's three <stop> elements) violet #a855f7, pink #ec4899, cyan #06b6d4 Also change the gradient's id (fx0407-grad) and the path's url(#…) if you put several copies of the effect on the page: two gradients with the same id get mixed up.
Size (HTML — width / height of the <svg>) 150 × 150 px The path is vector: 48 px for an icon, 300 px for a visual, with no loss of sharpness. The viewBox stays at 0 0 100 100.
Labels (HTML — the button's data-names) "Star,Heart,Circle" (English version) Three comma-separated names, in shape order. The script rereads the attribute on every click.
Shapes (JS — the shapes array) star, heart, circle Any new shape needs the same structure: M, ten C, Z, starting at the top and going clockwise. Also put the first shape in the HTML d attribute.

FAQ

Write it with an M at the topmost point, then ten C curves going clockwise around, then Z. A straight side becomes a curve whose two control points lie on the segment (at one third and two thirds). Match the landmarks: the n-th curve of one shape should sit at roughly the same place around the loop as the n-th curve of the others, or the path twists during the morph.
A SMIL animation does not expose its in-between value: the script cannot know what the path looks like halfway. Restarting the animation from a finished shape would make the path jump from its in-between position to that shape. Ignoring clicks for 0.8 s keeps each morph whole; for rapid clicks, shorten dur.
A CSS transition of the path goes through the d: path(…) property, which works in Chromium and Firefox but not in WebKit (measured: CSS.supports says no), so not in Safari. The <animate> element animates the d attribute in all three engines. The structure rule (same commands, same count) applies to both methods.