
The time wheel
Pickers, 3D, Physics
NewFree
The download of a design kit as a strand of yarn that comes in from the left, taut and swinging, and winds onto a ball that spins and grows with the bytes. Every pass is a great circle on a sphere at a new slant, drawn in 3D on a canvas with the back half hidden. If the connection drops the strand snaps and hangs from the ball until Retry; at the end the cut end tucks under, the ball gives a little hop and rolls a short way.
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, viewport-fit=cover">
<title>The yarn ball download</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the download of a design kit shown as a strand of yarn: it comes in from the left, taut and swinging, and is
wound onto a spinning ball that grows with the bytes. Every pass of yarn is a great circle on a sphere, tilted a
little more each time, drawn in 3D on the canvas. The words, the figure and the button are HTML -->
<section class="kit" aria-labelledby="kit-title" data-kit>
<div class="kit__head">
<div class="kit__words">
<h2 class="kit__title" id="kit-title"><span class="kit__swap" data-title>Field Kit</span></h2>
<p class="kit__meta"><span class="kit__swap" data-meta>Fonts, icons & textures · v2.4</span></p>
</div>
<p class="kit__figure" aria-hidden="true">
<span class="kit__pct is-quiet" data-pct>312 MB</span>
<svg class="kit__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.1 4.1L18.6 7.4" pathLength="1"/></svg>
</p>
</div>
<div class="kit__ball" role="progressbar" aria-labelledby="kit-title" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Not downloading" data-bar>
<canvas class="kit__paint" aria-hidden="true" data-paint></canvas>
</div>
<div class="kit__foot">
<p class="kit__note"><span class="kit__swap" data-note>1,208 files</span></p>
<button class="pill" type="button" data-action data-autoclic><span class="pill__label" data-label>Download</span></button>
</div>
<p class="sr" role="status" aria-live="polite" data-live></p>
</section>
</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.
28 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 yarn ball download” from Animorio and fit it into my siteThe ball is a single curve worked out once: a point p and its heading t walk along a great circle (p' = p cos ds + t sin ds, t' = t cos ds − p sin ds). At the end of every pass, a little longer than one turn so that the passes do not all cross at one place, the heading turns about p towards the next of a set of slants spread over the whole sphere (a golden spiral), by at most TILT degrees: so every pass is a new great circle and the ball gets covered all over instead of in a band.
The ball's volume grows with the yarn wound on, and the yarn with the bytes: the radius of every pass is the cube root of r0³ + (R³ − r0³) · s / N. Every pass lies on a sphere a little larger than the one before, so the old ones stay under the new ones in the drawing too.
Every frame the script finds where the strand coming from the edge touches the ball (the tangent from a point to a circle) and builds the rotation M = S·Eᵀ that brings the end of the curve exactly there, heading back along the strand and a little towards the viewer. The more yarn arrives, the further the end of the curve moves and the more the ball turns: no made-up spinning speed.
The projected curve is drawn in stretches: first the hidden half of the last passes, dark, from the newest to the oldest; then the core, a disc with a radial gradient as big as the passes of a few turns ago; then the front half from the oldest to the newest, so every pass covers the ones before. Every stretch has one dark outline under the whole piece (no joints), a body lit from the sphere's normal and darker the deeper it is buried, plies drawn as a dash that stays put on the yarn and a highlight on the newer passes. It is repainted only while something moves.
The incoming strand is a straight line with a little sag plus three damped modes of vibration (sines of one, two and three half waves): the tension sets their frequency, big chunks of bytes and the first jerk strike them, the ball's turning makes them quiver. When the connection drops the strand becomes two Verlet ropes with length constraints, gravity, the panel as a floor and the ball as an obstacle: one is pulled out through the edge, the other hangs from the ball and rests on it. Retry brings the strand back in and puts the rope back on the taut line.
The ball is a role=progressbar with aria-valuenow and an aria-valuetext in megabytes, the panel has aria-busy while it downloads and an aria-live line says when the download starts, pauses, breaks off and ends. The button changes its word (Download, Pause, Resume, Retry, Download again) and the pill takes the new width with a bounce; a click from a script, the preview's, never pauses.
| Name | File | Default | What it changes |
|---|---|---|---|
SECONDS | script.js | 6 | How long the demo download takes, in seconds (a real one follows its bytes). |
SIZE | script.js | 312 | The package's megabytes. |
PACKAGE | script.js | 'Field Kit' | The package's name. |
FAIL_EVERY | script.js | 2 | In the demo, one download in how many breaks off (0 never). |
FAIL_AT | script.js | 0.57 | How far the download that breaks off gets, from 0 to 1. |
BALL | script.js | 46 | The radius of the finished ball, in pixels. |
THREAD | script.js | 3.4 | How thick the yarn is, in pixels. |
PASSES | script.js | 96 | How many passes of yarn make the whole ball. |
TILT | script.js | 70 | By how many degrees at most each pass tilts from the one before. |
SWING | script.js | 1 | How much the incoming taut strand swings: 0 not at all, 1.5 a lot. |
FOLLOW | script.js | 0.4 | How long the ball takes to catch up with the bytes, in seconds. |
For a download or an export of a few seconds that has a name and a size: a font kit, an icon pack, a project archive. The growing ball says «it is piling up» better than a bar, and the snapped strand says at once that the connection dropped; for a half second load it is too much.
The panel takes the screen's width less 32 pixels (358 on a 390 screen) and keeps its height; the ball stays at 60% of the width and the strand gets shorter. The canvas paints at the screen's real density, up to twice. Under a finger the button squashes and springs back; its words widen on the width axis only under the mouse.
No jerks, swinging, whipping, hops or rolling: the strand is taut or slack without ringing, the ball grows following the bytes at once (it is what says how far along things are), the snapped strand already hangs still, at the end the cut end is already wound in, and the tick and the new words fade in.
GSAP 3.15.0, Anybody (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 16 KB; the libraries: GSAP 28 KB.
Tested on 8 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.