Skip to content
animorio
IT
All components

The hot air balloon button

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

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.

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 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>

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.

27 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 hot air balloon button” from Animorio and fit it into my site

What it does

On a white background a dark pill with a cloud and arrow and the words Upload photos, and under it the line 12 photos selected · up to 20 MB each. With the mouse over it the rim of a basket peeks out of the top edge of the pill. Pressed, the wicker basket rises out of a slot in the pill, a heap of crumpled coloured fabric spills out of it, slumped to one side, and a small flame lights under the mouth: the words become Preparing 12 photos. With every blast the fabric fills more, the folds smooth out, the seams stop wandering and the heap stands up into a round balloon of coral, yellow, cream, teal, indigo and pink gores with a pale band round its waist, which swells a moment too far and settles. Then it lifts off: the words say Uploading 12 photos with the percentage, the ropes go taut, the basket leaves the pill and climbs with the balloon; with every jump of the upload the balloon goes higher, and when the flame blows the fabric near the mouth glows orange from inside. At a hundred percent the balloon is at the top, the words become Uploaded with a green tick, and the balloon sways slowly in the wind, slowly turning on itself; now and then a gust pushes it and the basket swings under it, a beat late. Pressed again it comes down, the basket touches the pill, the flame goes out, the fabric sighs into a heap, goes back into the basket and the basket into the pill. Pressed during the upload, it cancels the same way.

How it works

The balloon as a surface of revolution

The 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.

From flat to full, on a spring

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, a double pendulum in verlet

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 wind and the climb

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 flame

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.

Values to change

NameFileDefaultWhat it changes
PHOTOSscript.js12How many photos the pretend upload sends.
INFLATEscript.js1.6The seconds the burner takes to fill the balloon.
UPLOADscript.js3.2The seconds of the pretend upload: the climb.
GORESscript.js12The panels of fabric round the balloon.
SPRINGscript.js[90, 9]The spring of the fabric: stiffness, and damping (lower, more it bounces when full).
WINDscript.js1How much the wind sways the balloon once it is up (0 = still air).
ROPEscript.js34The length of the ropes from the mouth of the balloon to the basket, in pixels.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • Drawn as filled quads, the fabric showed a grid of light lines between them and looked faceted: now every column is a single strip with a gradient down its length, and only the seams remain.
  • On white, particles added together turn white and the flame vanished: they are added on a separate transparent canvas, which is then laid on the drawing normally.
  • The basket coming out of the pill with an elastic entrance was thrown into the air by the rising floor and stayed above the mouth: the floor now rises without a bounce, and the hinge between ropes and basket only acts while the basket is under the mouth.
  • The hinge that pushed only the floor of the basket added energy at every step and the basket ended up spinning horizontally: now it pushes the rim and the floor opposite ways.
  • Coming down, the height spring accelerated faster than gravity: the ropes went slack, the basket floated and then jerked. The acceleration is capped below gravity.
  • On landing the basket touched down askew and toppled like an inverted pendulum: on the ground it stops being a pendulum and stands up under the mouth.
  • In Chrome a canvas emptied only with clearRect and then left alone sometimes kept showing its last drawing: when there is nothing left to draw the canvas is emptied by setting its width again.

Libraries

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.

Testing

Tested on 5 October 2026

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

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 components