GSAP Scroll Text Scramble — Free Decode-In Headline Snippet

GSAP Scroll Text Scramble · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Tweened proxy value
A plain object's progress drives character reveal.
Left-to-right decode
Resolved characters lock in order, not all at once.
Live re-randomization
Unresolved characters flicker every frame.
Data-driven target text
data-final lets one function scramble any element.
Independent ScrollTriggers
Headline and subhead decode on their own timing.
Reversible
Scrolling back out re-scrambles the text.
No paid plugin
Built from a plain onUpdate callback, not a bonus plugin.
Space-aware
Spaces are preserved instead of scrambled as characters.

About this UI Snippet

GSAP Scroll Text Scramble — Headlines That Decode as You Scroll

Screenshot of the GSAP Scroll Text Scramble snippet rendered live

The scroll text scramble snippet gives a headline a hacker-terminal decode effect — random characters flicker across the text and resolve into the real words left to right, timed to when the headline scrolls into view. It's built with a small hand-rolled scramble function driven by a GSAP tween, plus ScrollTrigger from a CDN — no paid text plugin required.

A tweened proxy drives the scramble

Instead of animating the DOM text directly (which GSAP can't tween as a number), the function tweens a plain object's progress value from 0 to 1 with gsap.to(obj, { progress: 1, onUpdate: ... }). Every onUpdate tick, that progress value determines how many characters from the left are "resolved" versus how many are still random — Math.floor(obj.progress * len) gives the cutoff index.

Left-to-right resolution, not a flat swap

For each character position, resolved indices (i < revealCount) show the real final character; everything after it is replaced with a random pick from a character pool on every single frame, so unresolved letters keep visibly flickering rather than sitting static. That per-frame re-randomization of only the *unresolved* tail is what produces the classic "decoding" look rather than a simple crossfade.

Reusable across elements

scramble(el) reads the target text from a data-final attribute rather than hardcoding it, so the same function decodes both the headline and the subheading with their own independent ScrollTrigger instances and timings — you can call it on any element that has a data-final value.

Gated by scroll, replays on reverse

Each scramble has its own scrollTrigger: { start: 'top 80%', toggleActions: 'play none none reverse' }, so the decode begins only once the element is nearly in view, and scrolling back out reverses the tween — since the scramble re-randomizes on every update, reversing doesn't look like rewinding footage, it just looks like the text scrambling again, which reads correctly either direction.

Customizing it

Swap the character pool, change duration for a faster or slower decode, or resolve characters from the center outward instead of left to right by changing which index check gates revealCount. Pair it with text scramble or scramble text links for hover-triggered variants, or a scroll reveal grid for the surrounding layout.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain why tweening a plain numeric progress object rather than the text itself is the technique that makes a scramble animation possible with GSAP, and how the revealCount cutoff produces the left-to-right decode instead of a flat all-at-once resolve. It can also help extend the effect — ask for a center-out or random-order reveal pattern, a version that scrambles on hover in addition to on scroll, or a variant using a themed character set (binary, glitch symbols, a different alphabet) for a different visual tone. Use the conversation to make sure you understand the onUpdate-driven approach before reusing it elsewhere.

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 scramble" headline effect in plain HTML, CSS, and JavaScript using GSAP with its ScrollTrigger plugin (load both from a CDN) — do not use any paid/club GSAP text plugin.

Requirements:
- A headline element (and at least one secondary text element) whose final text is stored in a data attribute, so the scramble function is generic and reusable across elements rather than hardcoding the target string.
- Implement the scramble by tweening a plain JavaScript object's numeric progress property from 0 to 1 with GSAP (not by tweening the DOM text directly), and use that tween's onUpdate callback to rebuild the element's rendered text on every frame.
- On each onUpdate tick, compute how many characters (counting from the left) are considered "resolved" based on the current progress value; resolved characters must show their real final character, while every character after the resolve cutoff must be replaced with a freshly random character from a defined character pool on every single frame, so unresolved characters visibly flicker rather than sitting static.
- Preserve literal spaces in the final string as spaces rather than scrambling them.
- Gate the scramble with a ScrollTrigger on the element so it only starts once the element scrolls to roughly 80% down the viewport, and set toggleActions so scrolling back out of view reverses the tween (letting it re-scramble) rather than leaving the text permanently resolved.
- On tween completion, force the element's text to exactly the final string to avoid any last-frame randomness lingering.

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.

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 JSA headline and subheading with data-final text render.
  3. 3
    Scroll toward the stageText starts as random characters near the trigger point.
  4. 4
    Keep scrollingCharacters resolve left to right into the real words.
  5. 5
    Scroll back upThe scramble reverses instead of staying resolved.
  6. 6
    Change data-finalAny element with that attribute can be scrambled.

Real-world uses

Common Use Cases

Hero headlines
A scroll-gated sibling to text scramble.
Section dividers
Decode a label before a scroll reveal grid.
Tech/product launches
Reinforce a "decoding" or reveal narrative.
Stat callouts
Link hovers
Reuse the scramble logic like scramble text links.
Editorial storytelling
Reveal chapter titles as the reader scrolls.

Got questions?

Frequently Asked Questions

It doesn't tween the text directly — it tweens a plain object's progress property from 0 to 1, which is a normal numeric tween GSAP handles natively. The onUpdate callback reads that progress value on every tick and rebuilds the element's textContent from it, so the visible "animation" is really a side effect of a simple number tween.

Because on every single frame, every character to the right of the current reveal cutoff is replaced with a fresh random pick from the character pool, not just displayed once. That continuous re-randomization of the unresolved tail is what creates the flicker, and only the resolved left portion stays stable — a typewriter effect just adds characters, it doesn't scramble them first.

No. It only uses GSAP core and the free ScrollTrigger plugin. The scramble behavior itself is plain JavaScript inside an onUpdate callback, not a specialized text plugin, so there's no licensing consideration beyond standard GSAP core and ScrollTrigger, which are free.

Yes — that's why the target text comes from a data-final attribute rather than being hardcoded in the function. Call scramble(el) on any element that has data-final set, and each call creates its own independent tween and ScrollTrigger, so elements can decode at different scroll positions with different timings.

Keep the scramble function outside your component (or in a utility module), and call it on the DOM node via a ref inside a mount effect once the element exists. Register ScrollTrigger once at the app level, and kill the created tween/ScrollTrigger in the cleanup function to avoid duplicates on re-render.