
The roll-up menu
Menus, 3D, WebGL, Physics
NewFree
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.
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>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
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 siteThe 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.
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.
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 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.
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.
| Name | File | Default | What it changes |
|---|---|---|---|
REST | script.js | 0.22 | How full the pill is at rest, from 0 (flat) to 1 (a round tube). |
HOVER | script.js | 0.8 | How full it gets under the pointer. |
FULL | script.js | 1.12 | How full the pump blows it on a click (over 1 it is taut). |
SPRING | script.js | [130, 8] | The air inside: stiffness, and damping (lower, more wobble). |
CREASES | script.js | 1 | How deep the creases of the soft vinyl are. |
GLOSS | script.js | 1 | How shiny the vinyl is. |
PUMPS | script.js | 3 | How many strokes of the pump fill it on a click. |
RESET | script.js | 4.5 | The seconds before it lets the air out and is ready again (0 = it stays full). |
LABEL | script.js | 'Join the waitlist' | The words on the pill. |
JOINED | script.js | "You're on the list" | The words once you have joined. |
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.
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.
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.
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.
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.