Skip to content
animorio
IT
All components

The bellows zoom

Sliders, PhysicsCanvas, GSAPAdvancedCompressed weight: Demo 8.8 KBGSAP 28 KBFreeDownload the zip

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.

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 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>

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.

27 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 bellows zoom” from Animorio and fit it into my site

What it does

On a white background the word Zoom with a small magnifier, and on the right the value, 3.2×. Below, a pale rail with a mark for every whole zoom; on the rail two dark metal plates, the left one still and the right one with the lens, and between them a dark red pleated leather bellows, like the ones on old cameras. When the page opens the closed bellows opens out to the zoom. Dragging the plate with the lens the bellows stretches or closes: pulled fast, the pleats near the hand open first, pushed they bunch up against the plate, and the ripple runs to the back plate and comes back once or twice before it stops; where the pleats are squeezed they stand out more above and below. Every face takes the light by how steep it is: closed pleats make sharp stripes of light and shadow, stretched ones go flatter and even. The number widens with the zoom, on the font’s width axis. Pressing on the rail the plate runs there and then follows the hand; past its two ends it gives a little, like rubber, and let go it comes back. The arrows, Page Up and Down, Home and End move it from the keyboard too.

How it works

The pleats are weights on springs

The 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.

The pleats never cross

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.

Light from the slope

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 swelling outline

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.

The hand, the keys and the preview

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.

Values to change

NameFileDefaultWhat it changes
START_ATscript.js3.2The zoom when the page opens.
RANGEscript.js[1, 8]The lowest and the highest zoom.
STEPscript.js0.1How much the zoom moves at a time.
KEYSscript.js[0.1, 1]How far an arrow key moves the zoom, and with Shift or the Page keys.
PLEATSscript.js18The pleats of the bellows, two faces each.
BELLOWSscript.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.
CARRYscript.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.
SWELLscript.js0.8How far a squeezed fold stands out above and below, and how far a stretched one slims.
RUBBERscript.js14How far the knob gives when pulled past its two ends, in pixels.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • With little air drag the slowest motion, the whole bellows going back and forth about once a second, lasted five seconds after every gesture, and the canvas was redrawn all that time. With stiffer springs and more drag the ripple goes round twice and stops; under a tenth of a pixel, which is not seen even on sharp screens, the folds are put back in place and the ticker stops.
  • With few pleats and the faces lit up to pink, the stretched bellows looked like a striped candy stick, not leather: more pleats, a darker colour scale, a narrow sheen and the lines on the ridges make it a bellows again.
  • With the Home and End keys the knob’s spring, which bounces just a little, took it past the end and crushed the closed bellows: now, let go, the knob does not pass the ends, it knocks against the stop and comes back a little.
  • The preview’s click went to a fixed value, which was sometimes a few tenths from the one before and showed nothing: now it always picks a zoom at least a third of the rail away.

Libraries

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.

Testing

Tested on 5 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