Skip to content
animorio
IT
All components

The inflatable button

Buttons, Physics, WebGL, 3DWebGL, Canvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The Join the waitlist button made of glossy vinyl, like a pool float, with puffed letters welded on top. At rest it is a little soft, low and full of creases; under the pointer it really blows up: the edges round off, the creases smooth out, the seam pulls tight and the shine slides over the swelling plastic. Pressed, it squashes where the finger is; clicked, a pump fills it in three strokes, it hops, and the letters go flat and puff up again as You’re on the list.

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 inflatable button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fredoka@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the inflatable: a pill of glossy vinyl, like a pool toy, with puffed letters welded on top. It is drawn in a
         canvas from a height map: the pill's distance from its own edge and the letters' distance from theirs, raised
         to a power that changes with the air inside, then lit like shiny plastic. At rest it is a little soft and
         creased; the pointer blows it up, a press squashes it, a click pumps it full -->
    <div class="float" data-float>
      <div class="float__body" data-body>
        <span class="float__shadow" aria-hidden="true" data-shadow></span>
        <button class="float__btn" type="button" aria-describedby="float-note" data-btn data-autoclic>
          <canvas class="float__vinyl" aria-hidden="true" data-canvas></canvas>
          <span class="sr" data-label>Join the waitlist</span>
        </button>
      </div>
      <p class="float__note" id="float-note" data-note>Early access opens in spring. No spam.</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.

19 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 inflatable button” from Animorio and fit it into my site

What it does

On a white page a pill of pink vinyl, glossy like a pool toy, with Join the waitlist in puffed, raised cream letters; under it, in grey, Early access opens in spring. No spam. At rest the pill is a little soft: it is low, its edge is not quite straight, the plastic folds across and creases along the welded seam that runs just inside the edge. With the pointer on it, it blows up on its spring, with a small wobble: it rises, its ends turn round like a tube, the creases vanish, the seam pulls tight and the long reflection of a window slides over the swelling surface following the pointer. Pressed, the plastic squashes under the finger and the air swells the sides, the pill widens and lowers. On a click the line under it says Saving your spot and the pill fills in three strokes of a pump, each with its own jolt, until it is taut and very shiny; then it hops, lands squashed, and the letters go flat and come back as You’re on the list with a tick, while the line turns green: You are in. We will email you the day we open. After a few seconds the air goes out slowly and Join the waitlist comes back.

How it works

A height map from the shape

The pill’s height comes from each pixel’s distance from the edge, worked out exactly for the capsule (the radius minus the distance from the middle segment). Divided by the radius it becomes s, from 0 at the edge to 1 in the middle, and the height is s(2−s) raised to a power that changes with the air: 1.75 when soft, a low shape that slopes gently to the edge; 0.5 when full, an arc of a circle, a real tube; under 0.5 when taut. A table of 1024 values works it out once a frame.

Puffed letters, from a distance transform

The words are written once on a hidden canvas. Felzenszwalb’s distance transform, one pass down the columns and one along the rows, gives every pixel inside the letters its distance from the edge of the stroke, corrected by half a pixel with the coverage. On each stroke it becomes a tube (the root of 1−(1−u)²) and two box blurs melt the ridge in the middle: the blurred distance field turns the letters into soft sausages, as tall as the pill is full.

Creases, seam and a fingertip

The creases are folds: segments laid at random across the tube and small puckers square to the seam, each shaped as a ridge beside a groove (the slope of a bell), fading as the pill fills. The seam is a groove that gets narrow and shallow as the plastic pulls tight; the soft outline wanders by a pixel and a half. A press is a bell that sinks under the finger and lifts all the rest a little.

The light of vinyl in a shader

The fixed fields go up to the graphics card as two float textures, read pixel for pixel with texelFetch. The fragment shader works out the height of the pixel and of its four neighbours, takes the normal from the slope and lights the plastic: diffuse light with an ambient stronger towards the top, an occlusion where the surface is hollow, the letters’ shadow, two Phong speculars and the reflection of a long window that moves with the pointer. Without WebGL 2 the same sum runs in the script, at density 1.

The air is a spring

How full the pill is, is a number moved by a lightly damped spring (SPRING), so every change wobbles; the press has its own stiffer spring. The pump is a GSAP timeline that raises the target in strokes, and the spring follows each with a jolt. The whole box lifts, squashes and hops with CSS transforms, and the shadow stays on the page and spreads. When everything is still, nothing is redrawn.

Values to change

NameFileDefaultWhat it changes
RESTscript.js0.22How full the pill is at rest, from 0 (flat) to 1 (a round tube).
HOVERscript.js0.8How full it gets under the pointer.
FULLscript.js1.12How full the pump blows it on a click (over 1 it is taut).
SPRINGscript.js[130, 8]The air inside: stiffness, and damping (lower, more wobble).
CREASESscript.js1How deep the creases of the soft vinyl are.
GLOSSscript.js1How shiny the vinyl is.
PUMPSscript.js3How many strokes of the pump fill it on a click.
RESETscript.js4.5The seconds before it lets the air out and is ready again (0 = it stays full).
LABELscript.js'Join the waitlist'The words on the pill.
JOINEDscript.js"You're on the list"The words once you have joined.

Try them in the Playground

When to use it

For the button of a launch page, a waitlist, a newsletter sign-up or a playful summer product: wherever the click should feel like a small treat. It works on its own on a light background, in a cheerful voice.

On phones

The pill scales to the width of the screen (at 390 pixels it is 314 wide) and the canvas follows the screen’s density up to 2. A finger resting on it blows it up and squashes it where it touches; the light follows nothing and stays up on the left.

With reduced motion

No wobble and no hop: the air changes quickly and evenly, the pill does not move or squash, the light stays still. The pump still fills it and the letters change with a fade, without a bounce.

Pitfalls

  • Worked out in the script, the light of 140 thousand pixels cost too much every frame: the sums moved to a WebGL 2 shader, and the script stays only as a fallback, at density 1.
  • With an 8 bit texture the slope of the height map came in steps and the light showed bands: the fields go up as 32 bit floats, read with texelFetch and no filtering (one texel for one canvas pixel).
  • The distance transform leaves a ridge along the middle of every stroke, and the letters looked like roofs: two box blurs melt it into a round puff.
  • Creases made of regular sines looked like scales: they became single folds laid at random, each shaped as a ridge beside a groove.
  • The white reflection added on the edges and at the bottom made the plastic grey: the reflections of the room take some of the vinyl’s colour, only the lamp’s specular stays white.
  • GSAP’s step grows when the computer is slow and the springs blew up: the physics takes four small steps a frame, with the step capped at 1/30 of a second.

Libraries

GSAP 3.15.0, Fredoka (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