Skip to content
animorio
IT
All components

The tuning knob

Dials, Physics, TextGSAP, CanvasAdvancedCompressed weight: Demo 14 KBGSAP 28 KBFreeDownload the zip

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.

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

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.

48 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 tuning knob” from Animorio and fit it into my site

What it does

On a black background the front of a radio, 360 pixels wide, in a warm black with rounded corners. At the top, behind glass, the scale: a sheet lit from behind with the FM frequencies from 88 to 108 and a tick every half MHz, and under them, in two rows, the genres of the seven stations (Jazz, Soul, Ambient, Dub, City pop, Blues, Folk), each with a thin line and a small wedge at its place on the scale; between the frequencies and the names runs the red needle. Below on the left a dark screen with the station's name, the frequency in large figures (100.4 MHz), five signal bars and the genre with the city (City pop · Osaka); on the right the knob, 140 pixels wide: a dark knurled rim, a polished chamfer, a cap of spun aluminium and a dimple for the finger. When the page opens the radio is between two stations and a hand takes it to Neon Avenue. The knob turns round, with a finger or the mouse, about one turn for every four MHz; it has weight: flung, it keeps turning and slowly slows down, and the needle follows on its cord a moment behind. Every station pulls the knob like a magnet, but only from close by: passing slowly it is caught and stops there with a little swing, passing fast it runs on and only slows a little; away from the stations the knob stays where it is left. Between stations the screen is full of snow, with a light bar rolling down and a few torn lines; the scale's light is grey and cold and under the frequency it says No station. Coming near a station the snow thins out, the scale's light warms to amber, the genre on the scale turns red, the bars fill up and the name on the screen comes together: each letter arrives at its own moment, and just before it does it flickers through random letters. With the mouse over it the knob rises a little and its shadow grows longer; pressed, it sinks. A tap on a genre on the scale takes the radio to that station, a tap elsewhere on the scale to that frequency. The arrows go to the next or the previous station, Home and End to the first and the last; the mouse wheel gives the knob a push.

How it works

A knob with weight

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

Stations as magnets

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.

Stiction

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, the light and the snow

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

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 drawn with the light standing still

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.

Values to change

NameFileDefaultWhat it changes
STATIONSscript.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.
STARTscript.js100.4The frequency the radio is tuned to when the page opens, in MHz.
TURNscript.js90How many degrees the knob turns for one MHz: the higher, the more turns it takes to cross the band.
PULLscript.js360How hard a nearby station pulls the knob in (0 = no magnet).
REACHscript.js0.3How far away a station's magnet is felt, in MHz.
FRICTIONscript.js1.9How quickly a flung knob slows down.
CLEARscript.js0.2How close to a station the noise clears and the light warms, in MHz.
SNOWscript.js1How strong the snow is between stations.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • The magnet's bell never reaches zero and friction only acted on the speed: a knob let go 0.65 MHz from a station slowly slid into it in two seconds. It needs stiction: nearly still and under a small force, the knob stops.
  • The entrance started from START − 3.3 MHz, which is exactly another station (97.1): the radio opened already caught and the hand had to tear it away. Now it starts from a point between two stations.
  • With the needle along the whole scale, resting on a station it cut its name and the figures nearby in half: the figures went to the top, the names to the bottom, and the needle only runs in the band between.
  • A single gradient to darken the sheet's edges, with a stop at 12%, left two clearly visible vertical bands on the cold light: now it is a curve of seventeen stops.
  • atan2 jumps from 180 to −180, and near the middle the angle is only noise: the difference between two readings, brought between −180 and 180, is added up, and within 10 pixels of the middle it is read but not used.
  • A span holding only a space, as inline-block, has no width: the name's letters have white-space pre, or Neon Avenue would become NeonAvenue.

Libraries

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.

Testing

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