Scroll Text Draw — Free GSAP SVG Outline Snippet

Scroll Text Draw · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Self-drawing text
stroke-dashoffset traces each glyph.
Two-layer type
Separate stroke and fill text stacks.
Gradient paint
One linearGradient inks stroke and fill.
Overlapped beats
Ink starts before the draw finishes.
Residual outline
Stroke dims to 25%, not to zero.
Responsive viewBox
Centers and scales with no JS measuring.
Pinned + scrubbed
The scrollbar is the pen.
Reversible
Scrolling up erases the word.

About this UI Snippet

Scroll Text Draw — A Headline That Sketches Itself, Then Inks In

Screenshot of the Scroll Text Draw snippet rendered live

The scroll text draw renders a headline as SVG outlines that trace themselves in as you scroll — like watching a hand letter the word — and once the strokes complete, the gradient fill floods in and the outline recedes. Scroll up and the word erases itself. This snippet builds it with GSAP ScrollTrigger (from a CDN) and the classic stroke-dashoffset trick applied to <text>.

The dash trick: a stroke hidden by its own offset

Every SVG stroke can be dashed with stroke-dasharray and shifted with stroke-dashoffset. Setting both to the same value — here 1000, longer than any letter's outline — makes the visible dash start exactly one dash-length away, so the stroke is completely invisible. Tweening the offset back to 0 slides the dash along each glyph's contour, and the letters appear to draw themselves. On a scrubbed timeline, the scrollbar becomes the pen.

Why the dash length is a constant, not a measurement

For a <path> you'd measure getTotalLength(), but SVG <text> has no path-length API — its outlines live inside the font. The pragmatic standard is a constant comfortably longer than the longest glyph contour. The only cost of oversizing: some letters finish drawing slightly before others (each contour shorter than 1000 completes early in the tween). Visually this reads as natural hand-lettering variation. If you need per-letter precision, convert the text to outlines in an editor and export real paths.

Two stacked text layers separate stroke from fill

The word is rendered twice at identical coordinates: a stroke-only layer (fill: none) and a fill-only layer (opacity: 0). Splitting them means the draw and the ink are independently animatable — the fill fades in at timeline position 1.6, while the stroke simultaneously dims to 25% instead of vanishing, leaving a subtle contour like inked linework under a wash. A single text element can't crossfade its own stroke against its fill this cleanly.

Gradient via url() reference

Both layers paint with url(#stdGrad), a <linearGradient> defined in <defs>. SVG paint servers apply to stroke and fill alike, so the outline and the flood share the exact same indigo-to-cyan ramp — a coherence trick that's awkward with CSS text (where stroke gradients need background-clip hacks).

Choreography: draw, ink, caption

The pinned timeline runs three beats: the outline draws for 2 units, the ink crossfade starts at 1.6 (slightly overlapping the final strokes so it feels continuous, not staged), and the letter-spaced caption fades at 2.2 as a sign-off. end: '+=180%' gives the whole sequence almost two viewport-heights of scroll, and scrub: 0.4 smooths wheel ticks into pen glides.

text-anchor centers without layout math

text-anchor: middle with x="50%" centers the word inside the responsive viewBox, so the SVG scales from phone to desktop with zero JS measurement — the drawing coordinates are resolution-independent by nature.

Customizing it

Change the word (bump DASH if you use a long word in a heavier font), the gradient stops, or the stroke width for a bolder sketch. Pair it with the line-drawing scroll SVG path draw, a scroll text clip reveal for paragraph copy, or gradient text for static headings elsewhere.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work through the two-layer text trick alone. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why SVG text elements need a constant dash length instead of getTotalLength like an SVG path would use, or why the word is rendered as two stacked text layers (stroke-only and fill-only) instead of animating one element's stroke and fill together. The same assistant can help optimize it — asking whether the fixed DASH value of 1000 is safe for a heavier or longer word, or whether the timeline's overlap between the stroke finishing and the fill starting (at position 1.6 while stroke duration is 2) should be tuned differently for a slower reveal. It's also useful for extending the effect: ask it to add a second word that draws immediately after the first, animate the gradient's stop colors as the ink fills in, or convert the text to real outline paths for exact per-letter draw timing. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll text draw" self-sketching headline in plain HTML, CSS, and JavaScript using GSAP with its ScrollTrigger plugin (load both from a CDN) and the SVG stroke-dash technique applied to text elements — no path conversion tools, no canvas.

Requirements:
- An inline SVG containing a linearGradient definition in defs, and the same headline word rendered twice at identical x/y coordinates as two separate SVG text elements: one styled with fill: none and a gradient stroke (the outline layer), and one styled with a gradient fill and starting opacity 0 (the ink layer). Add a smaller subtitle text element also starting at opacity 0.
- Because SVG text elements have no getTotalLength API (unlike SVG path elements), use a single constant dash length value picked comfortably larger than the longest letter's contour, and set both stroke-dasharray and stroke-dashoffset on the outline layer to that same constant so its stroke starts completely invisible.
- Register one GSAP timeline on a single ScrollTrigger with pin: true and a scrub smoothing value, over a scroll distance of somewhat under two viewport heights.
- On that timeline: first tween the outline layer's strokeDashoffset down to 0 with ease none over roughly the timeline's first half, tracing each letter's outline progressively; then, starting slightly before that stroke tween fully finishes (an overlapping timeline position, not a strictly sequential one), fade the ink layer's opacity up to 1 while simultaneously fading the outline layer's opacity down to a low but nonzero value like 0.25 (so a faint outline remains visible under the fill rather than disappearing completely); finally fade in the subtitle text after the ink has finished.
- Center the text using SVG's text-anchor: middle with percentage-based x coordinates inside a responsive viewBox, so the whole composition scales correctly at any screen size with no JavaScript-based centering math.
- Confirm scrolling back up reverses the entire sequence — ink fades out, the outline brightens back up, and the stroke un-draws letter by letter — purely because the timeline is scrubbed, with no separate reverse code path.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
  2. 2
    Paste HTML, CSS, and JSThe SVG headline renders with invisible strokes.
  3. 3
    Scroll into the stageLetter outlines start tracing themselves in.
  4. 4
    Keep scrollingGradient ink floods the letters; the outline dims.
  5. 5
    Scroll back upThe word un-inks and erases stroke by stroke.
  6. 6
    Change the wordEdit both text layers; raise DASH for longer glyphs.

Real-world uses

Common Use Cases

Brand moments
Draw the product name at a chapter break inside a scroll pin story.
Portfolio intros
Sketch your name as the opener, then a scroll reveal grid of work.
Event sites
Trace the event wordmark; add a countdown timer below.
Diagram narratives
Combine with scroll SVG path draw so lines and labels draw together.
Section headers
A drawn heading between a scroll curtain reveal and body copy.
Static fallbacks
Use gradient text where motion isn't wanted.

Got questions?

Frequently Asked Questions

The stroke is dashed with stroke-dasharray: 1000 and shifted by stroke-dashoffset: 1000 — the visible dash starts exactly one dash-length away, so nothing shows. Tweening the offset to 0 slides the dash along each glyph's contour, revealing the outline progressively. On a scrubbed pinned timeline, scroll position controls how much of each letter is traced.

getTotalLength() only exists on path elements; SVG text exposes no contour-length API because outlines live in the font. A constant longer than the longest glyph contour is the standard workaround — shorter letters just finish a bit early, which reads as hand-lettering variation. For exact per-letter timing, convert the text to outlines and animate real paths.

One text layer is stroke-only and one is fill-only, stacked at identical coordinates. That separation lets the draw and the ink animate independently: the fill fades in while the stroke dims to 25% rather than disappearing. A single element can't crossfade its own stroke against its own fill with separate timings.

Yes — any font-family works since the browser strokes whatever glyphs it renders, though bold geometric faces read best because their contours are long and clean. For multiple words, add more text-layer pairs at different y positions and stagger their tweens on the timeline; raise DASH if a heavier face has longer contours than 1000 units.

Inline the SVG in your template and run the gsap.set and timeline in a mount effect — useEffect, onMounted, or ngAfterViewInit — scoped with gsap.context to a ref, reverting on cleanup so the pin unregisters. Keep the gradient id unique per instance (suffix it with a component id) or two rendered copies will fight over url(#stdGrad). Layout around the SVG works fine in Tailwind.