Text✨ Premium

Text Shuffle Reveal

Each letter races through random substitutions before locking onto the correct value — a character-by-character decoder effect, 100% vanilla.

JSDecodeReveal

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →

3 usage examples

Splash screen — Product name decoded on launch — Text Shuffle Reveal example 1

① Splash screen — Product name decoded on launch

WhenThe app's launch screen appears: the product name or main tagline reveals character by character as soon as the page loads.
WhyAgainst a full-width dark background, the substitution cascade grabs attention before the text is even readable — ideal for locking in brand identity within the first few seconds.
Settingsfont-size 5–6 rem, uppercase text, CYCLE_SPEED 40 ms (fast), STAGGER 50 ms, CYCLES_PER_CHAR 10.
Digital magazine cover — Article title revealed on scroll — Text Shuffle Reveal example 2

② Digital magazine cover — Article title revealed on scroll

WhenThe reader scrolls to a long article's header: the main title decodes as the section enters the viewport via <code>IntersectionObserver</code>.
WhyThe effect creates a staging moment that extends attention before reading begins. The substitutions evoke typographic composition, grounding the effect in print culture.
SettingsCHARS restricted to uppercase letters for an editorial tone, CYCLE_SPEED 60 ms, STAGGER 80 ms for a more measured pace.
Reward screen — Access code deciphered in real time — Text Shuffle Reveal example 3

③ Reward screen — Access code deciphered in real time

WhenA level is completed or an achievement unlocked: the secret code, trophy name, or final score displays in decode mode just before transitioning to the next screen.
WhyRapid substitutions amplify the tension of the reveal — each locking letter reinforces the sense of discovery. The hacker aesthetic is immediately legible to players.
SettingsCHARS including digits and symbols (#@!0-9A-Z), CYCLE_SPEED 35 ms, CYCLES_PER_CHAR 12, monospace font.

How it works

The .kt-text.kt-shuffle container holds a data-text attribute defining the target string. The splitChars() function reads that value, clears the element, and rebuilds one <span class="char" data-char="X">?</span> per character: the target is archived in data-char, and the initial textContent is a random character.

On each tick of a setInterval (≈ 50 ms), every span receives a new character drawn from the CHARS pool (A–Z + a–z, digits, symbols). A per-position counter limits substitutions to CYCLES_PER_CHAR iterations; once reached, the interval is cleared and the span displays its data-char value at full white opacity.

A staggered start delay (STAGGER, ≈ 60 ms per position) offsets each character's animation via setTimeout — the first letter locks before the last one has even started cycling, producing the left-to-right decoding cascade the effect is known for.

A click listener on the container re-invokes shuffleReveal() — every span reverts to a random half-opacity character and the cycle restarts from zero. splitChars() rebuilds the span structure if textContent was modified between calls.

Accessibility

  • No @media (prefers-reduced-motion: reduce) block in the provided code — users who have enabled this OS preference see the cycling animation run normally. Fix: test matchMedia('(prefers-reduced-motion: reduce)').matches at start-up and, if true, show the data-char values directly without launching any intervals.
  • In-transit .char spans carry no aria-hidden — a screen reader may announce the intermediate random letters. Fix: add aria-hidden="true" to the animated container and provide a separate <span class="sr-only"> element holding the target text.
  • No role or aria-label attribute on the container by default — add aria-label="target text" and aria-live="off" if the container is the sole carrier of meaningful textual content.
  • The click trigger is not keyboard-accessible (no tabindex, no keydown listener) — add tabindex="0" and listen for Enter / Space to serve keyboard users.
  • In-transit characters are rendered in rgba(255,255,255,0.5) on a #0a0a0f background (ratio ≈ 3.5:1, below the WCAG AA threshold of 4.5:1). Acceptable for a short animation on a dark ground; reinforce if the cycle duration is long or the background lighter.

Browser compatibility

Requires DOM Level 2, setInterval, and addEventListener — available in all modern browsers since 2012.

Chrome 80+✓ Full
Firefox 75+✓ Full
Safari 13+✓ Full
Edge 80+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

With JS disabled, <code>.char</code> spans display their pre-generated random characters — the content is not readable. Provide a <code>&lt;noscript&gt;</code> fallback or an <code>aria-label</code> on the container to keep the text accessible.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="kt-text kt-shuffle" id="shuffle1" data-text="YOUR TEXT" style="cursor: pointer;">
  <!-- splitChars() dynamically generates the spans:
  <span class="char" data-char="Y">?</span>
  <span class="char" data-char="O">?</span>
  … one span per character in data-text -->
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
data-text "SHUFFLE" Text to reveal. Updatable on the fly: set the attribute, call splitChars(el), then shuffleReveal(el).
CHARS (JS const) A-Za-z0-9 + symbols Random character pool. Narrow to 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' for a clean typographic look; add digits and symbols for the hacker aesthetic.
CYCLE_SPEED (JS const) 50 ms Interval in ms between substitutions per position. 35 ms = very fast (hacker), 80–100 ms = slow and editorial.
CYCLES_PER_CHAR (JS const) 8 Number of random substitutions before locking. Raise (12–16) to extend tension; lower (4–5) for a near-instant reveal.
STAGGER (JS const) 60 ms Delay in ms between each position's start. 0 = all letters cycle simultaneously; 120 ms = very progressive cascade.
.kt-text { font-size } 2.2rem Text size — line-height and letter-spacing inherit automatically.
transitional color (inline) rgba(255,255,255,0.5) Color of in-transit random chars. The locked value snaps to #fff (full opacity) at the end of the cycle.

FAQ

Update the container's data-text attribute, call splitChars(el) to rebuild the spans, then restart the animation: el.setAttribute('data-text','NEW'); splitChars(el); shuffleReveal(el);
Replace or supplement the click listener with an IntersectionObserver on the container. In the callback, call shuffleReveal(el) when entry.isIntersecting is true, then observer.disconnect() if you only want a single trigger.
Yes — the code is 100% vanilla DOM. In React, call shuffleReveal inside a useEffect after mount; in Vue, inside the mounted hook; in Svelte, inside onMount. The animation only touches textContent and style.color of the spans — no conflicts with framework rendering.