Text✨ Premium

Scatter to Word

Characters spring from random positions and snap together into a word with elastic bounce — on load or on click.

JSAnimationTransform

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

Full-screen hero — Brand manifesto word — Scatter to Word example 1

① Full-screen hero — Brand manifesto word

WhenOn page load of a dark landing page, a single powerful word assembles to establish brand identity within the first second.
WhyThe scatter creates an immediate focal point on a flat background without cluttering the composition — a letter at 5 rem spans the full width of a mobile screen, and the entering animation replaces a carousel or background video.
Settingsfont-size: 5 rem; letter-spacing: .08 em; triggered 300 ms after DOMContentLoaded; gradient color via background: linear-gradient(...); background-clip: text; -webkit-text-fill-color: transparent; on .kt-text.
Section title — Creative portfolio cover — Scatter to Word example 2

② Section title — Creative portfolio cover

WhenOn chapter click or scroll in a one-page layout, a title word assembles to mark the visual transition between two sections.
WhyA decorative word assembling showcases custom typography without complex JS. Triggered on user action (click or IntersectionObserver) rather than on load so it doesn't distract visitors scrolling quickly.
Settingsfont-size: 3.8 rem; custom serif font (CSS inheritance on .kt-text); color #e2c97a (gold); JS scatter radius reduced to ±150 px for a more compact animation suited to section width.
Quiz reward — Answer word revealed on correct response — Scatter to Word example 3

③ Quiz reward — Answer word revealed on correct response

WhenWhen the player submits the correct answer in a quiz or word game, the answer word assembles as a visual reward synced with the validation.
WhyAssembly triggered by a business event (correct answer) creates a satisfying micro-reward. The effect is lightweight (no canvas) and fits inside a modal or card — the word changes dynamically via <code>data-text</code> on each question.
SettingsDynamic word: update data-text via JS before triggering classList.add('active'); transition cut to 0.6 s for more snap; color #22c55e (success green) via .kt-text { color: #22c55e }.

How it works

The utility function splitChars(t) reads the data-text attribute of the .kt-scatter container, clears its innerHTML, and re-injects one <span class="char"> per character. Each span is then given a random inline CSS transform — translate(Xpx, Ypx) rotate(Zdeg) — within roughly ±200 px horizontally and ±130 px vertically, plus opacity: 0: the letters are scattered away from their natural positions before anything is shown.

Assembly is driven entirely by CSS. The rule .kt-scatter.active .char forces opacity: 1 !important and transform: translate(0, 0) rotate(0deg) !important. The transition: .8s cubic-bezier(.34, 1.56, .64, 1) on each .char drives the movement — the second parameter of the cubic-bezier (1.56, above 1) produces a slight overshoot before settling: that's the elastic bounce, with no @keyframes involved.

Re-triggering on click removes the .active class, assigns fresh random positions to each span, then re-adds .active after ~80 ms. That minimal delay lets the browser apply the new starting transforms before launching the new transition. Without it, the transform property wouldn't have changed state and no new transition would fire.

The CSS bundle covers several text-animation variants sharing the same class engine: .kt-wave (sinusoidal float via @keyframes), .kt-elastic (appear by scaleY), .kt-liquid (per-letter hover deformation), .kt-whirlpool, .kt-explosion. Only .kt-scatter is documented here. The whole bundle is self-contained: no canvas, no external dependency.

Accessibility

  • prefers-reduced-motion: absent from the code — the 0.8 s transition fires regardless of the system setting. Recommended fix: @media (prefers-reduced-motion: reduce) { .kt-scatter .char { transition: none; opacity: 1 !important; transform: none !important; } }
  • Each <span class="char"> contains real text — screen readers will spell the word letter by letter. For natural word-level reading, add aria-label="[WORD]" on the container and aria-hidden="true" on each .char.
  • Text contrast: white (#fff) on dark background (#0a0a0f) — ratio ≥ 10.5:1, well above the WCAG AAA threshold (7:1).
  • Keyboard interaction: the click re-trigger has no keyboard equivalent in the code. For accessibility, add tabindex="0" on the container and a keydown handler for Enter / Space.
  • user-select: none on .kt-text is appropriate for purely decorative text — remove it if the displayed word conveys information the user may need to copy.

Browser compatibility

Requires CSS Transitions and 2D transform — no canvas, no external dependency. cubic-bezier values above 1 (spring easing) are supported in all modern browsers since 2020.

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

If JS does not run, the <code>&lt;span class="char"&gt;</code> elements stay at <code>opacity: 0</code> (invisible). Provide a <code>&lt;noscript&gt;</code> fallback or display the word via <code>content: attr(data-text)</code> on a pseudo-element of the container.

The code

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

index.html — structure
<div class="kt-text kt-scatter" id="scatter1" data-text="YOUR WORD">
  <!-- <span class="char"> elements are injected by splitChars() on init -->
  <!-- A single data-text attribute is all that's needed -->
</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 SCATTER The word to display — set this HTML attribute on the .kt-scatter container to change the word without touching JS. Can be updated dynamically before removing then re-adding the .active class.
.kt-text font-size 2.2rem Letter size. Go up to 4–6 rem for a full-screen hero, down to 1.4 rem for inline body use.
.kt-text color #fff Character color. Accepts any CSS value — for a gradient, apply background: linear-gradient(...); background-clip: text; -webkit-text-fill-color: transparent; on .kt-text.
.kt-scatter .char transition-duration 0.8s Assembly animation duration. 0.5 s = snappy, 1.2 s = cinematic.
cubic-bezier(.34, 1.56, .64, 1) (spring elastic) Animation curve. Increase the 2nd parameter (1.56) for more bounce; swap for ease-out for a straight deceleration without overshoot; cubic-bezier(.22, 1, .36, 1) for a softer spring.
.kt-text letter-spacing .02em Letter spacing in the assembled state. Go up to .08–.15 em for wide display style.
.kt-text font-weight 800 Font weight. 700 for normal bold, 900 for maximum impact if the font supports it.
Scatter radius (JS) ±200 px / ±130 px Starting random positions are computed over ±200 px horizontal and ±130 px vertical in the init script — reduce to ±100 px for a compact scatter, increase to ±300 px for a dramatic off-screen explosion.

FAQ

The JSON file exposes splitChars(t) as a splitting utility. The full initialisation — random position assignment, firing .active on load and on click — is wired in the <script data-scenes> bundled with the examples. Copy that logic into your own script: it's about twenty self-contained lines with no dependencies.
Yes — call splitChars on each .kt-scatter container with a distinct id, assign random positions to each set of .char spans, then trigger .active independently. CSS transitions are scoped to the container: no interference between instances.
Update data-text, remove .active, call splitChars(el) to regenerate the spans, assign random positions to each new .char, then re-add .active after ~80 ms. The word changes on each trigger without a page reload — useful for a quiz that reveals a different word on each question.