
The inflatable button
Buttons, Physics, WebGL, 3D
NewFree
A hotel menu lies under a sheet of thick cream paper with the word Menu: pressing it the sheet rolls up from the bottom like a scroll, a real roll in WebGL wound on a spiral that grows fatter, with the back of the paper showing and its shadow on the menu. The roll can be grabbed and dragged; let go, it goes up or down with its own momentum.
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 roll-up menu</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz-italic.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/figtree@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the roll-up menu: the menu lies under a sheet of thick cream paper with Menu printed on it. Pressed, the
sheet rolls up from the bottom like a scroll (a real cylinder, drawn in WebGL) and uncovers the menu; the
roll can be taken and dragged up and down -->
<div class="roll" data-roll>
<nav class="roll__menu" id="roll-menu" aria-label="Main" data-menu inert>
<ul class="roll__list">
<li><a class="item" href="#" data-link><span class="item__name">Rooms</span><span class="item__note">Suites and garden rooms</span></a></li>
<li><a class="item" href="#" data-link><span class="item__name">Dining</span><span class="item__note">Two kitchens, one terrace</span></a></li>
<li><a class="item" href="#" data-link><span class="item__name">Spa</span><span class="item__note">Thermal pools, open till late</span></a></li>
<li><a class="item" href="#" data-link><span class="item__name">Offers</span><span class="item__note">Stay three nights, pay for two</span></a></li>
</ul>
<a class="book" href="#" data-link>
<span>Book</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h13.5M13 6.5l5.5 5.5-5.5 5.5"/></svg>
</a>
</nav>
<!-- the sheet of paper, drawn in WebGL; without WebGL a plain sheet that slides up -->
<canvas class="roll__paper" aria-hidden="true" data-paper></canvas>
<div class="roll__plain" aria-hidden="true" data-plain><span>Menu</span></div>
<!-- the real button: the whole sheet while it is down, the roll at the top once it is up -->
<button class="roll__grip" type="button" aria-expanded="false" aria-controls="roll-menu" data-grip data-autoclic>
<span class="sr" data-name>Open the menu</span>
</button>
</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 roll-up menu” from Animorio and fit it into my siteThe sheet is a grid of 13 by 721 vertices, dense down the sheet so that even the first, tightest turn stays round. Each vertex carries only its coordinates on the sheet (u, v): the shape is worked out by the vertex shader every frame from three numbers, the line where the paper leaves the table, the radius of the outer turn and the thickness of the paper.
Above the line the sheet is flat. Below it, the length s from the line is wound onto a spiral whose radius shrinks by one thickness every turn: r = R − k·φ, with k = THICK/2π. The arc travelled is s = R·φ − k·φ²/2, so φ = (R − √(R² − 2ks))/k, and the point is (line + r·sin φ, R − r·cos φ). The outer radius comes from the paper already wound: R = √(CORE² + THICK·L/π). So the roll leaves the flat paper without a corner, the bottom edge ends up in the middle and the roll really grows fatter.
The normal comes from the same angle: (0, −sin φ, cos φ). In the fragment shader, if it faces away from the eye the back of the paper shows, in another colour and with its own grain, and the normal is flipped. A light from above makes the top of the roll bright and its underside dark, with a touch of sheen; the flat paper just above the roll falls into its shade. A little perspective makes the roll, nearer the eye, look slightly bigger.
Before the paper a band under the roll is drawn with transparency, fading downwards: the shadow on the menu, softer at the ends. The word Menu, the fibres and the shading at the edges are drawn into a double density canvas that becomes the texture; outside the rounded corners the fragment shader discards the pixels.
The real button covers the whole sheet while it is down and only the band of the roll once it is up. Dragging more than 6 pixels the line follows the finger, and the speed is estimated with a moving average; letting go, the position projected ahead decides whether to go up or down, and a damped spring (SPRING, BOUNCE) takes the roll there starting from the finger's speed. It is redrawn only while the roll moves.
| Name | File | Default | What it changes |
|---|---|---|---|
THICK | script.js | 2.8 | The thickness of the paper, in pixels: more, a fatter roll. |
CORE | script.js | 6 | The radius of the first turn, in the middle of the roll. |
TOP | script.js | 22 | How much flat paper stays above the roll when it is all the way up, in pixels. |
SPRING | script.js | 70 | How hard the roll is pulled to the top or the bottom once it is let go. |
BOUNCE | script.js | 0.8 | How much it is held back: less, it overshoots and swings. |
SHADOW | script.js | 0.8 | How dark the shadow of the roll is on the menu. |
WORD | script.js | 'Menu' | The word printed on the paper. |
For the menu of a hotel, a restaurant, a winery, a bookshop or a museum: a site with a warm, tactile character, where opening the menu can be a small physical gesture. It works well on a panel of its own, on a phone or in a column.
The sheet is at most 360 pixels wide and narrows with the screen; the canvas follows the screen's density (up to 2). The roll is dragged with a finger (the sheet does not scroll the page while it is touched), and a pressing finger lifts the edge like the mouse over it.
No rolling: the sheet fades away and comes back already rolled at the top, or already flat when closing. Dragging, the roll follows the finger but let go it goes straight to its place, without momentum.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Figtree (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 9.8 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.