Morphing shapes, and when not to
intermediate·7 min·Markdown for agents
What this lesson teaches
Author two paths that really morph into each other, recognise the ones that never will, and know when a simple icon swap is the better answer.
Morph the circle into a square. Then switch to "Mismatched" and try again.
Same list, same order. Every number has a partner, so each one slides to its new value.
Both squares look identical when they sit still. Only one can be reached from the circle. The difference is not the shape. It is how the shape is written down.
The one rule
A is the browser sliding each number in one path to the matching number in the other. So it needs every number to have a partner:
both paths need the same commands, in the same order, of the same type.Four curves can become four curves. Four straight lines can't become four arcs, because there is nothing to pair up. When the lists don't match, the browser gives up and swaps one shape for the other.
Writing two shapes that match
Decide the command list first, then place the points for each shape.
.shape {
d: path("M4 12 C4 7.6 7.6 4 12 4 C16.4 4 20 7.6 20 12 C20 16.4 16.4 20 12 20 C7.6 20 4 16.4 4 12 Z");
transition: d 240ms cubic-bezier(0.25, 1, 0.5, 1);
}
.shape[data-state="square"] {
d: path("M4 12 C4 4 4 4 12 4 C20 4 20 4 20 12 C20 20 20 20 12 20 C4 20 4 20 4 12 Z");
}Both are M, four C, Z. The square cheats: each "curve" has its control points pulled into the corner, so it looks straight.
Pick one point budget
Say
Mplus eightCplusZ. Both shapes use exactly that. If one shape needs less detail, add points that sit on top of each other; never delete commands.Same start, same direction
Start both at the same spot and draw both clockwise (or both counter-clockwise). Otherwise the morph twists through itself.
Match the holes
A shape with two holes needs two holes in the other state. Shrink an unused hole to nothing instead of removing it.
Normalise before shipping
Convert both to absolute cubic curves (
C, notcorS). A morph that works in Chrome and jumps in Safari almost always mixesSandC, orlandL: one browser quietly converts them, the other doesn't.
When the shapes just don't match
For two unrelated shapes (a logo becoming a word, a blob becoming a star), a library can resample both to the same number of points. does this with one function, interpolate(pathA, pathB), and GSAP's MorphSVG does the same inside GSAP.
The cost is a dependency and a JavaScript loop. Pay it when the morph is the product: a logo transformation, a shape-shifting hero. Not for a button.
Animatingd repaints every frame. One hero shape is fine. Twenty morphing at once is not.
Most of the time, swap instead
Play to pause. Copy to check. Menu to close. These feel like morphs, but the honest version is an . Press each button a few times, quickly.
Press each button, or read the five frames of each change below.
Both icons are rendered at once, stacked. The outgoing one goes to scale(0.3), opacity: 0 and blur(3px); the incoming one comes back from there. Because both are always on the page, pressing again mid-change just reverses the transition.
A swap reads as a real change of meaning. A timid morph between unrelated shapes, a copy icon stretching into a tick, reads as a glitch.
| Situation | Use |
|---|---|
| Two states of the same shape (chevron up and down) | Rotate it |
| Icons designed together on one line budget | A morph |
| Two different icons (copy and check, play and pause) | A swap |
| A logo or hero that shape-shifts | A morph, with flubber if the paths don't match |
Ask for the right one
Our copy button should turn into a check when pressed. Don't morph the paths. Render both icons stacked; outgoing goes to scale(0.3), opacity 0, blur(3px), incoming reverses that, 200-260ms with cubic-bezier(0.23, 1, 0.32, 1), interruptible. Separately, the hero blob morphs between three shapes: author all three as M + 8 absolute C + Z, same start point, clockwise, and transition the d property. Tell me if any shape can't fit the budget.
Quick check
Your agent morphs a hamburger icon (three M/L lines) into an X drawn as two M/L lines. It jumps instead of animating. Why?
In the skill
The nodes your agent reads when it does this work.
Connected lessons
What this lesson builds on and leads to, across every course.