Scroll Canvas Particle Text Formation — Canvas2D Particles Converge Into Type

Scroll Canvas Particle Text Formation · Scroll · Plain HTML, CSS & JS · Live preview

What's included

Features

Plain Canvas2D ctx.arc particles — no Three.js, no WebGL
Particle targets sampled directly from an offscreen ctx.fillText render via getImageData
Flat Float32Array particle state allocated once — zero per-frame allocation in the render loop
Dual-lerp motion (scroll-progress lerp plus a live-position trailing lerp) for an organic settle
Additive globalCompositeOperation blending for a glowing dust aesthetic
GSAP ScrollTrigger pins the stage and scrubs a single 0-1 progress value
Fully reversible — scrolling up scatters the word back into random dust
Resize-aware: text sampling and particle scatter both regenerate on window resize

About this UI Snippet

How to Build a Scroll-Driven Particle Text Formation With Canvas2D

Screenshot of the Scroll Canvas Particle Text Formation snippet rendered live

The Scroll Canvas Particle Text Formation snippet scatters a few thousand small dots randomly across a <canvas> and, as the visitor scrolls through a pinned stage, lerps every dot toward a target position sampled from the outline of a rendered word. It is plain Canvas2D — ctx.arc per particle — with no Three.js and no external noise or physics library.

Sampling the target shape from an offscreen render

Rather than hand-authoring particle target coordinates, the word "SCROLL" is drawn once to an off-screen <canvas> with ctx.fillText, then getImageData reads back every pixel's alpha channel. Any pixel with meaningful opacity becomes a candidate particle target, sampled every few pixels for density control — this is how the exact glyph shapes of any font, at any size, become particle targets with zero manual point-plotting.

Two lerps, not one

Each particle tracks three positions: a fixed startX/startY scatter point, a fixed targetX/targetY sampled from the text, and a live position that is itself lerped toward a scroll-eased interpolation of the two. Blending a per-frame live-position lerp on top of the scroll-progress lerp is what gives the convergence its slightly trailing, organic settle rather than every particle snapping instantly to its exact interpolated position each frame.

No per-frame allocation

All particle state lives in flat Float32Arrays allocated once in buildParticles(). The render loop only reads and writes into those arrays — never creates new objects or arrays per frame — keeping garbage collection out of the hot path even with thousands of particles animating every frame.

Additive blending for a glowing dust look

ctx.globalCompositeOperation = 'lighter' makes overlapping particles brighten instead of simply overdrawing each other, giving dense clusters (especially inside thick glyph strokes) a soft glow rather than a flat blob of solid color.

Browser support

Canvas2D and getImageData are supported in every modern browser, so unlike the native animation-timeline snippets in this collection, this technique needs no @supports fallback — only GSAP and ScrollTrigger are required as dependencies.

Customizing it

Change the sampled word in sampleTextPoints('SCROLL'), adjust step for denser or sparser sampling, or swap the hue range in the draw loop for a different color story. Pair it with Three.js Scroll Shader Fluid Gradient Wave for a WebGL companion effect.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out canvas pixel sampling and dual-lerp particle motion by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how getImageData turns a rendered word into particle target coordinates, and why the render loop blends a scroll-progress lerp with a separate per-frame trailing lerp instead of moving particles directly to their scroll-interpolated position. The same assistant is useful for extending the effect: ask it to sample a logo image instead of text, add a second word that the particles reform into after a delay, or vary particle size based on distance traveled. Treat the code as a conversation starter, not a finished artifact.

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-scrubbed particle text formation" in plain HTML, CSS, and JavaScript using Canvas2D (no Three.js, no WebGL) plus GSAP and GSAP's ScrollTrigger plugin loaded from a CDN (no bundler, no build step).

Requirements:
- A pinned section containing a full-size canvas with a 2D rendering context, resized to match its display size and device pixel ratio, regenerating particle data on window resize.
- Render a word once to an offscreen canvas using ctx.fillText at a large font size, then use getImageData to sample pixel coordinates wherever the alpha channel indicates the glyph is opaque, sampling every few pixels for density control, capped at a few thousand points for performance.
- Store each particle's scattered random starting position and its sampled text-target position in flat Float32Arrays allocated once — never allocate new arrays or objects inside the render loop.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin: true, start at top top, a numeric scrub, and a multi-hundred-percent end, animating a single plain progress value from 0 to 1 with linear easing.
- Every animation frame (requestAnimationFrame, independent of the scroll callback), apply an additional smoothstep easing to the scrubbed progress, compute each particle's eased target position by lerping between its start and text-target coordinates, then lerp the particle's separately tracked live position toward that eased target by a fixed small factor each frame (a trailing lerp, not a direct snap) for an organic settle.
- Draw each particle as a small filled arc using ctx.arc, with globalCompositeOperation set to 'lighter' (additive blending) so overlapping particles glow instead of flatly overdrawing each other, and vary hue and radius slightly with scroll progress.
- Confirm scrolling back up reverses the formation smoothly, scattering the word back into random dust.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<section class="ptf-stage" id="ptfStage">
  <div class="ptf-intro-overlay"><p>Scroll ↓ to converge the dust into a word</p></div>
  <canvas id="ptfCanvas"></canvas>
  <div class="ptf-hud"><span id="ptfPct">0</span>% formed</div>
</section>
<section class="ptf-bottom"><p>Hundreds of scattered particles, resolved into type.</p></section>

Step by step

How to Use

  1. 1
    Load the CDN scriptsAdd gsap.min.js and ScrollTrigger.min.js from the CDN panel, in that order.
  2. 2
    Paste HTML, CSS, and JSA scattered particle field appears inside a pinned canvas stage with a live "% formed" read-out.
  3. 3
    Scroll downParticles lerp from a random scatter into the shape of the word "SCROLL".
  4. 4
    Scroll back upThe word dissolves back into scattered dust as the scroll-eased progress reverses.
  5. 5
    Change the wordEdit sampleTextPoints('SCROLL') to render any word your font can display.
  6. 6
    Tune the densityLower step in sampleTextPoints for denser particle coverage of the glyphs.

Real-world uses

Common Use Cases

Brand or wordmark reveals
Resolve a logo word or tagline out of scattered particles as visitors scroll into a hero.
Album and product launch pages
A kinetic typographic reveal suited to music, fashion, or tech launches.
Generative art and creative-coding portfolios
Demonstrate pixel-sampling-driven particle placement as a standalone technique piece.
Teaching canvas pixel sampling
A compact real-world example of using getImageData to derive particle targets from any rendered shape.
Scroll-story chapter breaks
Use the formation as a mid-page transition, similar in spirit to Three.js Scroll Galaxy Formation.
Related: Three.js Scroll Shader Ripple Distortion
See Three.js Scroll Shader Ripple Distortion for a WebGL scroll-driven alternative worth comparing against this Canvas2D approach.

Got questions?

Frequently Asked Questions

The word is rendered once to an offscreen canvas with ctx.fillText, then getImageData reads back every pixel’s alpha channel. Any sufficiently opaque pixel, sampled every few pixels for density control, becomes a particle target coordinate — no manual point-plotting or font-outline parsing is needed.

The visual only needs simple filled circles composited additively, which ctx.arc handles directly with no scene graph, camera, or shader pipeline required. Three.js is used elsewhere in this collection for genuinely 3D or shader-driven effects; a flat particle field sampled from 2D text is a better fit for plain Canvas2D.

The scroll-progress lerp computes where each particle should be heading based on scroll position, while a second per-frame lerp trails the particle’s live position toward that target rather than snapping to it instantly. The trailing lerp is what gives the convergence its organic, slightly delayed settle instead of a mechanical linear interpolation.

No — Canvas2D and getImageData are supported in every modern browser, so no @supports fallback is needed here; the only requirements are the GSAP and ScrollTrigger CDN scripts.

Yes — replace the ctx.fillText call in sampleTextPoints with ctx.drawImage of a loaded logo image (drawn at the same offscreen canvas size), and the getImageData alpha-sampling logic works identically against the image’s silhouette instead of a text glyph.