Skip to content
animorio
IT
All components

The vending machine button

Buttons, 3D, PhysicsCanvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The Buy · €2.50 button painted like the window of a vending machine: in the end of the lacquered red pill a small lit window, and behind the glass a row seen from the front, in true 3D, with the steel spiral running back into the machine and wrapped chocolate bars standing between its turns. At a click the spiral turns once, its turns passing in front of the bars and behind them, and they all come forward; the front one goes past the edge of the shelf, tips, falls turning and bounces in the tray before it disappears. The pill says Enjoy · Change €0.50. Another click loads the next bar.

The code

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 vending machine button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the vending machine button: a Buy button painted like the front of a vending machine. In the round right end of
         the pill there is a small lit window with a row inside: a steel spiral, drawn in true 3D with its turns in
         front of the bars and behind them, holding wrapped chocolate bars on end. A click turns the spiral once: the
         front bar comes forward, tips over the edge of the shelf, falls and bounces into the tray, and the pill says
         Enjoy with the change; another click loads the next bar -->
    <div class="vend" data-vend>
      <button class="vend__btn" type="button" data-btn data-autoclic>
        <span class="vend__fill" aria-hidden="true"></span>
        <span class="vend__faces">
          <span class="vend__face" data-face="rest">
            <span class="vend__word" data-wide="Buy · €2.50" data-rest-text>Buy · €2.50</span>
          </span>
          <span class="vend__face vend__face--busy" data-face="busy" aria-hidden="true">
            <span class="vend__shine" data-busy-text>Vending…</span>
          </span>
          <span class="vend__face vend__face--done" data-face="done" aria-hidden="true">
            <span class="vend__line">
              <span class="vend__enjoy" data-done-text>Enjoy</span>
              <span class="vend__sep" aria-hidden="true">·</span>
              <span class="vend__change" data-change-text>Change €0.50</span>
            </span>
          </span>
        </span>
        <span class="vend__window" aria-hidden="true">
          <canvas class="vend__scene" data-scene></canvas>
        </span>
      </button>
      <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>

Playground

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.

25 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The vending machine button” from Animorio and fit it into my site

What it does

On a black background a lacquered red pill 60 pixels tall, with a soft reflection along the top and a red glow under it. On the left, in cream Mona Sans, Buy · €2.50; in the round right end a small window with a dark frame, rounder on the pill's side, and behind the glass the inside of a vending machine seen from the front and a little from above: at the top a strip of cold light lighting the machine, at the back the plates of the motors, the metal shelf running back and in front its edge with a small tag. In the middle a steel spiral of five turns runs from the motor to the edge, and between the turns four bars stand on end, wrapped in midnight blue with a cream band, an orange line, a few squares of chocolate drawn on it and the foil crimped at both ends; right and left the frame cuts two more rows, a green one and an ochre one. The nearer turns are larger, the far ones smaller and higher, and they pass in front of the bars low down and behind them up high; the wire is bright on top of each turn, where it reflects the light, and dark under it. On the glass two slanted reflections and a thin one. With the mouse over it the pill swells with a bounce, the words widen, the window's light comes up, a reflection runs over the glass and the spiral gives a small turn back, as a motor taking up its slack. At a click Vending… arrives with a sheen running through it and the spiral turns once round, starting and stopping gently: the turns run forward and all the bars move one step on. The front one goes past the edge of the shelf and tips forward ever faster, leaves the edge turning, falls into the room under the shelf, hits the tray with a bounce, lies flat and disappears under the frame. The pill says Enjoy, and smaller Change €0.50. Another click loads the next bar at the back of the row, which drops into its place with a bounce while the spiral takes up its slack and a reflection runs over the glass, and the pill goes back to Buy · €2.50. With the keyboard the focus shows a white ring, the words widen and the spiral gives its small turn; Enter or Space buy. On a phone a tap buys.

How it works

A pinhole camera

The window is a canvas 86 by 48 pixels, at the screen's density up to 3. The world has X to the right, Y up and Z towards us; the row's axis runs along Z from the edge of the shelf back. A point is projected with s = F / (F − Z): x = cx + X · s, y = horizon + (eye height − Y) · s. With the eye 22 pixels over the axis, things at the back are smaller and higher, and the depth of the row shows. The background, the light, the shelf and the motor plates are painted once on a canvas of their own.

The helix, segment by segment

The spiral is a helix of radius 11.5 and pitch 13: the wire at angle a lies at Z = −(a − φ) / 2π · pitch, so turning it by φ brings every turn forward. Each turn is made of 28 segments, and every segment is an item with its depth, sorted with the bars and the edge of the shelf from the farthest to the nearest: so the turns pass in front of the bars and behind them as in a real one. The wire's colour depends on the sine of the angle (bright on top, where it reflects the strip of light, dark under it), with a thin line of light shifted up, and it fades a little at the back.

The bars are boxes

Every bar is a box 13 by 17 by 4.5 pixels, with its centre and a tip ψ about the X axis. Its eight corners are turned and projected; of its six faces, those facing the camera are drawn (the dot product of the turned normal with the direction to the eye). The front face carries the wrapper, painted once on a canvas eight times finer and laid on the face's two triangles with the affine transform; the ends are foil, bright when they face up, and the sides folded paper. As it tips, the front face shortens and the top one widens, because it really is a box turning.

The turn and the fall

A GSAP tween takes the spiral round once in power1.inOut, and every bar in the row moves one pitch per turn. When the centre of the front one passes the edge, the bar is on its own: it turns about the edge like an upside down pendulum, ψ'' = 0.75 · g / r · sin ψ, ever faster; at 0.62 radians it leaves the edge with the speed it had (ω times the radius) and becomes a free body, with gravity, a little drag and its spin. At every step of 1/240 of a second its lowest corner is found: when it passes the floor of the tray the bar is put back up, its vertical speed is reversed and cut to BOUNCE and its spin is halved. At the third hit, or when it hardly bounces, it lies flat and slides down under the frame, disappearing.

The glass and the light

Over everything the strip of light at the top is drawn, with its glow coming down, and the reflections of the glass: three slanted bands with a gradient that rises and falls, in screen mode, and a bright line along the top edge. Under the pointer, or with the keyboard's focus, a fourth, stronger band crosses the glass and the glow grows; the spiral gives a small turn back and comes back with elastic.out. When nothing moves (no tween and no bar in the air), the loop stops drawing.

The words and the states

Three faces on top of each other: Buy · €2.50, with an invisible wide copy holding the room of the words that widen; Vending… with a sheen running through it; and Enjoy · Change €0.50 on a single baseline, its three parts arriving one after the other. The button is a real button: aria-disabled while it vends, an aria-live region says Vending…, Enjoy. Change €0.50. and Next bar loaded., and the aria-label says what the next click will do. The fall is over before the 2.8 seconds between two clicks of the preview, which so shows the whole round: buy, reload, buy.

Values to change

NameFileDefaultWhat it changes
LABELscript.jsBuy · €2.50The words at rest, with the price.
BUSYscript.jsVending…The words while the spiral turns; the aria-live region says it too.
DONEscript.jsEnjoyThe big word once the bar is in the tray.
CHANGEscript.jsChange €0.50The change, smaller after the word. In a real site this is what your server answers (the discount, the credit left, the order number).
TURNscript.js1.15The seconds of the spiral's turn.
COILSscript.js5The turns of the spiral, from 3 to 7: the bars in the row are one fewer.
GRAVITYscript.js1300How fast the bar falls, in pixels per second squared: lower, a slow fall, as on the moon.
BOUNCEscript.js0.34How much speed the bar keeps when it hits the tray: 0 it stops at once, 0.7 it bounces high.

Try them in the Playground

When to use it

For the button that buys a small thing at once: a snack, a coffee, a ticket, a token, a pack of credits in a game, a donation, the quick buy of a sweet shop or of a real machine wired to an app. It suits playful sites and late-night shops. It has to be wired to your real payment: here the purchase is simulated.

On phones

The pill is 300 pixels wide: at 390 pixels it fits with room to spare, and the window stays 86 by 48. A tap buys (with the elastic squash of the fill) and another tap reloads; the reflection on the glass, which the mouse brings on hover, passes on a phone when the bar is reloaded.

With reduced motion

The spiral does not turn or give its small turn, no reflection runs over the glass, the pill does not swell and the words do not widen (under the mouse the fill only lightens a little). At a click the front bar fades away and the others are already in their places, and Enjoy · Change €0.50 fades in; another click brings the new bar in at the back, fading in.

Pitfalls

  • With the bars as wide as the spiral, its turns showed only as an outline round them, and the spiral did not read. Bars narrower and lower than its diameter, and the camera higher, let the turns show over them and the row running back.
  • The bar fell under the frame in a single frame: the tray was below the edge of the window. Now the floor of the tray is just under the shelf, the bar bounces there in sight, lies flat and then slides down out of sight.
  • The first try turned the bar about the front edge of its base, which at the start was behind the edge of the shelf: the bar jumped two pixels forward. Now it turns about the edge of the shelf, which is right under its centre when it goes.
  • Enjoy, larger, and Change €0.50, smaller, each centred upright on its own, did not sit on the same baseline. Now they are inline pieces in a single line.
  • The new bar is loaded at the back of the row, where the front one hides it: the reloading click showed nothing. Now the spiral takes up its slack with a small turn back and a reflection runs over the glass.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.

Testing

Tested on 12 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components