Skip to content
animorio
IT
All components

The thermostat

Dials, Physics, SVGGSAPMediumCompressed weight: Demo 7.2 KBGSAP 28 KBFreeDownload the zip

A metal dial turned with a finger: the knurled ring follows the hand, the temperature clicks every half degree with the white mark jumping from notch to notch and the figure rolling, and the scale lights up between the room and the temperature asked for, warm when heating and cold when cooling, while the room slowly gets there.

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 thermostat</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/saira@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the thermostat: a dial turned by hand with a click every half degree; the scale lights up between the room
         and the temperature asked for, warm or cold, and the room slowly gets there -->
    <section class="thermo" aria-label="Thermostat" data-thermo data-autoclic>
      <div class="dial" role="slider" tabindex="0" aria-label="Temperature" aria-valuemin="10" aria-valuemax="30" data-dial>
        <svg class="dial__svg" viewBox="-100 -100 200 200" aria-hidden="true" data-svg></svg>
        <div class="dial__read" aria-hidden="true">
          <p class="dial__mode"><span class="roll" data-mode></span></p>
          <p class="dial__temp"><span class="roll" data-temp></span><span class="dial__deg">°</span></p>
          <p class="dial__now">Now <span class="roll" data-now></span>°</p>
        </div>
      </div>
      <div class="controls">
        <button class="step" type="button" aria-label="Half a degree less" data-less><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 12h12"/></svg></button>
        <div class="modes" role="radiogroup" aria-label="Mode" data-modes>
          <span class="modes__pill" aria-hidden="true" data-pill></span>
        </div>
        <button class="step" type="button" aria-label="Half a degree more" data-more><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 12h12M12 6v12"/></svg></button>
      </div>
      <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>

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.

16 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 thermostat” from Animorio and fit it into my site

What it does

On a black background a round dial: a knurled metal ring, a dark face with a scale of ticks over three quarters of a turn, a white mark on the temperature asked for and a small triangle on the room's; in the middle Heating, 21° and Now 19.4°; under it minus, Heat Cool Off and plus. Turning the dial with a finger or the mouse the ring goes round with the hand and every half degree the temperature clicks: the mark jumps to the next notch on a spring, the figure rolls upwards or downwards and gives a little jolt; past 10 and 30 degrees the ring gives only a little, as against a stop, and let go it goes back to its notch with a bounce. The ticks between the room and the temperature asked for light up orange when heating and light blue when cooling, and at the bottom of the face a glow of the same colour breathes; every little while the room warms or cools by a tenth, the triangle moves on and Now rolls. When the room gets there the word becomes Holding; with Off everything goes dark.

How it works

The drawn dial

The dial is an SVG with its origin in the middle: the ring has 96 grooves that turn with their group, the scale has a tick every half degree, longer every degree, spread over DEGREES degrees of turn for each degree of temperature. The mark and the triangle are groups that rotate round the middle (svgOrigin 0 0).

The hand and the clicks

Under the finger the angle is measured with atan2 round the middle and the difference, brought between −180 and 180, is added up, so going past the bottom makes no jump. The ring follows the hand without a break, while the temperature is rounded to the half degree: when it changes, the mark runs to the new notch on a spring (CLICK) worked out on GSAP's ticker and the figure rolls the way of the change.

The stop and the return

Past the ends the ring shows only a quarter of how far the hand turns, as against a stop; let go, a tween with elastic.out takes it back to the notch of the temperature asked for. The tween works on a plain object, so it has to be kept and stopped by hand when the finger grabs again.

The room and the colours

Every DRIFT seconds, while heating or cooling, the room moves a tenth towards the temperature asked for: the triangle slides and Now rolls. The ticks between the two temperatures take the colour of the CSS variable --on, which GSAP takes from orange to light blue or grey when the mode changes; the same colour tints the glow at the bottom of the face, which breathes by widening its radius.

Wheel, keys and the preview

The wheel and the minus and plus buttons change by half a degree; with the keys the dial is a slider (arrows, page up and down by two degrees, Home and End) and the modes are a group of choices. A click from a script (the library's preview) is a pretend hand that turns the dial to another temperature, and every third time goes from Heat to Cool.

Values to change

NameFileDefaultWhat it changes
RANGEscript.js[10, 30]The lowest and the highest temperature that can be asked for.
STARTscript.js[21, 19.4]The temperature asked for and the room's at the start.
DEGREESscript.js13.5How far the dial turns for one degree of temperature.
DRIFTscript.js2.5The seconds the room takes to warm up or cool down by a tenth of a degree.
CLICKscript.js[600, 0.42]The spring of the mark jumping from notch to notch: how stiff and how held back.

Try them in the Playground

When to use it

For a connected home's app, the panel of a hotel or a farm stay, a configurator, a mixer, any value set by turning a knob.

On phones

The dial is at most 320 pixels wide; the finger turns it without scrolling the page (touch-action none on the dial), and minus and plus are round 44-pixel buttons for those who prefer to tap.

With reduced motion

The figures change without rolling, the mark and the ring go straight to their places without a spring, the glow does not breathe, and no entrance.

Pitfalls

  • atan2 jumps from 180 to −180 at the bottom of the dial: add up the difference between two readings, brought between −180 and 180, never the angle read.
  • The ring and the temperature are two things: the ring follows the hand without clicks, the temperature is rounded and the mark jumps from notch to notch.
  • The tween that brings the ring back works on an object, not on the ring: killTweensOf(ring) does not stop it and it would fight the finger. Keep it and stop it by hand.
  • The wheel has to be listened to with passive: false, or preventDefault does not stop the page scrolling.
  • Ticks, glow and label take their colour from a single CSS variable (--on): GSAP changes it in one tween, and in the SVG gradient it must be read as a style (stop-color: var(--on)).

Libraries

GSAP 3.15.0, Saira (font variabile) 5.3.0.

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

Testing

Tested on 3 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

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