Skip to content
animorio
IT
All components

The lamp dimmer

Dials, Physics, HoverGSAP, CanvasAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The dimmer of a reading lamp, lit by the lamp it dims: a knob of turned metal that has weight, clicks from notch to notch, coasts when flung until a notch catches it and turns the lamp off with a firmer click. As it turns the light fades and warms like a filament, from warm white to the amber of a candle, and with it the metal's reflections, the plate, the words and the shadow change; switched off, only the cool light of a window is left.

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 lamp dimmer</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/jost@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the dimmer of a reading lamp: a fluted metal knob on a dark wall plate, and everything on the plate is seen
         by the light of the lamp it dims. The knob clicks from notch to notch, coasts when flung and turns the lamp
         off with a firmer click; the light warms as it dims. The plate and the knob are drawn by the script -->
    <div class="dimmer" data-dimmer data-autoclic>
      <canvas class="dimmer__plate" aria-hidden="true" data-canvas></canvas>
      <div class="dimmer__knob" role="slider" tabindex="0" aria-labelledby="dimmer-name" aria-valuemin="0" aria-valuemax="100" data-knob></div>
      <div class="dimmer__read">
        <p class="dimmer__name" id="dimmer-name" data-name>Reading lamp</p>
        <p class="dimmer__warmth" aria-hidden="true" data-kelvin></p>
        <p class="dimmer__level" aria-hidden="true" data-level><span data-value></span><span class="dimmer__pct" data-pct>%</span></p>
      </div>
      <p class="sr" aria-live="polite" data-live></p>
    </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.

17 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 lamp dimmer” from Animorio and fit it into my site

What it does

On a black background a dark wall plate with rounded corners and, in the middle, a metal knob 176 pixels wide: a fluted rim, then a polished bevel and the face turned in circles, with a slot cut in it to show the level. Round it a scale is printed that widens from the first notch to the last, with Off at its start; at the bottom Reading lamp, the warmth of the light (2550 K) and the level, 70%. Everything is seen by the light of two sources: the lamp, warm, coming from the top left, which leaves a pool of light on the plate and a long shadow behind the knob; and a window, cool, always lit, from the right. When the page opens the knob is on Off and a hand turns it to 70%: the lamp comes on. It turns by its rim, going round, or from its middle, dragging up and down; the knob has weight: it holds in a notch for a moment and then clicks into the next, let go hard it keeps turning and a notch stops it with a little bounce. Each notch is 5%; below 5% there is a firmer click and the lamp goes off. As it turns, the flutes pass under the light and the glints run along the rim, while the streaks of light on the turned face stay put, as on real metal. Turning the light down the lamp warms like a filament: from warm white to the amber of a candle, and the knob, the plate, the words and the shadow take that colour; the level's figure gets thinner. Switched off, the light fades with an amber tail and only the bluish light of the window is left. With the mouse over it, the reflections slide a little on the metal, as when you move your head. The mouse wheel and the keys work too.

How it works

The notches as a force

The knob has an angle and a speed, and a force that depends on the angle acts on it: −DETENT·sin(2π·(a − a1)/pitch), which pushes towards the nearest notch and has a hump between two notches. The Off notch is wider and nearly twice as deep; past Off and past the top the stops are stiff springs that give a degree or two. The sums are done on GSAP's ticker in steps of 2 milliseconds, with the frame capped at 1/30 of a second, because stiff stops on a slow frame would blow up.

The hand is a spring

The hand does not set the angle: it pulls the knob with a spring (HAND_K) and damps the difference between its speed and the knob's. Since the notches are stiffer than the hand, the knob lags a few degrees and then clicks into the next notch: you see the click. Let go, the knob keeps its speed, FRICTION slows it down and when it no longer has the strength to get over a hump a notch stops it, with a little bounce. Flung hard downwards it stops at 5%: the Off click is firmer, and the lamp does not go off by mistake.

Round, up and down, wheel and keys

Held by the rim (past half the radius) the hand's angle is read with atan2 round the middle and the difference between two readings, brought between −180 and 180, is added up; right over the middle the angle means nothing, so it is read but not used. Held from the middle, every pixel up or down turns it by SWEEP/230 degrees. The wheel adds up its moves and asks for a notch every 40 pixels (a trackpad sends many small ones); arrows, Page Up and Down, Home and End put the hand on the right notch and let go once the knob gets there.

The filament and the colour of the light

The lamp's light follows the notch with a delay: FILAMENT[0] seconds to brighten, FILAMENT[1] to fade. Its temperature goes from WARMTH[0] to WARMTH[1] kelvin with the square root of the level, like the bulbs that warm as they dim; the colour comes from the black body (Tanner Helland's fit) in linear light, divided by the white of a 4000 K bulb, the eye's white. All colours are added in linear light and become pixels through a soft shoulder (1 − e^(−x)) and the gamma; in the dark the eye opens up a little.

The light worked out on the metal

The knob is drawn on a canvas at every frame in which something moves. Each of the 48 flutes is a hollow cut into six strips, each with its own normal: its colour is what its reflection sees, the two lights (Phong) and the room. The bevel is a conic gradient of 96 colours worked out the same way. On the turned face the grooves are circles, and the light is worked out along the groove (Kajiya-Kay): each light draws a streak across the middle that does not depend on the knob's angle, so it stays put while the turning marks, a texture on top, go round. Of the slot's two walls, the one facing a light catches it.

The plate, the shadow and the words

The paint is matt: the light of a nearby lamp falls off with the cosine and the square of the distance, and becomes a radial gradient of ten worked-out colours; on top, a satin sheen towards the lamp. The shadows are the knob cast by the two lights (two circles and the band between them), softened with shadowBlur and as dark as the share of light they take away. The printed scale and the words take the light of the spot where they are, and the level's figure goes from a weight of 300 to 560 with the lamp.

Values to change

NameFileDefaultWhat it changes
LEVELscript.js70The light when the page opens, in percent.
STEPscript.js5The percent between two clicks of the knob.
SWEEPscript.js290How many degrees the knob turns from Off to full.
WARMTHscript.js[1800, 2700]The colour of the light in kelvin, nearly off and at full: it warms as it dims.
DETENTscript.js6000How hard each notch pulls the knob in (0 = no clicks).
FRICTIONscript.js14How quickly a flung knob slows down.
FILAMENTscript.js[0.09, 0.34]The seconds the bulb takes to brighten and to fade.

Try them in the Playground

When to use it

For the app of a connected home or a hotel, a lamp's configurator, the light panel of a room or a stage, and in general for a value that is set by turning and that changes the light.

On phones

The plate is 300 pixels wide (less on a narrower screen) and the knob 176. The finger turns it from the rim going round or from the middle up and down, without scrolling the page: touch-action none is only on the knob, outside it the page scrolls. The reflections that follow the mouse are not there on a phone.

With reduced motion

The knob goes straight to its notch, with no spring, no bounce and no fling; the reflections do not follow the mouse and there is no entrance. The light only changes with a fade, the filament's.

Pitfalls

  • Seen from above a straight side reflects the floor, not the lamp: the rim is a cone whose normal rises 57 degrees and the bevel's 62, so the reflection of a lamp 32 degrees high falls in them.
  • Without an eye's white, light at 2700 K on dark paint turns khaki and the words, between warm lamp and cool window, pink: the lamp's colour is divided by that of a 4000 K bulb.
  • The canvas's shadowBlur and shadowOffset do not follow the transform: the shape is drawn far away, off the canvas, and the shadow is brought back with an offset multiplied by the scale and the pixel density.
  • The shadow is the union of two circles and the band between them in a single path: the band has to be drawn the same way round as the circles, or the nonzero fill leaves a hole.
  • With flutes made of flat faces the rim looks like a gear: each flute is a hollow of six strips, darker at the bottom.
  • atan2 jumps from 180 to −180, and near the middle the angle is only noise: it is always read, so the last reading is right, but only used beyond 12 pixels from the middle.
  • A trackpad's wheel sends many small moves: they are added up and a notch is asked for every 40 pixels. And it has to be listened to with passive: false, or the page scrolls.

Libraries

GSAP 3.15.0, Jost (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.

Testing

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