SVG's animation elements come from SMIL (Synchronized Multimedia Integration Language). <animate> moves one attributeName from/to values or through a values list over dur; repeatCount="indefinite" loops and fill="freeze" keeps the end. <set> jumps, <animateTransform type="rotate"> animates a transform, and <animateMotion> follows a path (<mpath href>), turning with rotate="auto". begin takes times and events (2s, click, intro.end+0.5s); keyTimes and keySplines shape the pacing.
<svg id="anim" width="140" height="102" font-family="Arial" font-size="12">
<path id="trail" d="M15 85Q70-25 125 85" fill="none" stroke="#bbb" stroke-dasharray="4"/>
<rect x="-12" y="-8" width="24" height="16" rx="2" fill="#1f5f8b">
<animateMotion dur="2s" repeatCount="indefinite" rotate="auto">
<mpath href="#trail"/></animateMotion>
<animate attributeName="fill" values="#1f5f8b;#e09a10;#1f5f8b" dur="2s"
repeatCount="indefinite"/></rect>
<text x="70" y="100" text-anchor="middle">t = 0 s</text>
</svg>
<script>
for (const t of [0.5, 1, 1.25]) {
const copy = anim.cloneNode(true); copy.id = '';
copy.querySelector('text').textContent = `t = ${t} s`;
document.body.append(copy);
copy.pauseAnimations(); copy.setCurrentTime(t);
}
anim.pauseAnimations(); anim.setCurrentTime(0);
</script>
Each outer <svg> has its own SMIL clock, which pauseAnimations() stops and setCurrentTime() seeks. Motion is paced by default, so at 0.5 s the book has covered a quarter of the arc's length. beginElement() and endElement() start and stop one animation from script.