Falling Text with Gravity
As each line of a heading scrolls to the top of the viewport it shatters into individual characters that fall downward with randomised velocity, spin, and gravity — as if hitting a roof and crumbling off screen.
Setup — External Scripts
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script><script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/Physics2DPlugin.min.js"></script><script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrollTrigger.min.js"></script><script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/SplitText.min.js"></script>Code
<div class="drop-wrapper">
<div class="drop-section">
<h1 data-drop-text="" class="drop-heading">This is just a<span data-drop-img="" class="drop-heading-img is--first"><img src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecab_shape-squigle-1.png"></span>random quote<span data-drop-img="" class="drop-heading-img is--second"><img src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecad_shape-squigle-2.png"></span>we used</h1>
</div>
<div class="drop-section">
<h1 data-drop-text="" class="drop-heading">See how our window acts like<span data-drop-img="" class="drop-heading-img is--third"><img src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecaf_shape-squigle-3.png"></span> a roof?</h1>
</div>
<div class="drop-section">
<h1 data-drop-text="" class="drop-heading">So much fun!</h1>
</div>
</div>.drop-wrapper {
width: 100%;
min-height: 350vh;
}
.drop-section {
justify-content: center;
align-items: center;
width: 100%;
min-height: 100vh;
display: flex;
position: relative;
}
.drop-heading {
text-align: center;
max-width: 40rem;
margin-top: 0;
margin-bottom: 0;
font-size: 4rem;
font-weight: 500;
line-height: 1;
}
.drop-heading-img {
z-index: 2;
width: 1.2em;
display: inline-block;
position: relative;
}
.drop-heading-img img {
width: 100%;
max-width: 100%;
}
.drop-heading-img.is--first {
transform: rotate(-20deg)translate(.15em, -.2em);
}
.drop-heading-img.is--second {
transform: translate(-.1em, 0.2em)rotate(10deg);
}
.drop-heading-img.is--third {
margin-left: -.1em;
margin-right: -.1em;
margin-top: -.2em;
transform: translate(-.05em, .1em)rotate(50deg);
}
[data-drop-text] .line {
display: block;
}gsap.registerPlugin(ScrollTrigger, SplitText, Physics2DPlugin);
function initFallingTextGravity() {
new SplitText("[data-drop-text]", {
type: "lines, chars",
autoSplit: true,
linesClass: "line",
onSplit(self) {
let ctx = gsap.context(() => {
self.lines.forEach(line => {
gsap.timeline({
scrollTrigger: {
once: true,
trigger: line,
start: "top top-=10"
}
})
.to(line.children, {
duration: "random(1.5, 3)",
physics2D: {
velocity: "random(500, 1000)",
angle: 90,
gravity: 3000
},
rotation: "random(-90, 90)",
ease: "none"
})
.to(line.children, {
autoAlpha: 0,
duration: 0.2
}, "-=.2");
});
});
return ctx;
}
});
}
document.addEventListener("DOMContentLoaded", () => {
initFallingTextGravity();
});Notes
- •`autoSplit: true` makes SplitText automatically revert and re-split on resize so lines re-wrap correctly; the `onSplit` callback returns a GSAP context so old animations are cleanly killed before new ones are created.
- •Each line gets its own ScrollTrigger with `once: true`, so the fall animation fires exactly once per line as it exits the top of the viewport.
- •Adjust `start: "top top-=10"` to tune the moment characters begin falling — increase the offset to trigger earlier, decrease it for later.
- •The second tween (`autoAlpha: 0`) fades characters out at the very end of the fall so they disappear cleanly rather than landing off-screen.
Guide
How it works
SplitText splits each `[data-drop-text]` element into lines and then characters. Each line becomes a ScrollTrigger — when it hits the top of the viewport (`start: "top top-=10"`), its characters launch downward with Physics2D.
Physics2D values
`velocity` — initial speed in px/s (randomised 500–1000 for natural variation). `angle` — 90° means straight down; try `random(80, 100)` for a slight spread. `gravity` — downward acceleration in px/s²; higher = faster fall.
Inline images
Decorative `<span data-drop-img>` elements placed inside the heading fall alongside the text characters because SplitText includes all child elements in `line.children`.