SVG✨ Premium

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.

SMILanimateMotion

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

Balloon carried by the wind — hot-air balloon club home page — Path Following example 1

① Balloon carried by the wind — hot-air balloon club home page

WhenThe hero of a balloon flight club, in a dawn sky: a hot-air balloon slowly follows a dashed wind line across the full width, above the headline "Your first flight at sunrise" and the booking button.
WhyThe route gives the eye a direction, left to right and upward: exactly the idea of taking off. The balloon replaces the dot without changing the mechanism.
Settings--pthf-w:540px, --pthf-h:270px, transparent background; redrawn route (d="M6,64 C38,30 70,60 100,38 C130,16 160,42 194,16") in cream rgba(255,232,204,.4); follower replaced by a <g class="pthf-dot"> (envelope, basket) with rotate="0" to stay upright; dur="6s".
Boat route after a search — ferry comparison site — Path Following example 2

② Boat route after a search — ferry comparison site

WhenA booking site for boats between islands: after "Search", the map shows the route from the harbour to the island and a small boat sailing along it, above the departures found. Each new search restarts the trip from the harbour.
WhyA search result becomes concrete: you see the crossing, its shape and its destination before reading the times. The boat turns in the bends thanks to rotate="auto".
Settings--pthf-w:300px, --pthf-h:186px over a map drawn in the same 200 × 100 frame; white route rgba(255,255,255,.75); follower replaced by a hull and a sail; dur="4s". The scene button calls setCurrentTime(0) on the map's SVG.
Courier on the way — order tracking row — Path Following example 3

③ Courier on the way — order tracking row

WhenThe "My orders" list of a bike delivery service: only the current order shows a 128 × 64 px mini route, from the shop (ring) to the house, with the green dot moving along and the arrival time on the right. The other rows only have a status label.
WhyIn a list, the small animated route singles out the only "live" row at a glance; the arrival time next to it keeps the exact information.
Settings--pthf-w:128px, --pthf-h:64px, default route; green --pthf-dot:#22c55e, --pthf-track:rgba(34,197,94,.45), --pthf-glow:rgba(34,197,94,.55). Start and end markers drawn by the scene in the same frame.

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" and focusable="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-track so 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>.

Chrome 74+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 12.1+✓ Full (measured in WebKit)
Edge 79+✓ Full (Chromium engine)
Safari iOS 12.2+✓ WebKit engine, not verified on a real device
Chrome Android 74+✓ Full (Chromium engine)

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):

index.html — structure
<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 / propertyDefaultEffect
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

Yes. The script renames each copy's route (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.
Call 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>.
That is 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".