Text-Path Marquee
Up to three SVG text lines glide on rippling waves; the pointer lifts a bump under it and a drag moves the text by hand.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →
3 usage examples



How it works
The scene is an 800 × 300 unit SVG (preserveAspectRatio="xMidYMid slice": it fills the frame and crops what overflows). It holds up to three .tpm-path paths and as many <textPath>: the script gives each pair an id unique to the instance, reads your text from the <textPath> and repeats it up to four times the path length. Track 1 in the middle (y = 148), track 2 at the top (72), track 3 at the bottom (228).
Every 33 ms each path is rebuilt from 80 segments: a sine wave of amplitude 22 (×0.68 for the top and bottom tracks) whose phase drifts 0.42 radian per second and whose amplitude breathes ±20%. The text moves 60 px/s along the path, at relative speeds of 1, 0.58 and 1.22 for parallax. Under the pointer, a bell-shaped bump (32 units high, about 104 units wide) lifts the three lines and follows the cursor with a slight lag.
Hovering the scene with a mouse or moving focus into it stops the scrolling and the wave drift; the bump keeps following the pointer. Dragging horizontally (mouse or finger) moves the text forward or back by hand. The Pause button freezes everything. Each .fx-0833-root is independent and the loop stops off screen.
Accessibility
- Real text: the repeated SVG carries
aria-hidden="true"; the script copies each distinct sentence once into.tpm-sr, a visually hidden text that screen readers read. The scene has themarqueerole and anaria-labelto adapt. - Pause: hover and keyboard focus stop the scrolling; the Pause button (
aria-pressed) stops everything until the next click (WCAG 2.2.2). - Reduced motion: under
prefers-reduced-motion: reduce, no loop runs: the waves stay still and the Pause button is hidden. Horizontal dragging still works to read the rest of the text. - Touch:
touch-action: pan-ylets the page scroll with a finger; only a horizontal gesture moves the text.
Browser compatibility
SVG <textPath> with the unprefixed href attribute, getTotalLength(), getComputedTextLength(), Pointer Events, IntersectionObserver (feature-tested). Measured on 2026-11-08 in Chromium, Firefox and Playwright's WebKit: 60 px/s scrolling, stop on hover, focus and button, two independent copies, reduced motion, page still scrolling under a finger at 390 px.
Without JavaScript, the paths are empty and nothing shows in the SVG: provide a visible heading elsewhere if the text matters. Without CSS masks, the edge fade becomes a gradient laid on top.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0833-root" data-tpm-speed="60" data-tpm-amp="22" data-tpm-bump="32">
<div class="tpm-scene" role="marquee" aria-label="…">
<svg class="tpm-svg" viewBox="0 0 800 300" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<defs><path class="tpm-path" d=""/> ×3</defs>
<text class="tpm-text-solid"><textPath>…</textPath></text>
<text class="tpm-text-outline"><textPath>…</textPath></text>
<text class="tpm-text-solid tpm-text-soft"><textPath>…</textPath></text>
</svg>
<span class="tpm-sr"></span>
<div class="tpm-fade" aria-hidden="true"></div>
<button class="tpm-pause-btn" type="button" aria-pressed="false"><span class="tpm-pause-icon" aria-hidden="true">⏸</span> Pause</button>
</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 |
|---|---|---|
Text (HTML — content of each <textPath>) |
“EFFECT.LABS — VANILLA EFFECTS — NO FRAMEWORK — ” on all three tracks | One sentence per track, ending with a separator (“—”, “~”…) so the repetition reads well. |
Number of tracks (HTML — .tpm-path / <text> pairs) |
3 | 1, 2 or 3. With one, keep the middle track (y = 148). |
Speed (HTML — data-tpm-speed on .fx-0833-root) |
60 (px/s along the path) | From 0 to 600; the top and bottom tracks keep their relative speeds (0.58 and 1.22). |
Amplitude (HTML — data-tpm-amp) |
22 (SVG units) | Wave height, from 0 (straight line) to 80. |
Frequency (HTML — data-tpm-freq) |
1.5 | Number of waves across the SVG width, from 0.2 to 6. |
Pointer bump (HTML — data-tpm-bump) |
32 (SVG units) | Height of the bump under the cursor; 0 removes it. |
Colors and sizes (CSS — .tpm-text-solid, .tpm-text-outline, .tpm-text-soft, .tpm-fade) |
White 28, violet outline 26, violet 24; background #0a0a0f | fill, stroke and font-size (in SVG units). Give .tpm-fade and the solid text's stroke your background color. |
FAQ
font-size of the text classes (58 units give about 17 px in that case) and preferably keep a single track.<textPath> keeps its own text and its own repeat length. The text read by screen readers includes each distinct sentence once.