
The copy button
Buttons, Text, SVG, Physics
NewFree
The tuning knob of a radio: heavy, it turns round and round and, flung, keeps spinning and slows down while the red needle runs along the backlit scale. Between stations the screen is all snow; near a station the knob is pulled in as if by a magnet, the noise clears, the scale's light warms from a cold grey to amber and the station's name comes together letter by letter.
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 tuning knob</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the tuning knob of a radio: a heavy knurled knob with a finger dimple, a backlit glass scale with the stations
printed on it and a red needle, and a small screen that shows the static between stations. The knob coasts
when flung, a station pulls it in like a magnet, the static clears and the light of the scale warms. The
scale, the screen and the knob are drawn by the script -->
<div class="wrap">
<section class="tuner" aria-label="Radio tuner" data-tuner data-autoclic>
<div class="scale" data-scale>
<canvas class="scale__canvas" aria-hidden="true" data-scale-canvas></canvas>
<div class="scale__marks" aria-hidden="true" data-marks></div>
</div>
<div class="tuner__low">
<div class="panel">
<div class="screen" aria-hidden="true" data-screen>
<canvas class="screen__noise" data-static></canvas>
<p class="screen__name" data-name></p>
</div>
<div class="read" aria-hidden="true">
<p class="read__freq"><span data-freq>87.5</span><span class="read__unit">MHz</span></p>
<svg class="read__bars" viewBox="0 0 23 14" data-bars>
<rect x="0" y="10" width="3" height="4" rx="0.8" data-bar/>
<rect x="5" y="7.5" width="3" height="6.5" rx="0.8" data-bar/>
<rect x="10" y="5" width="3" height="9" rx="0.8" data-bar/>
<rect x="15" y="2.5" width="3" height="11.5" rx="0.8" data-bar/>
<rect x="20" y="0" width="3" height="14" rx="0.8" data-bar/>
</svg>
<p class="read__line" data-line>No station</p>
</div>
</div>
<div class="knob" role="slider" tabindex="0" aria-label="Tuning" aria-valuemin="87.5" aria-valuemax="108" aria-valuenow="87.5" data-knob>
<canvas class="knob__canvas" aria-hidden="true" data-knob-canvas></canvas>
</div>
</div>
<p class="sr" aria-live="polite" data-live></p>
</section>
</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.
48 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 tuning knob” from Animorio and fit it into my siteThe knob has an angle and a speed, and the frequency is the angle divided by TURN degrees per MHz: five turns from 87.5 to 108. The sums are done on GSAP's ticker in steps of 2 milliseconds, with the frame capped at 1/30 of a second: FRICTION takes away speed in proportion to the speed, so flung the knob slows down gently, and at both ends of the band two stiff springs stop it, giving a degree. The hand does not set the angle: it pulls it with a spring and damps the difference in speed, so when let go the knob keeps the hand's speed and carries on turning.
Every station adds a force −PULL·d·e^(−(d/W)²), where d is the distance in degrees and W is REACH in degrees: it pulls towards the station with a spring that is felt only from close by and quickly fades away. Inside the same bell there is damping, so a caught knob stops with a little swing. Arriving slowly it has not the energy to get out of the well and is caught; arriving fast it runs past, only a little braked. While the hand turns slowly, near a station the knob lags a few degrees and then snaps away: you feel the catch.
The tail of the bell never reaches zero: with nothing else, a knob let go half an MHz from a station would slowly slide into it in two seconds. So a knob that is nearly still, under a force weaker than STICK (about a tenth of the strongest pull), stops altogether: away from the stations it stays where it is left. Once still, nothing is worked out and nothing is drawn until the hand comes back.
The signal is e^(−(d/CLEAR)²), with d the distance in MHz from the nearest station, and the screen reads it 7 hundredths of a second late, so a station passed at speed barely flickers. The scale's light follows it like a bulb, quick to warm and slower to cool, and mixes the sheet's colour from --sheet-cold to --sheet-warm, with two bulbs behind at the bottom (radial gradients added with lighter) and darker edges. The screen is a canvas: the snow is worked out at every frame in an ImageData of 115 by 62 points, each point a random grey squared, a light bar rolling down and torn lines while the station is half in; it is laid out without smoothing, under the scanlines, with an opacity of (1 − signal)^1.3 · SNOW.
The name is made of spans, one per letter, and each letter has its own threshold, from 0.5 to 0.92, in a shuffled order. Just below its threshold a letter shows a random character at every frame, half transparent; above it, the real letter. So, turning towards the station, the name comes together out of order, and turning away it comes apart. The glow of the text grows with the signal.
The knob is a canvas drawn again only when it moves. Each of the rim's 72 ridges has a light face and a dark one, and their light depends on their angle on screen against the light, from the top left: turning, the ridges pass under a glint that stays put. The spun cap is a conic gradient with two streaks along the light's direction, which do not turn, while the turning marks on top do; the upper streak takes the warm colour of the scale. The dimple is a hollow: the wall away from the light is bright and the near one in shade, worked out at every position.
| Name | File | Default | What it changes |
|---|---|---|---|
STATIONS | script.js | [[88.9, 'Harbor Jazz', 'Jazz', 'Lisbon'], …] | The stations on the dial: frequency in MHz (between 87.5 and 108), name, genre (printed on the scale) and city. |
START | script.js | 100.4 | The frequency the radio is tuned to when the page opens, in MHz. |
TURN | script.js | 90 | How many degrees the knob turns for one MHz: the higher, the more turns it takes to cross the band. |
PULL | script.js | 360 | How hard a nearby station pulls the knob in (0 = no magnet). |
REACH | script.js | 0.3 | How far away a station's magnet is felt, in MHz. |
FRICTION | script.js | 1.9 | How quickly a flung knob slows down. |
CLEAR | script.js | 0.2 | How close to a station the noise clears and the light warms, in MHz. |
SNOW | script.js | 1 | How strong the snow is between stations. |
For a streaming radio or podcast app, a station's player, the site of a festival or a label with its playlists, and in general for choosing among a few channels along a continuous scale, when turning and searching is part of the fun.
The radio is 360 pixels wide (less on a narrower screen) and the knob 140. The finger turns it round and flings it without scrolling the page: touch-action none is only on the knob. Instead of the hover, a tap on a genre on the scale, or on the scale itself, takes the radio there.
No coasting and no magnets: the knob follows the hand and, let go, the radio goes to the nearest station; the arrows jump from station to station. The snow does not move, it is a still frame that fades with the signal, and the new station arrives on the screen with a fade; no entrance.
GSAP 3.15.0, Archivo (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.