Wave Path
Three violet, pink and cyan dots (14, 10 and 8 px) follow one another one second apart along a two-crest sine wave, at constant speed, in 4 s: a small line moving up and down the waves without the gaps changing — CSS only, zero JavaScript, three dots resting on the wave 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-0037-root root (background #0a0a0f, at least 300 px tall) centers a 300 × 200 px .mp-wave-scene. An <svg> draws, in white at 6%, a sine wave with two full crests: four 75 px wide cubic Bézier curves that rise and fall 37.5 px around the center line y = 100. Three .mp-wave-dot divs get the same path as offset-path: path(…) and the same animation: offset-distance from 0 to 100% in 4 s, linear, looping.
The offset is what makes the line. The 14 px violet dot #8b5cf6 starts at 0, the 10 px pink #ec4899 has animation-delay: -1s, the 8 px cyan #06b6d4 -2s: a negative delay starts the animation "midway", so the cyan one leads, the pink one is in the middle, and the big violet one brings up the rear, each a quarter of the path (111 px along the curve) from the next. With linear, the speed along the curve is constant: the gaps do not move, even on the climbs. At the end of the path, each dot starts again on the left; since the wave ends at the height where it begins, that return is a horizontal jump from one edge to the other.
The dots have no negative margin: the browser places their center on the path, so all three run exactly on the drawn line whatever their size (measured gap: 0.03 px in Chromium 151, Firefox 153 and WebKit 26). By default, offset-rotate is auto: each dot turns with the slope. On a circle you cannot see it; if you put a label or an icon in it, add offset-rotate: 0deg, as in the second scene above.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, a block scoped to.fx-0037-rootstops the three dots on the wave, at 15, 40 and 65% of the path: the same quarter gap, a crest, a trough, a crest. Measured: 0% of pixels change over 0.8 s in Chromium, Firefox and WebKit, versus 0.9% in normal mode. - Decorative content: the dots are empty
divs ignored by screen readers. If each dot stands for someone or something (a group of riders, an animal), write it in a caption or in the dot, withoffset-rotate: 0deg. - Contrast: on the dark background, all three colors pass the 3:1 threshold for graphical elements; the wave, at 6%, is only a guide. On a light background, darken it.
- Rhythm: a 4 s loop at constant speed, no flashing.
Browser compatibility
CSS only: offset-path: path(), animated offset-distance and negative animation-delay, no JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26 (Safari's engine): same positions for the three dots, same gaps, same stop under reduced motion. Not checked on a real Safari.
A browser without offset-path stacks the three dots, still, at the top left of the scene; the wave stays drawn.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0037-root">
<div class="mp-wave-scene">
<svg viewBox="0 0 300 200" fill="none">
<path d="M0,100 C25,50 50,50 75,100 … 300,100" … />
</svg>
<div class="mp-wave-dot mp-wave-dot--1"></div>
<div class="mp-wave-dot mp-wave-dot--2"></div>
<div class="mp-wave-dot mp-wave-dot--3"></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 |
|---|---|---|
Colors and sizes (CSS — .mp-wave-dot--1, --2, --3) |
Violet 14 px, pink 10 px, cyan 8 px | Change background, box-shadow, width and height. The center stays on the wave whatever the size: no margin to recompute. |
Order and spacing of the line (CSS — animation-delay) |
0s, -1s, -2s (a quarter of the path between two dots) | The most negative delay leads. To put the big dot in front, swap them: -2s for --1, 0s for --3. Closer delays (-0.5s) tighten the line. |
Speed (CSS — 4s in the animation) |
4 s | Keep the delays proportional to the duration (a quarter of the duration for a quarter of the path). linear keeps the gaps constant; another easing would make them vary. |
| Wave shape (HTML + CSS) | 2 crests of 37.5 px over 300 px | The path is written twice, in the svg path d and in the offset-path: change both. |
| Label or icon in a dot (HTML + CSS) | Empty dots | Put a span in the dot, with position: absolute, and add offset-rotate: 0deg to the dot, otherwise the text turns with the slope. |
Background (CSS — .fx-0037-root) |
#0a0a0f | Change or remove the background; on a light background, darken the wave (white at 6% by default). |
FAQ
overflow: hidden) so the jump happens out of view, or add a fade at both ends with opacity keyframes.linear, each dot moves at the same speed on the path, and a quarter of the duration separates two dots. Seen horizontally, the gap varies a little, since the wave rises and falls; measured, all three dots stay on the line within 0.03 px in the three engines.