
The time wheel
Pickers, 3D, Physics
NewFree
The Upload photos button that sends the photos up by hot air balloon: pressed, a wicker basket comes out of the pill, the fabric spills out in a crumpled heap and with every blast of the burner it fills a little more, until the folds smooth out, the seams pull tight and it becomes a balloon of coloured gores. Then it lifts off, the ropes go taut and the basket leaves with a jolt: how high it has climbed is how far the upload has gone. At the top it sways in the wind with the basket swinging under it; pressed again it comes down, lands and sighs flat back into the button.
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 hot air balloon button</title>
<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">
<!-- the upload button: pressed, a wicker basket comes up out of the pill, the fabric spills out of it and the
burner fills it into a balloon of coloured gores. Then it climbs: how high it is, is how far the upload has
gone. The balloon, the ropes, the basket and the flame are drawn in the canvas above the pill every frame -->
<div class="balloon" data-balloon data-autoclic>
<canvas class="balloon__sky" aria-hidden="true" data-sky></canvas>
<button class="balloon__btn" type="button" aria-describedby="balloon-hint" data-btn>
<span class="balloon__face" data-face>
<svg class="balloon__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7.5 18.5h-1a4.5 4.5 0 0 1-.6-8.96A6 6 0 0 1 17.6 8.1a4.25 4.25 0 0 1-.1 8.4h-.9"/><path d="M12 19.5v-8M8.75 14.5 12 11.25l3.25 3.25"/></svg>
<span data-text>Upload photos</span>
</span>
</button>
<p class="balloon__hint" id="balloon-hint"><span data-hint>12 photos selected · up to 20 MB each</span></p>
<p class="sr" aria-live="polite" data-live></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.
27 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 hot air balloon button” from Animorio and fit it into my siteThe fabric is a grid of 26 rows from the crown to the mouth and four columns for every gore, drawn in a canvas every frame. Each point comes from a profile (radius and height at a given row) turned round the axis and seen a little from the side; each column is a strip filled with a gradient down its length, with colours worked out row by row from the normal of each quad (wrapped light, a soft highlight, the limb getting darker). The columns are drawn from the furthest to the nearest, and the seams go on top.
The full profile is a teardrop: a half sphere on top and a cone narrowing down to the mouth. The flat one is a low wide heap slumped to one side, with folds: a few sine waves running round and along the fabric and moving slowly. One number, the inflation, mixes them: it goes towards 1 on a spring (SPRING), and every blast of the burner gives it a push, so the balloon fills in bursts and at the end goes a moment too far and settles. The fuller it is, the more the folds fade and every gore bulges a little between its two seams.
The basket is two points, the rim and the floor, moved in verlet in steps of 1/240 of a second: the ropes hold it no further than ROPE pixels from the mouth (they go slack when the mouth is closer, and are then drawn with a sag), the basket is rigid and a soft hinge keeps it in line with the ropes, pushing the two points opposite ways. On the ground, while the balloon fills or after landing, the basket stands straight under the mouth; in flight every move of the balloon, the climb and the gusts, swings it a beat late.
The height is a spring towards the percentage of the pretend upload (a timeline in fits and starts), with its acceleration capped below gravity: if the balloon came down faster than a fall, the basket would be left floating. In flight the balloon follows a slow drift on a second spring, and every two or three seconds a gust knocks it, tilting it a little and swinging the basket.
The burner sits between the ropes and sends particles towards the mouth, a few at a time for the pilot flame, in bunches at every blast. The particles are a radial gradient drawn once, added together on a separate transparent canvas and then laid on the drawing: on white, pure additive would not show. The same blast lights the fabric near the mouth and the inside of the mouth in orange.
| Name | File | Default | What it changes |
|---|---|---|---|
PHOTOS | script.js | 12 | How many photos the pretend upload sends. |
INFLATE | script.js | 1.6 | The seconds the burner takes to fill the balloon. |
UPLOAD | script.js | 3.2 | The seconds of the pretend upload: the climb. |
GORES | script.js | 12 | The panels of fabric round the balloon. |
SPRING | script.js | [90, 9] | The spring of the fabric: stiffness, and damping (lower, more it bounces when full). |
WIND | script.js | 1 | How much the wind sways the balloon once it is up (0 = still air). |
ROPE | script.js | 34 | The length of the ropes from the mouth of the balloon to the basket, in pixels. |
For the action that sends something somewhere and takes a while: uploading photos, publishing an album, sending a bundle of files, submitting a project. It suits a photo or travel app, a print service, a page where the wait is part of the fun; the button stays small, the balloon uses the room above it.
The sky narrows with the screen (at 390 pixels it is 358 wide) and the balloon scales with it; the pill stays 268 pixels wide and nothing spills over. A tap does what the click does and the line below says Tap; there is no basket peeking on hover, since a finger does not hover.
The balloon neither climbs nor fills: pressed, it fades in already full, at the top, with the basket still and straight and a still flame; the percentage runs in the pill and at the end it says Uploaded. Pressed again it fades away. No wind, no turning, and the words change without sliding.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 5 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.