SVG animation: animate, animateTransform, animateMotion

Animate an SVG with its own elements, no CSS or script: animate for attributes, animateTransform to spin and scale, animateMotion to follow a path.

Published

An SVG can animate itself. Put an <animate> element inside a shape, name the attribute to change and the values to pass through, and the browser does the rest. No CSS and no JavaScript are involved, so the animation travels inside the file and plays wherever a browser shows the file, an <img> tag included.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="20" fill="teal">
    <animate attributeName="r" values="20;40;20" dur="2s" repeatCount="indefinite"/>
  </circle>
</svg>

The circle swells from a radius of 20 to 40 and back, every two seconds, for ever. These elements are often called SMIL animation, after the W3C language they were taken from.

The four animation elements

ElementWhat it changes
<animate>One attribute or CSS property of a shape: a position, a size, a colour, an opacity, the d of a path
<animateTransform>The transform attribute: rotate, scale, translate, skewX or skewY
<animateMotion>The position of a shape, moving it along a path
<set>One attribute, switched to a new value with no steps in between

An animation element acts on its parent, the shape it sits inside. To animate a shape from elsewhere in the file, give the shape an id and point at it with href="#the-id" on the animation element.

From, to and how long

The shortest animation names an attribute, a start, an end and a duration:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 40">
  <rect x="10" y="10" width="20" height="20" fill="teal">
    <animate attributeName="x" from="10" to="70" dur="1s" fill="freeze"/>
  </rect>
</svg>

The square slides from x 10 to x 70 in one second and stays there.

AttributeMeaning
attributeNameThe attribute or property to animate
from, toThe first and last values
byA change relative to the start, in place of to
valuesA list of values separated by semicolons, in place of from and to
durThe length of one run: 2s, 500ms, 1.5s
repeatCountA number of runs, or indefinite
fillfreeze keeps the last value. remove, the default, puts the attribute back as it was
beginWhen to start. The default is 0s, as soon as the file loads

On an animation element, fill has nothing to do with colour. It says what happens when the animation ends, and forgetting fill="freeze" is why a shape snaps back to where it began.

Several steps, and easing

values takes any number of steps, spread evenly over the duration. keyTimes places each step at a fraction of the duration, from 0 to 1, and needs exactly one entry for each value:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 40">
  <circle cx="20" cy="20" r="10" fill="teal">
    <animate attributeName="cx" values="20;80;20" keyTimes="0;0.2;1" dur="2s" repeatCount="indefinite"/>
  </circle>
</svg>

The circle crosses in the first fifth of the time and drifts back over the rest.

The movement between two steps is at an even speed unless calcMode says otherwise.

calcModeEffect
linearEven speed between steps. The default, except for <animateMotion>
discreteJumps from one value to the next
pacedEven speed over the whole animation, ignoring keyTimes. The default for <animateMotion>
splineEases each step with a curve given in keySplines

For easing, set calcMode="spline" and give keySplines one curve for each gap between values, so one fewer than there are values. Each curve is four numbers between 0 and 1, the same four numbers as a CSS cubic-bezier(). This is ease-in-out for a three-value animation: keyTimes="0;0.5;1" keySplines="0.42 0 0.58 1;0.42 0 0.58 1". If the count is wrong the animation does not run at all.

Turning and scaling: animateTransform

transform holds a list of functions, not one number, so it has its own element. type chooses the function, and the values are written as they would be inside its brackets:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <rect x="35" y="35" width="30" height="30" fill="teal">
    <animateTransform attributeName="transform" type="rotate"
      from="0 50 50" to="360 50 50" dur="3s" repeatCount="indefinite"/>
  </rect>
</svg>

0 50 50 means an angle of 0 about the point 50,50, so the square turns about its own centre. Leave the two centre numbers out and it swings round the top-left corner of the drawing, the point 0,0.

An <animateTransform> replaces any transform attribute the shape already has while it runs. Add additive="sum" to apply it on top of the existing transform. SVG transform explains the functions.

Moving along a path: animateMotion

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
  <path id="track" d="M20 80 Q100 -40 180 80" fill="none" stroke="silver"/>
  <path d="M-8 -5 L8 0 L-8 5 Z" fill="teal">
    <animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
      <mpath href="#track"/>
    </animateMotion>
  </path>
</svg>

The arrow travels along the grey curve and turns to face the way it is going.

  • <mpath href="#track"/> reuses a path that is already drawn. The other way is to write the route on the element itself: <animateMotion path="M20 80 Q100 -40 180 80" .../>.
  • rotate="auto" turns the shape to follow the direction of the path. auto-reverse points it backwards. The default, 0, keeps it upright.
  • The movement is added to the shape’s own position. Draw the shape around the point 0,0, as the arrow is here, or it will travel beside the path and not on it.

Starting on a click, or after another animation

begin accepts more than a time.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 40">
  <circle id="dot" cx="20" cy="20" r="10" fill="teal">
    <animate id="go" attributeName="cx" to="80" dur="1s" begin="dot.click" fill="freeze"/>
    <animate attributeName="fill" to="crimson" dur="0.5s" begin="go.end" fill="freeze"/>
  </circle>
</svg>

Click the circle and it moves to the right. When that animation ends, the second one turns it red.

begin valueStarts
2sTwo seconds after the file loads
dot.clickWhen the element with the id dot is clicked. mouseover and other events work the same way
go.endWhen the animation with the id go ends. go.begin is its start
go.end+1sOne second after that
indefiniteOnly when a script calls beginElement() on the animation element

Clicks need an SVG that can receive them: inline in the page, or in an <object>. In an <img> or a CSS background the timed animations play, but nothing responds to the pointer. SVG in HTML compares the placements.

Changing the shape of a path

<animate> can change the d of a path, which turns one shape into another:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 60">
  <path fill="none" stroke="teal" stroke-width="4" d="M10 30 Q50 0 90 30">
    <animate attributeName="d" dur="2s" repeatCount="indefinite"
      values="M10 30 Q50 0 90 30;M10 30 Q50 60 90 30;M10 30 Q50 0 90 30"/>
  </path>
</svg>

The curve bends down and back up. Every value must use the same commands in the same order, here M, Q, with only the numbers differing. If the commands differ, the path jumps from one shape to the next with nothing in between. The points of a polygon animate the same way. SVG path explains the commands.

The set element

<set> holds a value for a time with no steps in between. It suits values that have nothing halfway, such as visibility:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 40">
  <text x="50" y="26" text-anchor="middle" visibility="hidden">Done
    <set attributeName="visibility" to="visible" begin="2s"/>
  </text>
</svg>

The word appears two seconds after the file loads and stays, because a <set> with no dur lasts indefinitely.

Animation elements or CSS

Animation elementsCSS
Where the code livesIn the markup, inside the shapesIn a <style> block or a stylesheet
Plays in an <img>YesYes, if the CSS is inside the file
Can changeAttributes and properties, including d and pointsCSS properties only
Start on a click with no scriptYes, begin="id.click"No. CSS has :hover but no click
Start one after anotherbegin="other.end"A delay worked out by hand
Reduced motionNo built-in switch@media (prefers-reduced-motion)

For colour fades, spins and the line that draws itself, CSS is shorter and most people already know it: see SVG animation with CSS. The animation elements earn their place when a path changes shape, when animations follow each other, or when a click should start something in a file with no script.

Respect reduced motion

Some people set their device to reduce motion, and the animation elements cannot read that setting. Two ways round it:

For an inline SVG, pause the timeline from script:

if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
  document.querySelector('svg').pauseAnimations();
}

For an SVG shown as an image, offer a still version of the file with <picture>:

<picture>
  <source srcset="logo-still.svg" media="(prefers-reduced-motion: reduce)">
  <img src="logo-animated.svg" alt="Plotcaster">
</picture>

Common problems

What you seeCauseFix
The shape jumps back at the endfill defaults to removeAdd fill="freeze"
Nothing movesattributeName is misspelt or has the wrong capitals, or dur is missingCheck the name against the shape’s attributes, and give a dur
A rotation swings round the corner of the drawingNo centre was givenWrite the centre into the values: from="0 50 50" to="360 50 50"
The shape leaps to another place when the animation starts<animateTransform> replaced the shape’s own transformAdd additive="sum", or animate a wrapping <g>
A shape on a motion path travels beside the pathThe shape is not drawn at 0,0Draw it around the origin
A click does nothingThe SVG is in an <img> or a backgroundInline the SVG, or use <object>
An eased animation does not runkeySplines has the wrong number of curvesOne curve for each gap between values
A path snaps between shapesThe d values use different commandsGive every value the same commands in the same order

Questions

Is SMIL animation deprecated? No. Chrome, Edge, Firefox and Safari all support these elements. Older examples carry attributeType="XML", which the current draft of the SVG animation specification has removed: leave it out.

Does it work in an img tag? Yes. Timed animations play in an <img> and in a CSS background. Clicks and hovers do not reach the file there.

Can it be exported as a GIF? Not directly. A GIF is a fixed set of frames, so the animation has to be recorded frame by frame, and a short video file usually does that job better. The SVG to GIF page makes a still picture of the first moment only.

How do I test an animated file? Open it in the SVG viewer, which shows the file the way an <img> tag does. Timed animations should play there. Ones that wait for a click will not start, for the reason above.