
The cookie banner
Consent, Transitions, SVG, Physics
NewFree
A Download · 248 MB button that, pressed, opens up round a small glass hourglass: the sand really falls grain by grain from the top bulb to the bottom one, digging a crater above and heaping into a cone below, and the sand let through the neck is the progress of the download. At the end the hourglass turns over on a spring and the button says Open file.
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 hourglass button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the hourglass button: pressed, it opens up round a little glass hourglass. The sand is a cellular automaton on
a canvas, grain by grain, and the grains let through the neck are the progress of the download. At the end the
hourglass turns over on a spring and the button opens the file -->
<div class="dl" data-dl>
<button class="dl__btn" type="button" aria-describedby="dl-progress" data-btn data-autoclic>
<!-- the hourglass: walnut caps and brass posts, the sand, and the glass over it -->
<span class="dl__glass" aria-hidden="true" data-glass>
<span class="dl__cap dl__cap--top"></span>
<span class="dl__post dl__post--left"></span>
<span class="dl__post dl__post--right"></span>
<svg class="dl__back" data-back></svg>
<canvas class="dl__sand" data-sand></canvas>
<svg class="dl__vial" data-vial></svg>
<span class="dl__cap dl__cap--bottom"></span>
</span>
<span class="dl__icon" aria-hidden="true" data-icon>
<svg viewBox="0 0 24 24"><path class="dl__arrow" d="M12 4.5v10.5M7.5 10.5L12 15l4.5-4.5"/><path d="M5 19.5h14"/></svg>
</span>
<!-- the words: one line at rest, two lines while it works -->
<span class="dl__rest" aria-hidden="true" data-rest>
<span class="dl__title">Download</span>
<span class="dl__size">· 248 MB</span>
</span>
<span class="dl__busy" aria-hidden="true" data-busy>
<span class="dl__title" data-title>Downloading</span>
<span class="dl__meta" data-meta>0% · 0 of 248 MB</span>
<span class="dl__hint" data-hint>Press to pause</span>
</span>
<span class="dl__done" aria-hidden="true" data-done>
<span class="dl__title">
<svg class="dl__file" viewBox="0 0 24 24"><path d="M14 3.5H7.5a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2V8z"/><path d="M14 3.5V8h4.5"/><path d="M9 13h6M9 16.5h4"/></svg>
Open file
</span>
<span class="dl__meta">field-notes.pdf · 248 MB</span>
</span>
<span class="sr" data-name>Download field-notes.pdf, 248 megabytes</span>
</button>
<span class="sr" id="dl-progress" role="progressbar" aria-label="Download progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" data-progress></span>
<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.
29 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 hourglass button” from Animorio and fit it into my siteThe hourglass is a grid of 27 × 47 cells, two pixels each, drawn on a canvas of the same size and scaled up with image-rendering: pixelated, so each grain stays a crisp little square. A second grid says which cells are inside the glass: the half width at each height is a curve that narrows quickly towards the neck, one cell wide, and the same curve, a little wider, is the SVG path of the glass.
Every step the grid is read from the bottom up (so a grain moves only once) and every other row the other way along. A grain falls if the cell below is free, otherwise it tries one diagonal at random and then the other; with probability REPOSE it slides two cells across over its neighbour, which flattens the heaps below the 45 degrees of the simple rules. On a step of the glass it slides towards the middle. FALL steps per frame set the speed of the fall.
The neck cell is a gate: a grain goes in only while the grains through are fewer than the ones allowed. The pretend download is a GSAP timeline in fits and starts taking the progress from 0 to 1 in SECONDS seconds, and at each update the grains allowed become progress × total grains. The percentage shown is through / total: the fallen sand really is the progress. On pause the allowed grains become the ones through at once and the thread breaks.
Each grain has one of the GRAINS colours, picked by a fixed generator, and the grains on the surface are a little lighter, the ones under three layers a little darker: the cones and craters read without outlines. The glass is two SVGs either side of the canvas: behind, a faintly lighter tint; in front, the edge and the highlights, strokes that follow the curve of each bulb a little inside it, long on the left and short on the right.
When the last grain is through and the sand is still, the angle of the hourglass goes to 180 degrees on a damped spring (stiffness 70, damping BOUNCE) while it shrinks a touch, so its corners stay clear of the words. With the spring at rest the angle goes back to 0 and the grid is turned round (turning by 180 degrees is reversing the order of the cells): the picture is the same, and the sand hanging from the cap falls onto the shut neck.
| Name | File | Default | What it changes |
|---|---|---|---|
SIZE | script.js | 248 | The size of the file, in MB. |
SECONDS | script.js | 6 | How long the pretend download takes, in seconds. |
FILL | script.js | 0.74 | How full the top bulb is at the start. |
REPOSE | script.js | 0.3 | How easily the grains slide across: 0 = steep 45 degree heaps, 1 = flat ones. |
FALL | script.js | 4 | How many cells a grain falls in each frame. |
BOUNCE | script.js | 0.3 | The spring of the turn over: low, it rocks before it settles; 1, it just stops. |
GRAINS | script.js | ['#e6be78', '#dcb26a', '#eecb8c', '#d2a660', '#c79a56', '#f2d7a0'] | The colours of the grains. |
For a download, an export, a backup or an upload that lasts a few seconds: wherever the wait is there anyway and the hourglass makes it pleasant, and where a natural action follows (opening the file). It suits a desktop app, a download page, a creative tool. For instant operations it is too much.
The open pill is at most 330 pixels wide and fits a 390 screen without spilling over; below 380 pixels the hourglass and the words move closer to the edge. A tap starts, pauses and resumes, and the hint says Tap to pause instead of Press to pause.
The pill changes size at once, the words and the hourglass fade in without sliding or turning. The sand is not seen falling: at every five per cent the automaton runs to the end within the same frame and the hourglass shows the sand already settled. The turn over is a fade.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 9.1 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.