Skip to content
animorio
IT
Back to the library

The spinning top

Physics, 3DCanvas, GSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

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.

The code

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 &amp; 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>

Playground

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

Or ask Claude Code for it

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 site

What it does

The page of a made-up wooden toy shop, Birch & Bobbin: a cream striped wall, an oak table seen a little from above, the title “Tops that hum.” and, top right, the top’s revolutions per minute. The letters of the title drop like wooden blocks and land with an elastic bounce. The top comes down hanging from its string, wound round its cone, swinging a little; then the string is pulled to the right, it unwinds (you see the turns disappear and the marks running along the string) and the top lands on its steel tip with two hops, already spinning. At first it sleeps: upright, nearly still, and its pattern (red petals on mustard, blue teeth, teal and mustard sectors, spiral stripes) has melted into rings of colour, with a green ring that is not painted on the top. Then it slows down: the revolutions drop, the axis leans and starts circling, the tip draws little loops and the top wanders across the table; the wobble grows, the rings come apart and the pattern shows again, first smeared, then still. In the end the rim hits the table with a knock (the letters of the title hop), the top bounces, rolls round on its rim about its tip slower and slower, rocks back and forth and stops on its side. The line to throw it again appears: a click lifts it, takes it away through the top and brings it down again on its wound string, for another throw, different from the first.

How it works

A top made of rings

The 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 pattern that melts into rings

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 string

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.

One timeline for the whole story

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.

Falling, rolling, rocking

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.

Values to change

NameFileDefaultWhat it changes
SPINscript.js1How hard the string pulls: the speed the top starts at (with less speed the rings of colour do not fully form).
SPIN_TIMEscript.js6How many seconds it spins before it falls.
WOBBLEscript.js1How far the axis leans while it circles, and how much it wobbles.
WANDERscript.js1How far it wanders across the table.
SIZEscript.js1How big the top is.
BLURscript.js1How long the shutter stays open: higher, the pattern melts into rings at a lower speed already.
--paint-tealstyle.css#1c8c86The teal of the top: spinning, together with the mustard, it makes the green.
--tablestyle.css#c58c55The colour of the oak table.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • Drawing the top several times with some transparency to make the blur does not work: the copies add up on each other and the edges turn see-through. The outline of a solid of revolution does not change as it spins about itself, so the geometry is drawn once and only the colour is mixed, with the running sums of each band.
  • With a pattern of opposite colours (blue and mustard, red and green) the average turns a muddy grey: the pairs on each band are chosen close (red on mustard makes orange, blue on cream makes pale blue), and only one pair, teal and mustard, is there on purpose to make a green that is not there.
  • With a straight cone from the tip to the rim, the fallen top lay on the whole side of its cone instead of its rim: the cone’s profile is hollowed (below the line from tip to rim), and the resting angle is worked out by looking for the first ring that touches.
  • Rolling on its rim without turning about itself, the top seemed to slide: for the point touching the table to stay still, its own spin must be minus the precession divided by the cosine of the lean.
  • A fromTo inside the relaunch timeline, as soon as it was created, put the top straight back up, before it had been lifted: the fromTo tweens and the first set have immediateRender: false.
  • The preview video stops when no GSAP animation is scheduled: the whole story, rocking included, is in the timeline, and the drawing every frame only reads its values; at rest the picture no longer changes.
  • The letters of the title have two spans: GSAP moves the outer one for the entrance and the hop at the knock, the inner one hums while the top spins. On a single element one transform wiped out the other.

Libraries

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.

Testing

Tested on 11 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All animations