Path Drawing
A purple curve draws itself from left to right under a dot that travels it, then erases as the dot comes back, 3 s each way: the line is born exactly under the dot, at every moment — pure CSS (stroke-dashoffset + offset-path) kept in step by pathLength="1", full line and still 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-0039-root block is at least 300 px high, background #0a0a0f, and centers .mp-drawing-scene (300 × 220 px). The scene holds an <svg> of the same size (viewBox="0 0 300 220", 1 unit = 1 px) with a single path, M30,180 Q80,30 150,110 Q220,190 270,60: two arcs that rise, dip and rise again, in purple #8b5cf6, 2 px, round caps. It is 358 px long. On top, a 10 px purple dot with a glow, in position: absolute in the top left corner.
The line is drawn with the dash technique: stroke-dasharray: 1 cuts the line into one full dash and one gap of the same length, and stroke-dashoffset slides that pattern along. At 1, only the gap shows; at 0, the dash covers the whole line. The pathLength="1" attribute on the <path> sets the path's length to 1, whatever its real size: so the dash matches the line exactly, with no math and no JavaScript, and a stroke-dashoffset of 0.4 means "60% drawn".
The dot follows the same path, copied into offset-path: path(…), with offset-distance going from 0 to 100%. Both animations, mpDraw for the line and mpOrbit for the dot, share the same duration (3 s), the same easing (ease-in-out) and the same round trip (alternate): on every frame, the drawn share equals the traveled share. Measured in Chromium, Firefox and WebKit: the line's tip and the dot move by the same percentage (less than 0.1% apart), and the dot's center stays on the line (less than 0.1 px). On the way back, the line erases behind the returning dot. Under prefers-reduced-motion: reduce, a block scoped to .fx-0039-root shows the whole line and sets the dot at the finish.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, the curve is complete and the dot sits at the end, as at the finish of a drawing. Measured in Chromium, Firefox and WebKit: 0 running animations, 0% of pixels change in 800 ms. - Route information: if the line stands for a trip or a route, give its start, finish and figures as text (like "Orléans → Rouen, 3 h 12 min" in the first example) and put
aria-hidden="true"on the root: a screen reader does not read a drawing. - Contrast: the 2 px purple line on
#0a0a0fshows well; on a light background or a map, thicken it (3 to 5 px) and pick a strong color, in CSS (stroke,stroke-width). - No interaction: the effect reacts to neither mouse, finger nor keyboard.
Browser compatibility
Pure CSS: animated stroke-dasharray and stroke-dashoffset, SVG pathLength attribute, offset-path: path(). No dependency, no JavaScript. Measured in Chromium 151, Firefox 153 and WebKit 26.5: line and dot in step (less than 0.1% apart), dot centered on the line, stop under reduced motion, two independent copies on the page.
Without offset-path (Safari before 16), the curve draws and erases normally, but the dot stays still in the scene's top left corner (measured by removing the property); @supports not (offset-path: path("M 0 0")) lets you hide it and keep only the line.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0039-root">
<div class="mp-drawing-scene"> <!-- 300 × 220 px -->
<svg viewBox="0 0 300 220" fill="none">
<path class="mp-drawing-path" pathLength="1"
d="M30,180 Q80,30 150,110 Q220,190 270,60" …></path>
</svg>
<div class="mp-drawing-dot"></div> <!-- same path in offset-path -->
</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 and offset-path) |
M30,180 Q80,30 150,110 Q220,190 270,60 |
Write the same path in both places, in the coordinates of the 300 × 220 px scene. Thanks to pathLength="1", the line stays in step with the dot with nothing to recompute, whatever the new path's length. |
Duration (CSS — 3s in both animations) |
3 s each way | Change it on the line AND the dot, or they drift apart. Same rule for the easing (ease-in-out). |
Draw without erasing (CSS — alternate in both animations) |
round trip | Without alternate, the line draws and then vanishes at once at the start of each cycle. To keep it drawn, use animation-iteration-count: 1 and animation-fill-mode: forwards on both. |
Line color and width (CSS — .mp-drawing-path { stroke: …; stroke-width: … }) |
purple #8b5cf6, 2 px |
The CSS rule wins over the SVG attributes. Do not add your own stroke-dasharray: that is what draws the line. |
Enlarge or flip (CSS — transform on .mp-drawing-scene) |
none | scale(1.5) enlarges line and path together; scale(-1, 1) makes the path start from the right. Do not change only the width: offset-path is in pixels. |
Dot (CSS — .mp-drawing-dot) |
10 px purple with a glow | The dot turns along the curve (offset-rotate: auto): a pencil or bird icon will follow the bends; offset-rotate: 0deg keeps it upright. |
FAQ
pathLength="1" tells the browser to count the line as 1: stroke-dasharray: 1 covers it exactly, and the line advances by the same percentage as offset-distance, for any path.animation-play-state: paused on the line and the dot, then, with an IntersectionObserver, switch them to running when the block enters the screen; add animation-iteration-count: 1 and animation-fill-mode: forwards so the line stays drawn.