SVG · Typography · Physics

Text on a Path

Start with occupation, not animation

Before text can loop or collide on a path, you need to know how much of the path it actually occupies. That single measurement — the rendered text width as a fraction of arc length — is the foundation everything else builds on.

This lab breaks the problem into live prototypes. The first two show how to measure and animate a seamless marquee. The third introduces physics: a text block that slides, settles, and eventually collides with others.

1. Measuring text length on a path

SVG's textLength attribute can force a span, but for a natural marquee you want the real rendered width — which means one JS call to getComputedTextLength(). Once you have that number, the animation itself can be entirely declarative.

Declarative: SVG SMIL

SVG has native animation support via SMIL. With the measured span and gap converted to path percentages, the browser can drive the marquee without any per-frame script.

The SMIL version runs in the browser's own animation engine — no frame callbacks, no state updates. The tradeoff: it is static after setup. Changing text or timing requires rebuilding the animated elements (the demo re-keys the group on gap changes, which causes a brief flash). Pause and resume require calling the SVG-level pauseAnimations() API rather than toggling a flag. SMIL is also formally deprecated in favour of CSS/Web Animations, though all major browsers still support it.

Imperative: requestAnimationFrame

For full runtime control — dynamic text, smooth gap changes, play/pause, speed scrubbing — a RAF loop gives complete flexibility.

Steps to reproduce a seamless text marquee on an SVG path:

  1. Measure in SVG units. Render the text in a hidden off-screen <text> element using the same font settings as the visible copies. Call getComputedTextLength() — this returns the natural glyph width in SVG user units, the same coordinate system as the path, so no scaling conversion is needed.
  2. Convert to a path percentage. Divide the text width by the path's total arc length and multiply by 100. This is the fraction of the path a single copy occupies.
  3. Add the gap. Convert the desired gap between copies from SVG units to a path percentage the same way. The loop step is textSpan + gapSpan.
  4. Generate copies. Place the first copy at offset −scrollPct (just behind the path origin), then repeat every step until the offset exceeds 100%.
  5. Animate with the correct period. Advance scrollPct each frame by speed × dt. Wrap at step, not at 100. Because the visual pattern repeats exactly every step, the wrap lands in an indistinguishable state and the loop is seamless.

2. Gravity well

A text block on a path can be represented as a 1D interval: a center percentage and a fixed occupied span. Gravity is then simply the y-component of the normalized path tangent at that center point. When the path slopes downward, ty is positive and the block accelerates forward; when it slopes upward, ty is negative and the block decelerates. No spring target is needed — the well emerges from the path geometry itself.

The path is presampled at 600 arc-length-equal intervals. Each frame the physics reads the tangent ty at the block's center, integrates the resulting acceleration (with linear friction), and updates the path percentage. The HUD shows the live slope value so the relationship between geometry and motion stays visible.


Press ~ or D for controls