Skip to content
ExamsLibraryFor agentsConnect your agent

Making SVG move

intermediate·8 min·Markdown for agents

What this lesson teaches

Choose the right animation engine for where an SVG lives, draw a line on with two CSS properties, and stop rotations from orbiting the corner.

  1. 1First question: where does the file live?
  2. 2Drawing a line on
  3. 3The corner problem
  4. 4Keep it cheap
  5. 5Reduced motion travels with the file
  6. 6Ask for it precisely

Press replay. Then grab the slider and drag it yourself.

pathLength="1"
stroke-dasharray: 1;
stroke-dashoffset: 1 → 0;
The drawing animation is one property moving from 1 to 0. The slider moves the same property by hand.

That line is not being drawn. It is already all there. It is hidden behind one very long dash, and the dash is sliding out of the way. That trick, and a handful like it, covers most of the SVG animation you will ever need.

First question: where does the file live?

Before easing, before duration, ask where the SVG renders. That decides which tools can even reach it.

Where it rendersWhat can animate it
Inline in the page (React, HTML), reacting to clicks and stateCSS transitions or the Web Animations API
Inline, but driven by a drag, a spring or a long timelineMotion or GSAP, whichever the project already uses
Loaded as <img>, a background image, in an email, on social, in FigmaOnly what is inside the file: CSS @keyframes in its own <style>, or tags
A rich After Effects export that already needs a player or Rive, and only then

The third row catches people out. An SVG loaded as an image is sealed: your page's CSS and JavaScript can't touch it. Animation has to travel inside the file. CSS keyframes are the most portable choice; SMIL still wins for moving something along a path with <animateMotion>.

Drawing a line on

The trick in the demo uses , which turns a stroke into dashes, and stroke-dashoffset, which slides them along.

Measuring a path's length is fiddly, so skip it. Set pathLength="1" on the path and the browser treats the whole line as one unit long, whatever its real size.

path {
  stroke-dasharray: 1;        /* one dash, as long as the whole path */
  stroke-dashoffset: 1;       /* pushed fully out of view */
  stroke-linecap: round;      /* soft ends while it draws */
  animation: draw 1600ms cubic-bezier(0.65, 0, 0.35, 1) forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }

Drawing is one of the few places an ease-in-out curve is right: the line is moving across the screen on its own, not answering a click.

The corner problem

Now press rotate, a few times.

default: (0, 0)
transform-box: fill-box;
transform-origin: center;
Same rotate, same gear. On the left it swings around the top-left corner of the drawing (the red dot). On the right it turns in place.

In HTML, a rotation turns around the element's center. In SVG, it turns around (0, 0), the top-left corner of the whole drawing. So every rotation and scale you copy from HTML orbits the corner.

The fix is two lines. tells the browser to measure from the shape's own box, and then transform-origin means what you expect.

.gear { transform-box: fill-box; transform-origin: center; }
.arm  { transform-box: fill-box; transform-origin: 10% 20%; } /* the shoulder */

For a mascot, put the origin where the part would hinge in life: a shoulder, a wrist, a lid.

Most "my SVG animation goes haywire" bugs are this one.

Keep it cheap

The rules from motion apply unchanged. transform and opacity are cheap: the browser moves a finished picture. Animating d, points, r, stroke-width or a filter makes it repaint every frame. Fine for one small icon; ruinous for a whole scene.

  • Move groups, not paths. Animate <g id="arm">, <g id="eyes">, not three hundred separate paths.
  • Stagger by 30 to 80ms. Paired parts (two eyes, two arms) share duration and easing.
  • Budget the file. About 2 KB for an animated icon, 10 to 30 KB for a hero mascot. Reuse shapes with <use>, move them with transforms instead of redrawing.

Reduced motion travels with the file

Some people turn on "reduce motion" in their system settings because movement makes them unwell. Inline SVG respects it only if you wrote the media query. A self-contained file can carry its own:

@media (prefers-reduced-motion: reduce) {
  .gear, path { animation: none; }
}

That query works even inside a file loaded as <img>. SMIL can't read it, so a SMIL-only file needs a still fallback image.

A copied animated icon with its own keyframes and no reduced-motion query is the most common miss in generated UI. Check every one.

Ask for it precisely

Animate an SVG· for your agent
Animate this SVG. It is used as <img> in our marketing page, so the animation must live inside the file.
- CSS @keyframes in the file's own <style>, selectors scoped under the root id
- draw the outline on with pathLength="1", stroke-dasharray: 1, stroke-dashoffset 1 → 0, round linecaps
- the lid rotates around its hinge: transform-box: fill-box with transform-origin at the hinge
- only transform, opacity and stroke-dashoffset animate
- @media (prefers-reduced-motion: reduce) shows the finished drawing
- keep it under 10 KB; report the final size

Quick check

A logo SVG ships as an <img> tag on your landing page. Your agent added the animation to the page's global CSS file. What happens?

What this lesson builds on and leads to, across every course.