
The weightless objects
Physics, 3D, Hover
NewFree
A hand-painted beech top, thrown by a string that unwinds: it sleeps upright, slows down, its axis starts to circle, it wanders across the table, wobbles more and more, its rim touches, it rolls round and stops on its side, rocking. When it spins fast the painted bands melt into rings of colour (teal and mustard turn green). A click throws it again.
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 spinning top</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<!-- the wall and the oak table, drawn once in perspective -->
<canvas class="room" aria-hidden="true" data-room></canvas>
<header class="shop">
<p class="shop__name" data-name>Birch & Bobbin <span>wooden toys</span></p>
<h1 class="shop__title" data-title>Tops that hum.</h1>
<p class="shop__line" data-line>Turned from beech, painted by hand. Spin it fast and the stripes melt into rings of colour, even green, which is not painted on.</p>
</header>
<!-- the top, its shadow and the string: a solid of revolution drawn every frame -->
<canvas class="toy" aria-hidden="true" data-toy></canvas>
<div class="meter" data-meter aria-hidden="true">
<span class="meter__value" data-rpm>0</span>
<span class="meter__unit">rpm</span>
</div>
<p class="hint"><span class="hint__pill" data-hint>Click to spin it again</span></p>
<!-- the whole table is the button: a click winds the string and throws the top again -->
<button class="catch" type="button" aria-label="Throw the spinning top again" data-catch data-autoclic></button>
</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.
26 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 spinning top” from Animorio and fit it into my siteThe shape is a profile: a list of rings from the tip to the handle, each with its height and radius. Turning the profile through 64 slices gives a grid of quads. Every frame the vertices are tilted and turned with the top’s axis, projected with a light perspective and drawn on the canvas from the furthest to the nearest, skipping those that face away. The light is a diffuse light plus a tight varnish highlight, worked out from the normal of each quad.
The colour of each band is a function of the angle round the axis (petals, teeth, sectors, spirals). For every band the running sums of the colour are prepared, so the average over any arc costs two lookups. Each quad takes the average of the colour that passed under it while the shutter was open: at rest it is the pattern, slowly it becomes a smeared stripe, fast (more than one turn a frame) it is the average of the whole ring. That is why teal and mustard, mixed by the eye, turn green.
The wound string is a helix on the cone: its turns are counted from how much is left to unwind and drawn only where the surface faces us. The free piece runs from the end of the helix to the hand, off the right of the screen: a curve that goes taut when the pull starts, with dashes that run to show the string moving; once the top has gone, the string whips away and disappears.
The throw is a GSAP timeline that takes spin speed, lean, precession speed, wobble size and how much it wanders from one value to the next: it sleeps, slows and precesses, wobbles, falls. Every frame these values become angles: spin and precession add up over time, the wobble is a sine, and the tip slides sideways to the lean (little loops) plus a drift that curves and turns back before the edge of its patch.
The angle at which the top lies on its side comes from the profile: the first ring that touches the table as it leans. When it gets there there is a knock and a bounce, then the precession slows to zero: the top rolls on its rim round its tip and, so that the rim does not slip, it turns about itself as much as the rolling asks (divided by the cosine of the lean). The final rocking is a keyframe tween on the same angle.
| Name | File | Default | What it changes |
|---|---|---|---|
SPIN | script.js | 1 | How hard the string pulls: the speed the top starts at (with less speed the rings of colour do not fully form). |
SPIN_TIME | script.js | 6 | How many seconds it spins before it falls. |
WOBBLE | script.js | 1 | How far the axis leans while it circles, and how much it wobbles. |
WANDER | script.js | 1 | How far it wanders across the table. |
SIZE | script.js | 1 | How big the top is. |
BLUR | script.js | 1 | How long the shutter stays open: higher, the pattern melts into rings at a lower speed already. |
--paint-teal | style.css | #1c8c86 | The teal of the top: spinning, together with the mustard, it makes the green. |
--table | style.css | #c58c55 | The colour of the oak table. |
For the page of a toy shop or a woodworker, a product page to play with, a site for children, a 404 or waiting page worth watching, or to explain a little physics (precession, colour mixing) with a toy.
The same story with the sizes adapted: the text sits above, the table takes the lower part and the top is in the middle, a little bigger compared to the screen; it wanders in a narrower patch, so it never leaves the sides. The revolutions per minute sit at the bottom right and a tap throws it again.
Nothing moves: the top already stands on its tip, upright, its pattern melted into its rings of colour (the picture of it spinning fast), and the room and the title fade in. No throw, no string, no revolutions per minute and no line to throw it again.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 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.