SVG Path Follow
A glowing purple dot goes back and forth along a dashed S-curve, slowing down at each end, followed by two smaller, paler echoes 0.15 s and 0.3 s behind: a ghost trail that bunches up at the stops and stretches out at full speed — pure CSS with offset-path, frozen mid-course when the user reduces motion.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →
3 usage examples



How it works
The .fx-0032-root block is at least 300 px high, background #0a0a0f. It centers .mp-pathfollow-scene, a 300 × 200 px scene that holds two things: a <svg> of the same size (viewBox="0 0 300 200", so 1 unit = 1 px) that draws the S-curve as a dashed purple line at 15% (stroke-dasharray="6 4"), and three circles in position: absolute placed at the scene's top left: the 18 px dot and two 10 px echoes, at 60% and 30% opacity.
The three circles share the same path, offset-path: path("M 20 100 C 80 20 150 180 220 80 C 260 20 280 100 280 100"), an exact copy of the SVG line. Since path() coordinates are read in pixels from the scene's corner, and the SVG is the same size as the scene, the dot's center runs exactly on the dashed line (measured: less than 0.1 px off in Chromium, Firefox and WebKit). The mpOrbit animation moves offset-distance from 0 to 100% in 3 s, with ease-in-out and alternate: 3 s out, 3 s back, slow at both ends, fast in the middle of the curve (321 px long).
The echoes run the same animation with a negative delay: -5.85s and -5.7s. A full round trip takes 6 s; starting 5.85 s "ahead" amounts to being 0.15 s behind, with no wait at the start. So the echoes follow the dot both ways. Because they are offset in time, not in space, they bunch up against the dot when it slows down and stretch out when it speeds along (about 25 px apart at full speed). Under prefers-reduced-motion: reduce, a block scoped to .fx-0032-root stops everything and sets the dot at 62% of the curve, the echoes at 57 and 52%, just behind it.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, the dot and its two echoes stop in the middle of the curve, in running order: the direction of travel still reads. Measured in Chromium, Firefox and WebKit: 0 running animations, 0% of pixels change in 800 ms. - Decorative content: there is no text in the effect. If it illustrates a trip (a ferry, a route), write the times or the distance as text next to it and put
aria-hidden="true"on the root. - Curve contrast: the purple dashed line at 15% is just a guide. If the route is the information, raise its opacity (example 1: white at 75% over the water); the dot itself stands out on any dark background.
- No interaction: the effect reacts to neither mouse, finger nor keyboard.
Browser compatibility
Pure CSS: offset-path: path(), animated offset-distance, negative animation-delay, inline SVG for the curve. No dependency, no JavaScript. Measured in Chromium 151, Firefox 153 and WebKit 26.5: dot centered on the line (less than 0.1 px off), echoes behind the dot both out and back, stop under reduced motion, two independent copies on the page.
Without offset-path (Safari before 16), the dashed curve shows, but the dot and its echoes stay piled up, still, in the scene's top left corner (measured by removing the property). If that audience matters, hide them with @supports not (offset-path: path("M 0 0")).
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0032-root">
<div class="mp-pathfollow-scene"> <!-- 300 × 200 px -->
<svg viewBox="0 0 300 200" fill="none">
<path d="M20,100 C80,20 150,180 220,80 C260,20 280,100 280,100" … stroke-dasharray="6 4"></path>
</svg>
<div class="mp-pathfollow-trail mp-pathfollow-trail--2"></div> <!-- 0.3 s behind -->
<div class="mp-pathfollow-trail mp-pathfollow-trail--1"></div> <!-- 0.15 s behind -->
<div class="mp-pathfollow-dot"></div>
</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 |
|---|---|---|
Path (HTML + CSS — d attribute of the <path> and offset-path) |
S-curve from (20, 100) to (280, 100) | Write the same path in both places, in the coordinates of the 300 × 200 px scene. If you change the scene size, change the SVG viewBox to match, or the line and the dot drift apart. |
Enlarge (CSS — transform: scale() on .mp-pathfollow-scene) |
none | offset-path is in pixels: changing only the scene width would stretch the SVG line but not the path. A scale(1.5) enlarges both together. |
Duration (CSS — 3s in animation) |
3 s per trip | If you change it, recompute the echo delays: delay = -(2 × duration - lag). For 4 s and a 0.15 s lag: -7.85s. |
Echo spacing (CSS — animation-delay of .mp-pathfollow-trail--1 and --2) |
-5.85s and -5.7s (0.15 and 0.3 s behind) |
-5.7s and -5.4s double the trail (0.3 and 0.6 s). For a one-way trip with no return, remove alternate: a cycle then lasts 3 s and the delays become -2.85s and -2.7s. |
Pace (CSS — ease-in-out in animation) |
slow at both ends | linear gives a constant speed: the echoes then stay the same distance from the dot all along. |
Colors (CSS — background, box-shadow; SVG — stroke) |
purple #8b5cf6, echoes purple at 30% |
The line color can also be set in CSS: .mp-pathfollow-scene path { stroke: … } wins over the attribute. |
FAQ
-0.15s would put the echo in front of the dot.offset-rotate defaults to auto, so the dot turns to follow the curve. On a circle you cannot tell. If you replace the dot with an icon (a boat, an arrow), it will follow the bends; add offset-rotate: 0deg to keep it upright.