
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
A freelancing app’s payout going to the bank as gold coins: each one comes in from above turning over, lands on the stack with a clink of light, the stack rocks on its spring and the amount beside it climbs by its share. The height of the stack is the progress; at the end a glint runs up it. If the bank does not answer, the coin hits the rim, lands on its edge and rolls away.
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, viewport-fit=cover">
<title>The coin stack transfer</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 payout card of a freelancing app: the money goes to the bank as coins that fall turning over and stack up
in a column on the right. The height of the stack is the progress, the amount beside it climbs as every coin
lands. The coins are drawn in pseudo 3D on the canvas by script.js; the words and the button are HTML -->
<section class="pay" aria-labelledby="pay-title" data-pay>
<div class="pay__words">
<h2 class="pay__title" id="pay-title">Payout · Fernhill Studio</h2>
<p class="pay__amount" aria-hidden="true"><span class="pay__figure" data-amount>€2,480.00</span></p>
<p class="pay__status">
<span class="pay__swap" data-status>Ready to transfer</span>
<svg class="pay__tick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.6 10.4l3.5 3.5 7.4-8" pathLength="1"/></svg>
</p>
<p class="pay__sub"><span class="pay__swap" data-sub>To account ending 4821 · instant</span></p>
<button class="pay__btn" type="button" data-action data-autoclic><span class="pay__label" data-label>Transfer to bank</span></button>
</div>
<div class="pay__stack" role="progressbar" aria-labelledby="pay-title" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Not transferred yet" data-stage>
<canvas class="pay__coins" aria-hidden="true" data-coins></canvas>
</div>
<p class="sr" role="status" aria-live="polite" data-live></p>
</section>
</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.
32 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 coin stack transfer” from Animorio and fit it into my siteEvery coin has three vectors: its normal and two axes in its face. The camera is orthographic and looks down at VIEW degrees, so a vector of the world lands on the screen as (x, −(y cos φ − z sin φ)). The face towards the camera is a circle carried onto its ellipse by a single affine transform (ctx.transform with the projected axes), so the relief is drawn on it with drawImage; the edge is a ring of 72 quads (128 for the stacked coins), split into three bands, a bevel at each side and the reeding in the middle, each coloured by its own normal. Only the surfaces facing the camera are drawn: on a convex solid they never overlap, and no sorting is needed.
The colour of every surface depends less on where the light comes from than on where its reflection points, r = 2(n·v)n − v: a soft box above and to the left, a bright horizon and a dark floor. The number that comes out picks the colour on a ramp of three golds (deep, plain, pale) read from the CSS. So a turning face flashes when its reflection crosses the box, and the upper bevel of every stacked coin catches a line of light that sets the coins apart.
The euro sign, the rim, the ring of beads and the rosette on the back are drawn in white on black as a height map, blurred a little and differentiated in x and y. From it come eight images, one for every direction of the light: the lit slopes in light paint, the ones turned away in dark, and the raised parts veiled like satin metal. Every frame the face works out where the light falls in its own axes and blends the two nearest images: the relief stays right while the coin turns.
A coin starts above the card with a small speed and falls with GRAVITY; its time to reach its place is solved in advance, and its turn about the level axis has the speed that brings it down flat (a whole number of turns). Landed, its tilt dies away while the axis turns faster and faster, like a coin on a table, and it gives a one pixel hop. The stack is a rigid column on an angular spring, integrated in steps of 1/240 of a second, softer the taller it is: it turns about the edge of its bottom coin on the side it leans to, and every coin pushes it from the side it came from. The coins below the top are a picture painted once; the top one is drawn for real.
When the bank stops answering, the coins already in the air land and then one is sent at the right rim. Striking the rim it bounces to the right and towards us, the stack takes a knock, and its turn changes speed so that it meets the floor on its edge, face towards us; there it bounces a little more, rights itself with a dying wobble and rolls: the distance over the radius turns the design on its face. Retry goes on from the share already confirmed and the lost coin is sent again.
The stack is a role=progressbar with aria-valuenow and an aria-valuetext that says how many euros have arrived, the card has aria-busy while it works and an aria-live line says when the transfer starts, is interrupted and ends. The button changes its word (Transfer to bank, Sending…, Retry, Done) and takes the new width with a little bounce; while the coins fall it is aria-disabled. The dashed outline of the full stack shows from the start how much is left.
| Name | File | Default | What it changes |
|---|---|---|---|
AMOUNT | script.js | 2480 | The payout, in euros. |
ACCOUNT | script.js | '4821' | The last digits of the account it goes to. |
SECONDS | script.js | 4.8 | How long a transfer takes in the demo, in seconds (a real one follows the bank). |
COINS | script.js | 16 | How many coins make the full stack: each one is an equal share of the amount. |
FAIL_EVERY | script.js | 2 | In the demo, one transfer in how many is interrupted (0 never). |
FAIL_AT | script.js | 0.56 | How far the interrupted transfer gets, from 0 to 1. |
FLIPS | script.js | 2.4 | How many times a coin turns over in a second of falling. |
GRAVITY | script.js | 2600 | How hard the coins fall, in pixels a second squared. |
SWAY | script.js | 1 | How much the stack rocks when a coin lands: 0 not at all, 2 a lot. |
SPRING | script.js | 120 | How stiff the stack’s spring is: higher rights it faster. |
VIEW | script.js | 24 | How far down we look on the coins, in degrees. |
SHEEN | script.js | 0.9 | The glint that runs up the finished stack: 0 none, 1.5 bright. |
For a wait of a few seconds that is about money: an outgoing payment, a withdrawal from a balance, an instant transfer, a refund. The coins say at once what is going on and the amount climbing beside them reassures; for any loading that is not about money the metaphor is out of place.
The card takes the screen’s width less 32 pixels (358 on a 390 screen) and stays 238 tall; the coin column narrows to 132 pixels and the amount drops to 31. The canvas paints at the screen’s real density, up to twice. The button is a pill 36 tall: under a finger it squashes and springs back, and its words widen on the width axis only under the mouse.
No falls, turns, hops or rocking: every coin fades in at its place in the stack, without rings or sparks, and there is no stray coin (the red words stay). No glint at the end and no coins picked up: they fade out. The stack still grows, because it is what says how far along things are.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 17 KB; the libraries: GSAP 28 KB.
Tested on 8 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.