
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
A camera’s zoom slider made like the bellows of an old plate camera: dragging the plate with the lens, the leather pleats open and close like weights on springs, a ripple runs down the bellows and back, and every face takes the light by how steep it is.
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 bellows zoom</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 zoom of a camera, made like the bellows of an old field camera: the knob is the front plate with its lens,
and a pleated bellows runs from it to the back plate. The bellows is drawn by the script on the canvas -->
<div class="zoom" data-zoom>
<div class="zoom__head">
<span class="zoom__label" id="zoom-label">
<svg class="zoom__icon" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="10.5" cy="10.5" r="6.5"/>
<path d="M15.4 15.4L20 20M10.5 7.8v5.4M7.8 10.5h5.4"/>
</svg>
Zoom
</span>
<span class="zoom__value" aria-hidden="true"><span class="zoom__number" data-number>3.2</span><span class="zoom__times">×</span></span>
</div>
<div class="zoom__track" role="slider" tabindex="0" aria-labelledby="zoom-label" aria-orientation="horizontal" aria-valuemin="1" aria-valuemax="8" aria-valuenow="3.2" aria-valuetext="3.2 times" data-track data-autoclic>
<span class="zoom__rail" aria-hidden="true" data-rail></span>
<canvas class="zoom__bellows" aria-hidden="true" data-bellows></canvas>
<span class="zoom__rear" aria-hidden="true"></span>
<span class="zoom__knob" aria-hidden="true" data-knob>
<span class="zoom__plate"></span>
<span class="zoom__lens"></span>
</span>
</div>
</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 bellows zoom” from Animorio and fit it into my siteThe bellows is a row of faces, PLEATS times two: the first fold is on the back plate, the last on the knob, and each one between is a small weight tied to its neighbours by a spring, with the stiffness and air drag of BELLOWS. The spring pulls towards the point halfway between the two neighbours, so at rest the pleats are evenly spaced at any length; a second brake, between neighbours, calms the finest ripples first. It is worked out on GSAP’s ticker in steps of 1/300 of a second, with the frame held to 1/30.
Two folds closer than 1.7 pixels push apart with a hard spring, and a face stretched too far is pulled back. After every step one pass forwards and one backwards put each fold at least a hair from its neighbour, so even a bellows squeezed in a hurry stays in order.
Between two folds there is a face: its width and the depth of the pleat give its slope, and from that the way it faces. The light comes from the left and the front: how much the face turns to it, plus a narrow sheen, picks one of 131 colours worked out once from the four in the CSS. Under the faces the whole shape in the darkest colour shows as a crease at the joins; over them, the pale lines of the ridges, a gradient in source-atop that rounds the tube and one in destination-over that puts its shadow under it.
The outer folds stand out 4 pixels above and below: more where they are closer than the average, less where they are farther apart, as much as SWELL says. So the ripple shows in the bellows’ outline too, which swells where it passes. The zoom’s number follows the knob on Instrument Sans’s width axis, from 75 to 100.
Held, the knob goes where the hand goes, spread over the small steps; pressed on the rail it runs there on a stiff spring and then follows; past the ends it gives RUBBER pixels at most. Let go, or moved with the keys, the CARRY spring takes it to the value, and at an end it knocks against the stop and comes back a little. The slider role is on the whole rail, with aria-valuenow and aria-valuetext; a click from a script (detail 0, the one of the library’s preview) is a pretend hand that takes the knob far away.
| Name | File | Default | What it changes |
|---|---|---|---|
START_AT | script.js | 3.2 | The zoom when the page opens. |
RANGE | script.js | [1, 8] | The lowest and the highest zoom. |
STEP | script.js | 0.1 | How much the zoom moves at a time. |
KEYS | script.js | [0.1, 1] | How far an arrow key moves the zoom, and with Shift or the Page keys. |
PLEATS | script.js | 18 | The pleats of the bellows, two faces each. |
BELLOWS | script.js | [12000, 5] | The bellows’ springs: how stiff they are (the stiffer, the faster the ripple runs) and how much the air holds them back. |
CARRY | script.js | [380, 0.72] | The spring that carries the knob when no hand holds it: how stiff it is and how much it is held back. |
SWELL | script.js | 0.8 | How far a squeezed fold stands out above and below, and how far a stretched one slims. |
RUBBER | script.js | 14 | How far the knob gives when pulled past its two ends, in pixels. |
For the zoom of a camera, a photo editor or a map; for a focal length, a distance, the length of something that really stretches. It suits places where photography and old tools belong: camera shops, film labs, photographers’ portfolios, film apps.
The slider takes the width of the screen minus the margins (358 pixels at 390) and stays 60 pixels tall; the pleats are the same, closer together. A finger drags it sideways while the page still scrolls up and down (touch-action: pan-y), and a tap on the rail brings the knob there.
No ripples, no rubber and no opening when the page loads: the pleats are always evenly spaced, the knob follows the finger, jumps where you press and goes straight to the value chosen with the keys. The light on the faces, which changes with the zoom, and the widening number stay.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.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.