Gravity Drop Text
Letters break free from the top of the scene, accelerate under gravity and bounce off the baseline before settling — pure physics, zero library.
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



How it works
The splitChars() function reads the data-text attribute on the container and generates one <span class="char" data-char="…"> per letter. Each span is a display: inline-block — required for transform to apply independently per character.
A requestAnimationFrame loop animates each letter separately. Vertical velocity increases every frame by a gravity constant (g ≈ 0.60 px/frame). Position is applied via style.transform = 'translateY(' + y + 'px)' as an inline style — no CSS transition involved. Each letter starts at translateY(-220px), opacity: 0, and becomes visible on its first active frame.
On impact (y ≥ 0), velocity is reversed with damping (vy = −vy × 0.45) — the letter bounces upward then falls again with progressively shorter arcs until it comes to rest. The CSS keyframe @keyframes ktElastic (scaleY(0) scaleX(1.8) → scaleY(1) scaleX(1)) is available to add a squash & stretch effect on landing via the active class.
Letters start in a cascade with a ~90 ms stagger per letter — early letters have already landed before the last ones begin to fall. A click listener on the container resets all letters to the top position and restarts the RAF loop.
Accessibility
prefers-reduced-motion: absent from the code — no branch stops or reduces the animation. To implement: detectmatchMedia('(prefers-reduced-motion: reduce)').matchesand place letters directly attranslateY(0)without launching the RAF.- Text remains in the DOM as readable
<span>elements — screen readers access the content independently of the animation. user-select: noneon.kt-text— letters are not cursor-selectable; not blocking for screen-reader accessibility.- Contrast: white text (#fff) on a dark background (#0a0a0f) — ratio ≥ 7:1, WCAG AAA.
- The
.kt-gravitycontainer is clickable but has norole="button"ortabindex— the 'click to replay' feature is not keyboard-accessible; add both if needed.
Browser compatibility
Requires CSS Transforms and requestAnimationFrame — supported in all modern browsers. Zero external dependencies.
Without JS, letters stay invisible (<code>opacity: 0</code> set by <code>.kt-gravity .char</code>). Add a CSS fallback state or <code><noscript></code> if non-JS visitors are targeted.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="kt-text kt-gravity" id="gravity1" data-text="YOUR TEXT" style="cursor: pointer;">
<!-- splitChars() generates one <span> per letter -->
<span class="char" data-char="Y">Y</span>
<span class="char" data-char="O">O</span>
<span class="char" data-char="U">U</span>
<span class="char" data-char="R">R</span>
</div>
<span class="demo-hint">Click to replay</span>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| data-text | GRAVITY | Text to animate — change the value on the .kt-gravity element to swap the word without touching JS. |
| font-size on .kt-text | 2.2rem | Letter size. Go up to 3–5rem for a hero impact; down to 1.5rem for a subtle UI integration. |
| color on .kt-text | #fff | Letter color. Accepts any CSS value — a gradient via background-clip: text is possible on the parent. |
| font-weight on .kt-text | 800 | Weight. 900 + a condensed font maximises the mass-on-landing feel. |
| letter-spacing on .kt-text | 0.02em | Letter spacing. Set to 0 or negative for a tight word; 0.1–0.2em to open it up. |
| gravity constant g (script) | 0.60 | Gravitational acceleration in px/frame². Raise to 0.8–1.0 for brutal impact; lower to 0.3–0.4 for a lunar feel. |
| bounce damping (script) | 0.45 | Restitution coefficient (0 = no bounce, 1 = perfect bounce). 0.3 = one dry bounce, 0.6 = several visible bounces. |
| stagger (script) | 90 ms | Delay between each letter's start. 50 ms = fast cascade, 150 ms = each letter lands before the next begins. |
FAQ
.kt-gravity elements as needed, each with a unique id. Call the init logic once per element — the RAF loop runs independently for each instance.font-family to .kt-text. One precaution: load the font before starting the RAF (listen to document.fonts.ready) to avoid a font-flash during the first animation frames.0 in the script: the letter stops cleanly at the baseline without going back up. Trigger ktElastic (CSS squash & stretch) at that point to simulate an impact without kinetic rebound.