
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
The fairground tin can alley, played for a discount code: six enamel-painted tins in a pyramid on a wooden shelf over a striped counter, and a cloth ball in a slingshot. Pull the ball back and let go, or let the button throw it; from there it is all real rigid-body physics: the tins spin, knock into each other, bounce and fall. Three balls: clear the shelf and the ticket with the code comes up.
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>Knock them all down</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the tin can alley of a shop's promotion: six painted tins stacked on a fairground shelf, a cloth ball in a
slingshot. Pull the ball back and let go, or press Throw; the tins are rigid bodies that tumble, knock into
each other, bounce and fall. Three balls: clear the shelf and the ticket with the code comes out -->
<section class="game" aria-labelledby="game-title" data-game data-autoclic>
<header class="game__head">
<h2 class="game__title" id="game-title">Knock them all down</h2>
<p class="game__rule">Three balls. Clear the shelf for 15% off.</p>
</header>
<div class="booth" tabindex="0" role="application" aria-roledescription="tin can alley"
aria-label="Six tins on a shelf and a ball in a slingshot. Drag the ball back and let go to throw. With the keys: up and down aim, left and right set the strength, Enter throws."
aria-describedby="game-say" data-booth>
<canvas class="booth__scene" aria-hidden="true" data-scene></canvas>
<canvas class="booth__fade" aria-hidden="true" data-fade></canvas>
<!-- the prize: a fairground ticket that comes up when the shelf is clear -->
<div class="ticket" data-ticket hidden>
<div class="ticket__paper">
<svg class="ticket__star ticket__star--l" viewBox="-10 -10 20 20" aria-hidden="true"><path d="M0-9l2.6 5.6 6 .7-4.4 4.1 1.2 6L0 4.4-5.4 7.4l1.2-6-4.4-4.1 6-.7z"/></svg>
<svg class="ticket__star ticket__star--r" viewBox="-10 -10 20 20" aria-hidden="true"><path d="M0-9l2.6 5.6 6 .7-4.4 4.1 1.2 6L0 4.4-5.4 7.4l1.2-6-4.4-4.1 6-.7z"/></svg>
<p class="ticket__kicker">Shelf cleared</p>
<p class="ticket__prize" data-prize>15% off</p>
<p class="ticket__line" data-prize-line>your whole order</p>
<button class="ticket__code" type="button" data-copy>
<span class="ticket__code-text" data-code>TINCAN15</span>
<span class="ticket__copy" data-copy-label>Copy</span>
</button>
</div>
</div>
</div>
<div class="bar">
<div class="say" aria-live="polite" id="game-say">
<p class="say__big" data-say>Pull the ball back and let go</p>
<p class="say__small" data-say-small>Three balls left</p>
</div>
<button class="throw" type="button" data-throw><span class="throw__face"><span class="throw__label" data-throw-label>Throw</span></span></button>
</div>
</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.
45 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 “Knock them all down” from Animorio and fit it into my siteEvery tin is a rectangle with mass and inertia, the ball a circle, the shelf, the counter and the floor bodies that do not move. At each step of 1/120 of a second gravity pulls, then the contacts are found: between two rectangles the face that overlaps least is taken, the edge of the other is clipped against its sides and up to two points remain; for the ball, the point of the rectangle nearest its middle. Each contact has a key made of the edges it came from.
Twelve passes over all the contacts: an impulse along the normal that never pulls (it only pushes) and one along the surface limited by friction, with a small extra push that takes the overlap out. Last step's impulses start again from the same key, and that is what keeps the pyramid still. A hard knock bounces (BOUNCE), the cloth ball hardly at all; whatever stays still for half a second goes to sleep, and wakes when something moving touches it or when its support goes.
The label is painted once on a canvas two turns long and then wrapped round in 24 strips: each strip takes the piece of label at the angle asin(x / radius), so at the sides the pattern narrows as on a real tin, and drops a little with the cosine to follow the curve of the rim seen from above. Lit side, dark side and the sharp shine are worked out from the normal of each point against the light, with the tin's angle at that moment; the lid shows on the side that looks up.
Pulling, the pouch follows the finger, limited to PULL pixels, never down among the waiting balls nor out of the scene; the ball leaves with a speed of POWER for every pixel pulled. The dashes are the same integration as the physics, step by step, so the ball takes exactly that way until it touches something. Throw and the keys look for the angle that takes the ball onto the chosen tin, trying a quarter of a degree at a time.
Shelf clear: the ticket comes up with elastic.out, the stars turn, the prize widens with Anybody's wdth axis and little paper stars fall with gravity and air drag. With the scene in focus the arrows aim and set the strength, Enter throws. A click from a script (the library's preview) makes two throws that are always the same, because the physics steps are fixed and the result is the same each time: the pyramid falls, the ticket comes up, then everything goes back.
| Name | File | Default | What it changes |
|---|---|---|---|
BALLS | script.js | 3 | How many balls a go, from one to four. |
GRAVITY | script.js | 1500 | How hard everything falls, in pixels a second squared. |
POWER | script.js | 14.2 | How fast the ball leaves for every pixel the band is pulled back. |
PULL | script.js | 66 | How far the band can be pulled back, in pixels. |
BOUNCE | script.js | 0.3 | How much the tins bounce off each other, the shelf and the floor: 0 not at all. |
GRIP | script.js | 0.42 | How much the tins grip what they touch as they slide. |
HEAVY | script.js | 5 | How many times heavier than a tin the cloth ball is. |
AIM | script.js | 5 | How far, in pixels, Throw may miss the tin it aims at. |
TINS | script.js | [['#d8333b', '#f8ecd4', 'stripes'], …] | The paint of the six tins, from the bottom row up: the colour, the second colour and the pattern (stripes, zigzag, stars, diamonds, bands, waves). |
For an online shop's promotion, the page of a fair or a village festival, the pop-up that gives away a discount, a newsletter with a prize, the site of a place that lets its customers play: anywhere a discount code can be won by playing instead of just handed out.
The scene is at most 340 pixels wide and narrows with the screen, but the physics always works in its own units (340 by 320), so it throws and falls the same. The finger grabs the scene and pulls the slingshot without scrolling the page (touch-action none), and the ball never leaves the edges.
No flight or fall to watch: the throw is worked out at once and the scene fades from how it was to how it ends; the next ball appears in the slingshot with a fade, the ticket appears without pops and the pyramid goes back with a fade. The slingshot can still be pulled by hand, with the dashes.
GSAP 3.15.0, Anybody (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 21 KB; the libraries: GSAP 28 KB.
Tested on 17 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.