Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 13.4Intermediate ~22 min · shot 4 of 6

Morphing Shapes

In one line

CSS can animate a path's d property, but only between two paths with the same commands in the same order. Anything else does not blend: it jumps at the halfway point. To morph any two shapes, resample both into the same number of points first.

0/6 · 0%

Think of it like this

A rubber band pinned at 4 nails, which you move to 4 other nails. The band stretches smoothly because each nail has a partner. Give it 4 nails and then 5, and there is no partner for the extra one, so it jumps.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

d
The path data of a <path>: a list of commands such as M (move), L (line), C (curve), A (arc) and Z (close). In Chromium it is also a CSS property.
command
One letter of a path with its numbers. A capital means absolute coordinates, a small letter relative ones.
resample
Walk along a path in equal steps and write the points down as a new path, so that two paths get the same number of points.
interpolate
Work out the values between a start and an end value, frame by frame. A value that cannot be blended is shown as the start until the middle and the end after it.
winding
The direction a path is drawn in, clockwise or not, and the point it starts at. Morphing pairs points by their order, so both must match.

Step by step

01Read the table

"Which pairs blend?" prints five rows: the first point of each shape at 0, 499, 501 and 1000 ms. The first two rows change by 0.06 between 499 and 501: smooth. The last three jump from (20, 20) to (50, 10) across 2 ms.

02Watch each pair

Press each button. Pause at 0.1× near the middle of the 1200 ms run. The first two shapes are half way between a square and a diamond. The others are a square, then at once the other shape.

03Morph the square into a circle

In "A square into a circle" press Morph as written and step past 600 ms: the square is replaced by a circle in one frame. Press Morph resampled: at 600 ms the shape is a rounded square, and it ends as a circle.

04Count the commands

The console says as written: 5 commands against 4 and resampled: 65 commands against 65. The equal counts are what let the browser pair the points.

Try it yourself

  1. 1

    Start the circle at the wrong corner

    In the second example change the circle text to M78.28 78.28 A40 40 0 0 1 21.72 21.72 A40 40 0 0 1 78.28 78.28 Z and press Morph resampled. At 600 ms the shape has shrunk to a cross a few pixels across, in the middle. Each point crosses the shape to meet its partner.

  2. 2

    Use fewer points

    Change resample(d, n = 64) to n = 8. Near the end of the run the circle is a clearly angular polygon, with visible corners. More points make smoother shapes and longer path text.

  3. 3

    Find the jump on the timeline

    In the first example press the L against C button, pause at 0.1× and step through the middle of the 1200 ms run: the shape changes in one frame, and the track gives no hint of it.

Code & diagrams

Which pairs blend?

Each row shows the start of the shape at 0, 499, 501 and 1000 ms. A blend changes a little between 499 and 501. A pair that cannot blend jumps.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

same commands (L L L L Z): (20, 20) (34.97, 15.01) (35.03, 14.99) (50, 10)
relative l against absolute L: (20, 20) (34.97, 15.01) (35.03, 14.99) (50, 10)
H and V against L: (20, 20) (20, 20) (50, 10) (50, 10)
L against C: (20, 20) (20, 20) (50, 10) (50, 10)
4 points against 5 points: (20, 20) (20, 20) (50, 10) (50, 10)
A square into a circle

Press **Morph as written**, then **Morph resampled**, and step through the middle of each.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

as written: 5 commands against 4
resampled: 65 commands against 65

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Morph without resampling

In the second example press Morph as written.

terminal
$ Step through 600 ms at 0.1×
── what you'll see ──
The square stays a square until about the middle of the run, then a circle appears in one frame. There is no half way picture.

Myth vs fact

Myth

If both shapes look similar, the browser will morph them.

Fact

It needs the same command letters in the same order: one H against an L made it jump.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    A resampled path is a polygon, not a curve: with 64 points a circle of radius 40 looks round, but zooming far in shows corners. Use it for the in-between frames and show the original d at rest if the shape must stay exact.

  • ▸

    Equal steps along the length are not always the best pairing: points pair by distance fraction, so a long thin part of one shape can land on a short part of the other. Tools like MorphSVG let you choose. For icons, draw both shapes with the same commands by hand.

  • ▸

    Reduced motion for a morph: skip the in-between and show the final shape at once (as the morph() answer below does).

Remember this

  1. 1

    The problem this solves: a menu icon becomes a cross, a heart becomes a star, a square becomes a circle. The shape changes outline, not position. d: path("…") is a CSS property in Chromium, so @keyframes or element.animate() can use it. MDN lists it as not Baseline (checked October 2026), so read its compatibility table for Safari before you ship it. Writing the right path pairs is the whole craft.

  2. 2

    What blends. The browser moves each coordinate from the first path to the matching one in the second. For that the two paths need the same list of command types. We measured five pairs at 0, 499, 501 and 1000 ms of a 1000 ms animation, printing where each path starts. Four L points against four L points: (20, 20), then (34.97, 15.01) at 499 and (35.03, 14.99) at 501, a smooth glide. Relative l against absolute L blends with the same numbers, because both become absolute before blending.

  3. 3

    What does not blend. H and V against L (a different command letter), L against C (a line against a curve) and four points against five all gave (20, 20) at 499 ms and (50, 10) at 501 ms: the first shape until the halfway point, the second after it. There is no error. The same jump happens for any value that cannot be blended, such as display.

  4. 4

    Why tools resample. To morph a square into a circle, written with 4 and 5 commands (M H V H Z and M A A Z), you walk along each path in equal steps and write the points down as M x y L x y … Z. Both shapes then have the same number of L points and blend smoothly. In the second example the two paths had 5 and 4 commands as written and 65 and 65 after resampling 64 points. getTotalLength() and getPointAtLength() do the walking.

  5. 5

    Where the start point and direction matter. Point 1 of the first shape blends with point 1 of the second. Start the circle at the same corner and go the same way as the square (the second example does) and the shapes grow into each other, a rounded square at 600 ms. From the opposite corner every point crosses the middle (tryIt 1).

  6. 6

    What CSS cannot morph. The points attribute of <polygon> and <polyline> is not a CSS property here (CSS.supports("points", …) was false). Rewrite the shape as a d path, animate points with SMIL (Topic 13.5), or use a library: GSAP's MorphSVG plugin does the pairing and resampling for you (Phase 14).

  7. 7

    On the timeline. A d track is red (layout: the cost table, measured in headless Chromium, lists d with cx, r and stroke-width), because the browser rebuilds the shape's geometry each frame. Fine for an icon; measure before morphing a large drawing.

Explain it without notes

01

What must be true of two paths for CSS to morph them smoothly, and what happens otherwise?

02

How do you morph a square into a circle?

Practice

01

Morph a plus sign into a cross by animating d: both are written with 12 L commands. What do you check first?

02

Write morph(el, from, to) that resamples two path strings and animates d once, with no animation under reduced motion.

Done when you can

  • You can say which path pairs blend and which jump

  • You can resample two paths so they morph

  • You know why the start point and direction matter

  • You know points is not animatable in CSS here