
The weightless objects
Physics, 3D, Hover
NewFree
The old wooden toy with the maze and the steel ball, all in CSS 3D: the pointer tilts the board on its two axes and the ball rolls with real physics (gravity on the tilted board, bounces off the walls, friction). The holes swallow it with a small whirl, the brass cup at the end sets paper confetti flying. On its own, the board tilts and steers the ball along the line.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The tilt maze</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<header class="shop">
<p class="shop__name" data-shop>Marlow & Pip Toyworks</p>
<p class="shop__line">The tilt maze, hand-cut in maple</p>
</header>
<!-- the toy: a box that stays still, the maze board that tilts inside it, the steel ball on the board -->
<div class="scene" data-scene>
<div class="fit" data-fit>
<div class="intro" data-intro>
<div class="table" data-table role="img" aria-label="A wooden tilt maze with a steel ball, holes along the way and a brass cup at the end">
<div class="box" data-box>
<div class="lift" data-lift>
<div class="board" data-board>
<div class="board__top" data-top>
<svg class="art" data-art aria-hidden="true"></svg>
<div class="holes" data-holes></div>
<svg class="whirl" data-whirl viewBox="-30 -30 60 60" aria-hidden="true">
<path d="M0 -22 C14 -22 22 -12 20 0 C18 10 8 14 0 12"/>
<path d="M19 11 C12 23 -2 25 -12 17 C-19 10 -16 0 -9 -3"/>
<path d="M-19 11 C-25 -2 -18 -16 -6 -20 C3 -23 9 -15 7 -8"/>
</svg>
<i class="shadow" data-shadow></i>
</div>
<div class="ball" data-ball><i class="ball__shine"></i></div>
</div>
</div>
</div>
<!-- the knobs that turn the two axles (they are moved onto the front of the box by the script) -->
<span class="knob" data-knob><i class="knob__cap"></i></span>
<span class="knob knob--right" data-knob><i class="knob__cap"></i></span>
</div>
</div>
</div>
</div>
<p class="hint" data-hint aria-live="polite"></p>
<!-- what flies when the ball reaches the cup -->
<div class="confetti" data-confetti aria-hidden="true"></div>
<div class="win" data-win>
<p class="win__shop" data-shop>Marlow & Pip Toyworks</p>
<p class="win__title" data-win-title>Rolled home!</p>
<p class="win__line">Hand-cut maze boards. Steel balls included.</p>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
41 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The tilt maze” from Animorio and fit it into my siteEach wall is a block with five faces (the top and the four sides), made of divs turned 90 degrees round one edge and with backface-visibility hidden: the ones turned away are never drawn. The walnut box is built the same way and stays still; the board, with its walls and the ball, sits in a container with transform-style: preserve-3d that turns with rotateX and rotateY. Everything is measured in board units (580 by 400), the same ones the physics uses; a scale on the outer container fits it to the screen and the perspective grows with it.
The physics moves in fixed steps of 1/240 of a second inside GSAP's ticker. On every step the ball gets the part of gravity that runs along the board: the sine of the tilt on each of the two axes, times 5/7 for a rolling sphere. Then rolling friction (a steady brake) and a little drag are taken off, and the ball moves. Inside the rim of a hole an extra force pulls it towards the middle, like the wood sloping down: once its centre goes past the edge, the ball is in.
For each wall the point of the rectangle closest to the centre of the ball is found; if it is closer than the radius, the ball is put back outside along that direction and the part of its speed going into the wall is reversed, damped by BOUNCE, while the part along the wall loses 4%. Steps this small stop the ball from going through a wall even when it is fast. The hardest knock of each frame gives the board an elastic jolt of a few tenths of a degree, in the drawing only.
The ball is a circle with a steel gradient, the warm reflection of the wood below and a window at the top. It lives inside the tilted board, at its height with translate3d, but with the turns of the board and the box applied backwards (rotateY and rotateX with the sign flipped, in reverse order): so it always stays round towards you, and the walls in front hide it as they would a sphere. The shadow instead is drawn on the wood, further away and softer when the ball falls from above.
The autopilot has a list of points, the middle of each square along the line moved away from the holes nearby. On every step it works out an acceleration like a spring towards the point plus a damper on the speed, turns it into the tilt that would give it (arcsine, capped at MAX_TILT) and the board follows with a little lag, like on two real axles. Near the point it moves on to the next. The first ball of the round has the hole as its last point: that way the video shows the whirl too. The whirl, the fall, the cup, the confetti and the tag are GSAP timelines; everything always starts from the same state, so each round is identical to the one before.
| Name | File | Default | What it changes |
|---|---|---|---|
MAX_TILT | script.js | 6 | How far the board can tilt, in degrees: a little makes a calm game, a lot a jumpy one. |
GRAVITY | script.js | 1 | How heavy the ball feels: 0.6 slow and floaty, 1.6 fast. |
FRICTION | script.js | 1 | How much the wood slows the ball down: 0 is ice, 3 is felt. |
BOUNCE | script.js | 0.34 | How much speed the ball keeps after hitting a wall (0 stops dead, 0.9 bounces a lot). |
VIEW | script.js | 34 | How far the box leans away from you, in degrees (0 = seen straight from above). |
SHOP | script.js | Marlow & Pip Toyworks | The shop's name, on the box and on the tag. |
TITLE | script.js | Rolled home! | The title when the ball reaches the cup. |
CONFETTI | script.js | 72 | How many strips of paper fly out of the cup. |
For the site of a toy shop or a woodworker, a 404 page you can play, an app's waiting screen, a contest or a surprise gift that opens when the ball reaches the cup, the site of a studio that wants to show what CSS 3D can do. The physics (projected gravity, knocks against rectangles, holes with a rim) works for any table game with a ball.
The same box, the same physics and the same autopilot, with the sizes adapted: the box shrinks to fit the width of the screen, and the perspective with it. You tilt the board by dragging a finger: wherever the finger is compared with the middle of the box, that is where the board goes down; when the finger lifts, the autopilot takes over again.
No motion: the box fades in with the game already done, the board level, the ball resting in the brass cup, and the «Rolled home!» tag fades in too. No confetti and no tilting.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 15 KB; the libraries: GSAP 28 KB.
Tested on 11 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.