Path Following
A glowing dot that loops along a dashed curved route, in native SVG: swap the dot for a boat or a balloon and it follows the curve.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →
3 usage examples



How it works
The route is a dashed <path class="pthf-track"> (2-unit stroke, 5,5 dashes) in a 200 × 100 unit SVG: two waves going from (10,50) to (190,50). The follower .pthf-dot is an indigo #6366f1 circle of radius 8 with a glow (8 px drop-shadow).
The motion is SMIL: <animateMotion dur="3s" repeatCount="indefinite" rotate="auto"> with an <mpath> pointing at the route. The dot starts on the left, reaches the right in 3 s and starts over. Since the route is read from the path's d, changing that d changes the route. rotate="auto" turns the follower along the curve: invisible on a circle, useful as soon as you put an oriented shape in its place (arrow, boat, plane).
The script gives each copy its own route id (pthf-track-1, pthf-track-2…) and reconnects its <mpath> to it: two copies on one page each follow their own route, even if one is edited or hidden. It also handles reduced motion, which CSS cannot stop on a SMIL animation.
Accessibility
- Decorative: the SVG has
aria-hidden="true"andfocusable="false". If the route carries information (a parcel on its way, a crossing), write it next to it: duration, arrival time, status. - Reduced motion: with
prefers-reduced-motion: reduce, the script pauses the drawing (pauseAnimations()) with the dot resting at the end of the route (arrival), and resumes if the setting changes during the visit. - No forced interaction: nothing to hover or click, no focusable element added.
- Contrast: the route is deliberately faint (indigo at 30% opacity); on a light background, strengthen
--pthf-trackso it can be seen.
Browser compatibility
SMIL <animateMotion> + <mpath href> (no xlink:), pauseAnimations() / setCurrentTime(), prefers-reduced-motion. Measured in Chromium, Firefox and WebKit (Playwright): 0 errors, two copies with distinct ids, the 2nd copy follows its own curve when the 1st one has another route and is hidden, dot stopped at the end of the route under reduced motion. The versions below are those that read an unprefixed href on <mpath>.
Without SMIL, the dashed route stays visible and the follower sits at the top left of the drawing (0,0). Without JavaScript, a single copy per page works perfectly; with several copies, they all follow the first route.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0604-root">
<div class="pthf-stage">
<svg class="pthf-svg" viewBox="0 0 200 100" aria-hidden="true" focusable="false">
<path id="pthf-track" class="pthf-track" d="M10,50 C40,10 60,90 100,50 C140,10 160,90 190,50"/>
<circle class="pthf-dot" r="8">
<animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
<mpath href="#pthf-track"/>
</animateMotion>
</circle>
</svg>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Size (CSS — --pthf-w / --pthf-h) |
200px / 100px | Drawing size; the 200 × 100 frame keeps its proportions and is centered in the box. |
Colors (CSS — --pthf-track, --pthf-dot, --pthf-glow) |
rgba(99,102,241,.3) / #6366f1 / rgba(99,102,241,.6) | Dashes, follower and glow, adjustable per copy with a style on the root. |
Block background and height (CSS — --pthf-page, --pthf-root-h) |
#0a0a0f / 300px | transparent and 0px to lay the route over a map, a photo or a list row. |
Route (HTML — d of .pthf-track) |
M10,50 C40,10 60,90 100,50 C140,10 160,90 190,50 | Any SVG path within the 0-200 × 0-100 frame: the follower travels it from its first point to its last. |
Trip time (HTML — dur of <animateMotion>) |
3s | Time to go from start to end; repeatCount="1" and fill="freeze" for a single trip that stops on arrival. |
Follower (HTML — the .pthf-dot element) |
circle of radius 8 | Replace it with a <g class="pthf-dot"> holding your drawing (centered on 0,0) and keep the <animateMotion> inside. rotate="auto" turns it along the curve, rotate="0" keeps it upright. |
FAQ
pthf-track-1, -2…) and attaches its <mpath> to it. Without this, both copies would share the same id and would all follow the first route. If you add a copy after page load (injected content), run the script again or give it a unique id yourself.svg.setCurrentTime(0) on the relevant .pthf-svg: the follower goes back to the start. For a route played only once, set begin="indefinite" repeatCount="1" fill="freeze" on <animateMotion> and call animateMotion.beginElement() from a real <button>.rotate="auto": it aligns the follower with the direction of the curve, which suits an arrow, a boat or a plane drawn pointing right. For a hot-air balloon, a character or text, replace it with rotate="0".