132

3D Physics Background

£7 or the whole Vault, £79.20

A full-bleed pile of satin ceramic 3D pieces with real mass. The pointer is an invisible force that parts the pile under a warm pool of light, pieces collide and tumble, and a click scatters the pile before it pulls itself back together.

advanced
4 more details
webgl-shaderpointer-effectsphysicsclick-toggle

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

Updated

About this effect

A hero background made of real objects. A dense pile of satin-glazed ceramic pieces in orange, sand, grey and rust fills the frame and runs off every edge. The pointer is an invisible force: wherever it goes the pile parts around it, the pieces it meets push their neighbours, roll and tumble, and a warm pool of light on the pile shows exactly where it is. Leave the pile and the pool fades as the pieces close the gap. A click scatters the pile outward from the pointer, and it pulls itself back together at new orientations, so it is never quite the same twice.

Read the full effect overview

The pieces are satin physical materials under an authored studio environment, with a soft key-light shadow and per-piece contact shading, so the pile reads as a lit object rather than a field of sprites. The physics is a small solver written for this effect, with no physics library, and the loop stops completely when the pile is at rest.

3D Physics Background - GSAP animation effect preview

What's included

10 items
  • Dense pile of lit 3D pieces that fills and overflows the frame at any size, with piece size held steady in screen pixels
  • Pieces with real mass: each collides with its neighbours, so a push travels through the pile
  • An invisible pointer force that parts the pile, marked by a warm pool of light that fades in over the pile and out when the pointer leaves
  • Click or tap to scatter the pile from the pointer; it regroups at new orientations
  • Satin ceramic materials under an authored studio environment, soft shadows and contact shading; no HDR download
  • Lead colour from one data attribute, three supporting tones from another, ground sweep from three CSS custom properties
  • Load drop-in, physical settle, and a loop that sleeps at rest and wakes on input
  • Touch: drag through the pile, tap to scatter, with an optional slow pass between touches
  • Still image fallback for reduced motion, no WebGL and no JavaScript
  • scatter(), pointAt(), release(), pause(), resume() and destroy() per instance, plus a revert-safe GSAP context

Perfect for

4 use cases
  • A studio or agency homepage hero that should feel physical and tactile
  • A product launch background behind a short headline, with the accent set to the brand colour
  • A campaign or event landing page that invites the visitor to play before scrolling
  • A portfolio intro where the background itself is the first piece of work

How it works

2 sections

Each piece is a rigid body made of a few overlapping spheres. Every frame the solver pulls each piece gently toward the centre of the frame, finds the pieces touching it and pushes them apart with spring contacts and friction, so forces pass from piece to piece. The pointer is an invisible sphere of force that follows the cursor through the middle of the pile and pushes whatever it meets; a spot of warm light follows it, and the rest of the room dims slightly while it is on. When the pointer stops, damping brings the pile to rest within about a second and a half and the render loop stops until the next input.

three.js draws the pieces as four instanced meshes (one per tone) with satin clearcoat materials, a studio environment and a shadow-casting key light. GSAP manages the motion-preference branches and teardown, plays the drop-in on load and shapes the click scatter.

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

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. 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 3D Physics Background 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. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£7 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £79.20, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

The inline script is not optional. The still image of the pile is the fallback for no JavaScript, no WebGL and reduced motion, and without this it paints for the half second it takes three.js to arrive, then gets swapped for the canvas. The probe runs before first paint, stamps html.gl when a canvas is coming, and the stylesheet hides the still under that class. No JavaScript, no class, the still shows; the effect removes the class again if it cannot build a renderer after all.

2. Add to your <body>, inside the element you want the pile to fill:

Code snippet omitted: it ships with the download.

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

Code snippet omitted: it ships with the download.

The script finds every [data-physics-background], drops a canvas into it and hides the still once the canvas is actually there.

Why the import map rather than a plain <script src> for three.js: three ships as ES modules only, and its old UMD build logs a deprecation warning on every page load. The shim above imports it as a module (dynamically, so a blocked CDN falls back to your still instead of a blank stage), puts it on window, and then loads assets/script.js as an ordinary script, so the effect itself stays a plain file you can drop into any build (or none).

Already using three.js as a module? Then skip the shim, and just make sure window.THREE is set before assets/script.js runs:

Code snippet omitted: it ships with the download.


Using It With Your Own Design

What the effect needs from your markup: one element with data-physics-background that has a real size, and inside it the [data-physics-background-source] figure with the still. The script reads nothing else. Put your headline and buttons in a sibling layer above it (position: relative; z-index: 1 on your content and pointer-events: none on the parts that should let the pointer reach the pile).

What is only the demo's styling: the .showcase-* rules (the full-screen shell and the cue pill in the corner), the Mona Sans font and the page colours. Delete them freely.

Colours: the lead hue is data-accent (about 35% of the pile) and the three supporting tones are data-tones, three colours separated by spaces (about 15%, 25% and 25%). Any CSS colour three.js can parse works: hex, rgb(), hsl() or a colour name. Keep the four in one tonal family with one saturated lead; the satin material and the warm pool are tuned for that. The ground behind the pile is a vertical sweep read once, at start, from three custom properties on the root:

Code snippet omitted: it ships with the download.

The sweep is drawn inside the scene, so the pieces and the ground share one light.

Non-obvious things the effect depends on:

  • The container must have a real size. The canvas is sized from the container's clientWidth/clientHeight, so .physics-background needs a height from somewhere: it is position: absolute; inset: 0 in the demo. Give it a height of 0 and you get no canvas and the still stays.
  • The .physics-background__source still must stay in the document. It is the fallback for no JavaScript, no WebGL and reduced motion, and it is only hidden by .physics-background.is-live, a class the script adds after a canvas exists.
  • touch-action: pan-y on .physics-background. On touch this keeps vertical page scrolling working from on top of the pile; a horizontal drag moves the force through it.
  • Pieces keep their size on screen. A larger container gets more pieces, not bigger ones, so the pile reads as a pile from a phone to a wide monitor. Use data-scale to make every piece bigger or smaller.

Replacing the still: if you change the colours, capture your own still so the fallback matches. Open the page, let the pile settle, and screenshot the container; any 16:9 image of about 2400 by 1350 works. It is shown with object-fit: cover, so portrait screens crop its centre.


Options

Attribute Values Default Description
data-accent CSS colour #e8582a The lead tone (about 35% of the pieces)
data-tones Three CSS colours, space separated #9c3f24 #ead3ae #9b9ea2 The three supporting tones: rust (15%), sand (25%) and grey (25%) in the demo
data-count Number from the container size Number of pieces. Leave unset to fill the container; capped at 130 on desktop and 56 on touch devices
data-scale 0.4 to 2.5 1 Piece size on screen. Larger pieces means fewer of them when data-count is unset
data-gravity Number 5.5 How hard the pile pulls together. Lower is looser and slower to regroup
data-force 1, 0 1 The pointer force. With 0 the pointer moves nothing and the pile only reacts to clicks
data-force-size 0.3 to 3 1 Size of the gap the force opens
data-pool 0 to 2 1 Strength of the pool of light that marks the force. 0 switches it off
data-point "x y" from -1 to 1 none Open with the force already in the pile at this point and the pool lit, until the pointer takes over (0 0 is the centre, 1 1 the top right)
data-scatter 1, 0 1 Click or tap to scatter
data-intro 1, 0 1 The drop-in on load. With 0 the pile is in place on the first frame
data-drift Seconds, 0 7 on touch, 0 with a mouse On touch devices the force makes one slow pass through the pile this often between touches. 0 switches it off

Every autonomous behaviour has an off switch: data-intro="0" and data-drift="0" give a page that does nothing until the visitor moves.

Accessibility

  • Reduced motion: the scene never starts. The still of the settled pile stays exactly as it is, a full-bleed image with its alt text.
  • Without JavaScript, or without WebGL: the same still, for the same reason. One fallback, three failure modes, and it is never hidden until a canvas has actually been created.
  • The canvas is aria-hidden: the pile is decoration, and the still's alt text describes it once. Nothing on the page depends on playing with it.
  • Keyboard: there is nothing to operate; the pile is a pointer toy. If you want a keyboard trigger, call scatter() from your own button.
  • Touch: drag horizontally through the pile to move the force, tap to scatter. Vertical swipes scroll the page.

Dependencies

Dependency Version Required
three.js 0.180.0 Yes, as an ES module (see Quick Start)
GSAP core 3.15.0 (3.12+ works) Yes: matchMedia branching, teardown, the drop-in and the scatter envelope

No GSAP plugins. GSAP plays two values, the drop-in (the pile's centre of gravity falling into the frame) and the strength of the click scatter over a third of a second; the render loop reads them. Every position, rotation and velocity, the force's pointer chase and the pool's fade belong to the physics loop.

Everything the effect uses (WebGLRenderer, MeshPhysicalMaterial with clearcoat, InstancedMesh, ExtrudeGeometry, PMREMGenerator, DirectionalLight shadows, SpotLight) is long-stable three.js API, so pinning to a different version is a one-line change in the import map.

Browser Support

Anything with WebGL, which is every current browser. Support is probed before three.js is asked for a renderer, deliberately: three logs its own failure to the console, as errors, several times over, before it throws, so catching the exception would hide nothing. Probed first, a visitor with WebGL disabled simply gets the still and a clean console.

Import maps are supported everywhere current. In a browser old enough to lack them the module never runs, window.THREE is never set, and the still is again what shows.

Performance

Four draw calls for the pile (one instanced mesh per tone), plus one shadow pass. The pool is an unshadowed spot light, so it costs a little shading and no extra pass. The physics costs about 1ms a frame for 120 pieces on a laptop core while the pile is moving, and nothing at all once it rests: the loop stops and only input starts it again. It is also gated on an IntersectionObserver, so nothing runs while the effect is off screen. Pixel ratio is capped at 2 (1.5 on touch devices).

To buy back frames on low-end hardware, raise data-scale first: bigger pieces means fewer of them, which cuts the physics and the shadow pass together.

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