038

Physics Word Pile

£5

Display-type chips that fall, collide, tumble and stack under a real rigid body solver, with drag-and-fling throwing. No physics library, just GSAP.

advanced
6 more details
physicscollisiondraggablemomentumpointer-effectsmicro-interaction
Physics Word Pile - GSAP animation effect preview

This demo reads better at your own screen size than in the frame below:

About this effect

A gravity playground for typography. Words drop into the frame as physical objects, knock each other sideways, tumble and settle into a pile that never lands the same way twice.

Read the full effect overview

Grab any word and it dangles from the exact point you caught it. Fling it and it leaves at the speed of your pointer, squashes against whatever it hits, flashes, and sends a shockwave out from the contact point.

The whole simulation is hand-written on top of GSAP's ticker: no physics library, no WebGL, no canvas. The words stay real HTML you can select, translate and style.

What's included

8 items
  • Impulse-based rigid body solver with restitution, friction and rotational inertia, written from scratch with no physics library
  • Drag any word with a mouse-joint grab that pins to the point you caught it, so chips dangle and swing naturally
  • Throw velocity is inherited from the pointer, so a hard flick genuinely fires a word across the stage
  • Impact feedback on three channels: squash-and-stretch along the chip, a colour flash, and a shockwave ring scaled by collision impulse
  • Fixed 120Hz timestep with an accumulator, so stacking stays stable at any frame rate and on any machine
  • Collision shapes are built from each chip's rendered size, so the physics tracks fluid type as it reflows
  • Bounds come from the container's padding box, so the walls and floor are positioned entirely in CSS
  • Full keyboard support: every word is a button that can be focused and kicked with Enter or Space

Perfect for

5 use cases
  • Hero sections that want a headline visitors can play with rather than read past
  • 404, coming-soon and maintenance pages where a toy earns the wait
  • Agency and portfolio landing pages needing one memorable interaction above the fold
  • Product launches and campaign microsites built around a short, throwable tagline
  • Event and conference sites where speaker names or session titles become the objects

How it works

3 sections

Every chip is approximated as a chain of circles laid along its long axis, which is exactly a capsule and matches a pill-shaped element's real silhouette. That turns collision detection into circle-versus-circle and circle-versus-wall tests, and skips the polygon clipping an SAT solver would need.

Each fixed 1/120s step integrates gravity, gathers contacts, and then runs several passes of sequential impulses over them. Each pass resolves normal restitution and clamped Coulomb friction at the contact point, with torque taken from the offset between the contact and the body's centre; a positional pass then pushes remaining overlap apart without feeding energy back into the stack.

Grabbing applies an impulse at the grab point rather than the centre, which is what makes a held word swing. Rendering is three GSAP quickSetters per body, and the impact squash is tweened on an inner element so the per-frame transform never clobbers it.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, 9 September 2026. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Physics Word Pile opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

No GSAP plugins are needed.

3. Add the markup to your <body>:

Code snippet omitted: it ships with the download.

4. Optional shake button, anywhere on the page:

Code snippet omitted: it ships with the download.

Using It With Your Own Design

What the effect actually requires. Four things, and nothing else:

  1. A container carrying data-word-physics. It must be position: relative (or absolute/fixed) and should be overflow: hidden or clip, since a hard throw briefly sends words above the top of the box.
  2. One element per object carrying data-chip, as a direct or nested child.
  3. [data-chip-body] on a wrapper inside each chip. This is where the impact squash is tweened. Without it the squash is applied to the chip itself and is overwritten every frame by the simulation, so it silently never appears.
  4. This positioning rule, which is the one piece of CSS the effect cannot supply for itself:

Code snippet omitted: it ships with the download.

data-state="live" is added by the script once it starts. Gating on it is deliberate: with JavaScript off, or under prefers-reduced-motion, the chips are never taken out of normal flow, so whatever your CSS lays them out as is what those visitors get. Make that layout a presentable one — a centred wrapped row works well.

Pad the chip, not just its face. The demo's .wp-chip carries a transparent band of padding around the glass. That band is the collision box and the touch target, so words come to rest a few pixels apart instead of flush against each other, and each word's target is larger than the shape it draws. Both matter: a pile of words is a pile of overlapping buttons, and a target has to keep a 24x24px area that nothing else covers (WCAG 2.5.8). With the box drawn tight to the glass, a small word with another word resting on it has nothing left. Visually the band costs nothing — the gaps it leaves between stacked words are what a pile of solid objects would have anyway.

The bounds are your container's padding box. The floor is padding-bottom, the walls are padding-left / padding-right. Put a visible floor bar inside the bottom padding and words land exactly on it. Change the padding at a breakpoint and the collision box follows, with no JavaScript to keep in sync. The ceiling is deliberately far above the box so a shake throws words out of view and lets them rain back in.

What is only the demo's styling and can be deleted or replaced wholesale: the aurora ground, .wp-chip__body's glass treatment (fill, backdrop blur, streak highlight, shadows), the size modifiers (.wp-chip--xl and friends), the tone attributes (data-tone), the frosted floor shelf, the cue line and the shake button. The script never reads any of them.

Shape matters to the physics. Each chip is modelled as a chain of circles along its long axis — a capsule — sized from the element's rendered box. That is exact for a pill (border-radius: 999px) and a close fit for a squat rectangle like the demo's blocks (sharp corners overhang the capsule by a few pixels at most, which disappears in a moving pile). Square-ish chips collapse to a single circle and roll like balls, and their spin is applied to [data-chip-label] rather than to the chip itself: a circle has no visible orientation, but a rotated element's box is the box around it, so a 32px bead turned 45 degrees measures 45px across and covers whatever is beside it — for a pointer and for a target-size audit alike. The bead looks identical either way; only the box changes. A tall narrow chip is modelled on its width, so keep chips wider than they are tall unless you want that.

Size the type against height, not just width. This is the one sizing mistake worth warning about. font-size: 5vw looks right in a full browser window and then jams the moment the container is short and wide — dropped into a 16:9 frame 940px across, the box is only 529px tall, and a pile sized for width fills it to the ceiling with no room to throw anything. Cap on both:

Code snippet omitted: it ships with the download.

Use container query units instead if the container is not the viewport.

Keep every word narrower than the gap between the walls. A word too wide to fit touches both walls at once and has nowhere to rest. The effect pins such a word to the centre rather than letting the two constraints fight, so nothing breaks, but it will sit there inert. Check your longest word at your narrowest breakpoint.

Two elements worth restyling. .wp-chip__flash is a span inside every chip whose opacity the script tweens on impact; all of its paint is CSS (the demo uses a white light bloom, but a currentColor inset rim works just as well on flat chips). .wp-shock (the elements the script creates and pools) is scaled and faded by the script; give it any paint that reads as an impact — the demo uses a soft radial glow, a hard border ring suits flatter designs.

Options

Set on the [data-word-physics] container.

Attribute Values Default Description
data-gravity Any number (px/s²) 2600 Downward acceleration. Lower feels floaty and moon-like; higher feels dense
data-bounce 0 to 1 0.28 Restitution. Above about 0.5 the pile stops settling and keeps jostling
data-friction 0 to 1 0.42 Coulomb friction. Low values make words slide off each other rather than stack
data-settle Any number, 0 disables 9 Strength of the righting torque that turns a slowing word back toward horizontal
data-seed Any whole number 2 Which opening pile you get. The drop is seeded, so the same number always gives the same arrangement
data-shockwave true, false true Whether impacts emit expanding rings

About data-settle

Left purely to the physics, a long pill will happily balance on one end against a wall. It is defensible physically and useless for typography: the word ends up unreadable and often clipped out of the frame. data-settle adds a damped spring toward the nearest horizontal, faded in as a word slows down, so words rock flat and stay legible. A word still travelling is never touched by it, so throws are unaffected.

Set data-settle="0" for pure physics, or raise it above 9 if you want words to lie down faster. The demo runs at 18.

About the opening drop

The first pile is authored, not random. The words are packed into rows, tallest words at the bottom, and each row is released a short way above the shelf it is landing on, a beat after the row below has settled. Two things make that worth doing:

  • Rows of equal height settle flat. A row of mixed heights comes to rest as a surface with steps in it; the next row lands bridging a step, tips into the hollow, and from there the pile shears sideways and words finish standing on their ends. Grouping by height is also just how anyone would stack these by hand.
  • A short fall lands flat. A word released from the top of the frame arrives at around 2000px/s, and at that speed a word that clips the corner of the pile cartwheels and jams on its side, where the righting torque cannot free it without the whole pile rearranging.

The drop uses a seeded generator (data-seed), so the arrangement is the same every load. Throws and the shake button stay on Math.random, so everything after the first landing is as unrepeatable as it ever was. If you change the words, try a few seeds: the layout is stable per seed, so you can pick the one you like and keep it.

About the rest state

The simulation switches itself off once the pile has stopped, and back on the moment anything touches it — a grab, a throw, the shake button, a resize.

The test is how far each word has moved since the last check, not how fast it is going. A resting stack in a solver of this kind sits in a limit cycle: the speed hovers around whatever threshold you pick while the pile goes nowhere. A speed test never fires, and a stack of capsules is a stack of rollers, so the words creep for as long as the tab is open. There is also a hard backstop a few seconds in, because a word can keep inching down the pile long after it looks finished.

That is what makes the opening state repeatable. The simulation is deterministic step for step, so a pile that stops at a fixed step is the same pile every load; a pile that never stops is whatever step you happened to look at it on.

Markup Hooks

Attribute Where Required Description
data-word-physics Container Yes Marks the simulation container
data-chip Each object Yes An object in the simulation
data-chip-body Inside each chip Yes Element the impact squash is tweened on
data-chip-label Inside each chip No The word itself. A round chip spins this rather than its own box (see below)
data-chip-flash Inside each chip No Overlay faded in on impact
data-word-shake Any button No Punts every word skyward. Multiple buttons are fine
data-state="live" Container Added by the script once running. Gate your positioning CSS on it
data-grabbing Container Present while a word is held. Useful for a grabbing cursor

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce no simulation runs at all and no listeners are attached. The chips are left in normal document flow as a static, fully legible word group.

  • No JavaScript: identical to the above. Nothing is hidden by CSS that only JavaScript reveals.

  • Keyboard: every word is a real <button>. Tab to one and press Enter or Space to kick it, which is the keyboard equivalent of a flick.

  • Focus visible: focused words take a visible outline offset clear of the block edge.

  • Labels: give each chip an aria-label that contains its visible word, e.g. aria-label="Throw the word Gravity".

  • Target size: the chip button is padded out beyond the glass, so a word that has another word resting on it still keeps a clear area to grab. Keep that padding if you restyle: without it a small word in a settled pile has nothing left of it uncovered.

Words in a pile will overlap and some will land upside down. That is the nature of the effect, so do not use it for text a visitor has to be able to read — put the message in real copy nearby and use the pile as the toy.

Performance

  • One gsap.ticker callback drives everything; the ticker is removed on teardown.
  • A settled pile costs nothing: the simulation stops once nothing has moved and restarts on the next grab, throw, shake or resize.
  • Rendering is three gsap.quickSetter calls per word per frame.
  • Simulation runs on a fixed 1/120s step with an accumulator, capped at five steps per frame, so a slow frame cannot start a catch-up spiral.
  • Pairs are broad-phase rejected on centre distance before any circle test.
  • Shockwave rings are a fixed pool of ten reused elements, never allocated per impact.
  • Around a dozen words is comfortable. Past roughly thirty, or with very long words (a long word needs more collision circles), profile before shipping.

Dependencies

Required:

  • GSAP 3.12+ (core only — no plugins)

Optional:

  • None

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

Everything in the catalogue, plus everything we release next.