Morphing Shape
A solid SVG shape that smoothly turns from a diamond into a circle, a star and a square while shifting hue — a real morph that also runs smoothly in Safari.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →
3 usage examples



How it works
The drawing is a single <path class="mrph-shape"> in a 120 × 120 unit SVG. The four shapes (diamond, circle, five-pointed star, square) are written with the same structure: 20 anchor points joined by 20 cubic Bézier curves. Straight sides are curves whose handles stay on the line; the circle uses arc handles. Since every shape has the same number and type of commands, the browser can compute every in-between shape: the change is continuous, with no jump.
The animation is SMIL: an <animate attributeName="d"> runs through the four paths in 4 s (a quarter of the cycle per change, soft acceleration and braking through keySplines=".42 0 .58 1"), then returns to the diamond and starts again. A second <animate attributeName="fill">, on the same clock, slides the color from indigo #6366f1 to violet #8b5cf6, magenta #d946ef and pink #ec4899.
Why SMIL rather than the CSS d property: Safari does not support d in CSS, so the shape would stay frozen there. <animate> on the d attribute is supported by Chrome, Firefox and Safari. The small script only handles reduced motion; there is no generic class or CSS animation name: everything is prefixed mrph-, so the code can sit next to any other effect.
Accessibility
- Decorative by default: the SVG has
aria-hidden="true"andfocusable="false", screen readers skip it. If the shape carries meaning (a "listening" state, for instance), say it in visible text next to it, as in the voice-notes example. - Reduced motion: a SMIL animation cannot be stopped with CSS. The script reads
prefers-reduced-motion: reduce, pauses each drawing withpauseAnimations()and rewinds it (setCurrentTime(0)): a crisp indigo diamond remains. It also follows a change of the setting during the visit and restarts the animation if the option is turned off. - Nothing to operate: the effect reacts to neither pointer nor keyboard; no focus trap. For an interactive use (pause on click), use a real
<button>and the standard SVG methods, see the FAQ. - Pace: a full cycle lasts 4 s, with no blinking or flashing; the color change is gradual.
Browser compatibility
SMIL <animate> on the d attribute and on fill, pauseAnimations() / setCurrentTime(), prefers-reduced-motion media query. Measured in Chromium, Firefox and WebKit (Playwright): 0 errors, continuous in-between shapes for every pair (same path within 50 ms in all 3 engines), two independent copies, a lone copy still drawn when another copy is hidden, clean stop under reduced motion. The morph itself works in much older versions; the versions below are those where reduced motion is honored too.
Without SMIL, the path keeps its starting value: a still indigo diamond. Without JavaScript, only the reduced-motion stop is missing; the morph runs normally.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0601-root">
<div class="mrph-stage">
<svg class="mrph-svg" viewBox="0 0 120 120" aria-hidden="true" focusable="false">
<path class="mrph-shape" fill="#6366f1" d="…diamond…">
<animate class="mrph-d" attributeName="d" dur="4s" repeatCount="indefinite" calcMode="spline" keyTimes="0;.25;.5;.75;1" keySplines="…" values="…diamond;circle;star;square;diamond…"/>
<animate class="mrph-fill" attributeName="fill" dur="4s" repeatCount="indefinite" calcMode="spline" keyTimes="0;.25;.5;.75;1" keySplines="…" values="#6366f1;#8b5cf6;#d946ef;#ec4899;#6366f1"/>
</path>
</svg>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Size (CSS — --mrph-size) |
120px | Side of the drawing. 100% makes it follow the parent's width; the SVG keeps its proportions. |
Block background and height (CSS — --mrph-page, --mrph-root-h) |
#0a0a0f / 300px | --mrph-page:transparent and --mrph-root-h:0px to set the shape on your own background, inside a button or a card. |
Cycle length (HTML — dur on both <animate> tags) |
4s | Use the same value on both tags to keep color and shape in sync (e.g. dur="6s" for a calm background). |
Colors (HTML — values of the fill animation) |
#6366f1;#8b5cf6;#d946ef;#ec4899;#6366f1 | One color per shape (diamond, circle, star, square), the first one repeated at the end to loop without a jump. Also change fill on the <path> (color when nothing animates). |
Pace of each change (HTML — keySplines) |
.42 0 .58 1 (×4) | Acceleration curve of each quarter cycle. 0 0 1 1 gives an even motion, with no visible rest on each shape. |
Shapes (HTML — values of the d animation) |
diamond; circle; star; square; diamond | Each shape must keep exactly the same structure (one M, then 20 C, then Z): otherwise the browser jumps from one shape to the next instead of blending them. |
FAQ
C curves. For a new shape, start from an existing one and move its points, or have it redrawn as 20 cubics in a vector editor before pasting it into values.svg.pauseAnimations() freezes the image, svg.unpauseAnimations() resumes, svg.setCurrentTime(0) goes back to the diamond. Call them on the .mrph-svg of the copy concerned, from a real <button>. CSS animation-play-state has no effect on SMIL.d attribute with SMIL, which Safari supports; the CSS d property does not exist in Safari, and a shape animated that way would stay frozen there. Measured in WebKit with the same rendering as Chrome and Firefox.