
The needle gauge
Counters, Physics, SVG
NewFree
A travel desk’s weekends in the cabins of a steel Ferris wheel: the wheel is dragged round its hub, runs on and snaps on the cabin at the bottom, which opens a little to show its dates and price. Every cabin hangs from its pin like a real pendulum: it lags when the wheel speeds up, swings past when it brakes, and settles.
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 Ferris wheel carousel</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/figtree@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the weekends of a travel desk, each in a cabin of a Ferris wheel. The wheel turns with the arrows, the keys,
the mouse or a finger, runs on and snaps on the cabin at the bottom, which opens a little to show the dates.
Every cabin hangs from its pin like a real pendulum: it lags when the wheel speeds up, swings past when it
brakes and settles. The wheel and the cabins are drawn by the script -->
<section class="ride" role="region" aria-roledescription="carousel" aria-labelledby="ride-title" data-ride>
<h2 class="ride__title" id="ride-title">Autumn weekends away</h2>
<div class="ride__view" tabindex="0" aria-describedby="ride-help" data-view>
<svg class="ride__wheel" aria-hidden="true" data-wheel></svg>
<div class="ride__cabins" data-cabins></div>
<button class="turn turn--prev" type="button" aria-label="Previous weekend" data-prev>
<svg viewBox="0 0 24 24" aria-hidden="true">
<g transform="matrix(-1 0 0 1 24 0)"><path d="M5.4 9.6A7 7 0 1 1 18.1 15.5"/><path d="M21.7 13.8 18.1 15.5 17.7 11.5"/></g>
</svg>
</button>
<button class="turn turn--next" type="button" aria-label="Next weekend" data-next data-autoclic>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5.4 9.6A7 7 0 1 1 18.1 15.5"/><path d="M21.7 13.8 18.1 15.5 17.7 11.5"/>
</svg>
</button>
</div>
<p class="sr" id="ride-help">Drag the wheel to turn it, or use the arrow keys. The cabin at the bottom is the weekend chosen.</p>
<p class="sr" 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.
51 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 Ferris wheel carousel” from Animorio and fit it into my siteThe wheel is a single number, p: the cabin at the bottom as a fraction. Cabin i’s pin sits at the angle b = (i − p)·2π/N from the bottom, that is at centre + R·(sin b, cos b). A spring takes p to the chosen cabin, a = ω²·(goal − p) − 2ζω·v, with ω and ζ from GLIDE (ζ under 1, so it goes a touch past); the stiffness is divided by the square root of the distance, so half a turn starts with the same push as one step.
From p, its speed and its acceleration (the one the spring works out, smooth, not the difference between two frames) comes each pin’s acceleration in pixels: the tangential one, R·b″ along the wheel, and the one towards the hub, R·b′². It is multiplied by PUSH and held under two g with a hyperbolic tangent, so a hard throw does not make the cabins loop the loop.
Every cabin is a pendulum: L·φ″ = −g·sin φ − ax·cos φ + ay·sin φ, plus the air’s damping (HOLD) and a touch of dry friction in the pin, which stops the last small swings instead of letting them fade for ever. g comes from SWING: g = (2π / SWING)²·L. L runs from the pin to the middle of the cabin and grows when the cabin opens; at the top the pull towards the hub takes a little gravity away and the cabins swing more slowly, like real ones. It is integrated every frame with a step of 1/30 s at most, cut into slices of 4 ms.
With a finger or the mouse what counts is the pointer’s angle round the hub (the change of atan2 is (y·dx − x·dy) / r², with r never under half the radius, where the angle gets shaky), and the wheel chases it on a stiff spring. Let go at more than SNAP cabins a second it runs free and slows down with a = −FRICTION·v; under SNAP the magnet picks the cabin where it would have stopped, p + v / FRICTION, and gets there on the spring: the final snap sets the cabins swinging.
Each cabin has its own number f, from 0 closed to 1 open, on a spring that bounces just a little; the CSS works out the width, the height, the window, the name and the line with dates and price from it with calc() and the --f variable. Only the cabin at the bottom opens, and only when the wheel has nearly stopped, so the cabins passing by do not swell.
The wheel is an SVG redrawn by the script when the width changes: spokes laced in two families, the truss, the pin brackets, a hub with a radial gradient, the A-frame legs; the wheel’s group turns with a single transform attribute, while the light on the rim stays put like the lamp. The radius shrinks so the cabins at the sides fit; with six cabins two neighbours never touch in any position. When everything is still the loop switches off.
| Name | File | Default | What it changes |
|---|---|---|---|
SWING | script.js | 1.25 | The seconds a cabin takes to swing there and back. |
HOLD | script.js | 0.14 | How much the air holds a swinging cabin back: 0 it hardly stops (only the pin’s friction is left). |
PUSH | script.js | 0.38 | How much the cabins feel the wheel speeding up and braking: 0 not at all. |
GLIDE | script.js | [4.6, 0.62] | The wheel’s spring towards a cabin: how quickly it gets there and how much it is held back (under 1 it goes a little past). |
FRICTION | script.js | 1.5 | How quickly a thrown wheel slows down before it snaps on a cabin. |
SNAP | script.js | 1.1 | The speed, in cabins a second, under which a running wheel snaps on the nearest cabin. |
WEEKENDS | script.js | [['Dune Camp', 'Oct 16 – 18', '€540', '#b9781f', 'dunes'], …] | The weekends: name, dates, price from, the cabin’s colour, the landscape in its window (dunes, lighthouse, lake, glacier, port, lagoon). Made for six cabins: with more the wheel packs them tighter and two neighbours may brush. |
--wheel | style.css | 128px | The wheel’s radius at most (on a phone it shrinks by itself). |
--open-w | style.css | 128px | The width of the open cabin (106 on a phone). |
For a travel agency’s destinations or experiences, a festival’s weekends, a venue’s nights, a hotel’s rooms, a school’s trips: wherever each thing has a picture and a name, there are only a few (six or seven) and the play of the wheel makes people want to turn it.
The wheel shrinks to a 128 pixel radius and the cabins to 64 by 84 (106 by 140 the open one), so the cabins at the sides stay on the screen with room to swing. A finger drags the wheel sideways round its hub and throws it (up and down scrolls the page), a tap on a cabin brings it down and the buttons are 42 pixels.
The wheel does not turn and the cabins do not swing: at each change the wheel fades out and back in with the new cabin already at the bottom and already open, the cabins upright. Dragging is a swipe: one cabin the way the hand went. The mouse over a cabin only brightens it. The component comes in with a fade only.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Figtree (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 14 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.