Skip to content
animorio
IT
All components

The lava lamp switch

Toggles, Physics, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The night light in a smart home app, and the switch is a little lava lamp lying on its side: a glass of dark liquid with drops of wax in it, and the knob is the biggest drop. Turned on, the knob slides over to the base taking some wax along, the bulb lights the glass and the wax warms up from that side, from dark red to magenta and orange; then it rises, lets go, drifts round the glass, cools and sinks again, as in a real lamp. Off, it all cools down and settles.

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 lava lamp switch</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the night light of a smart home app, and the switch is a little lava lamp lying on its side: a tapered glass
         capsule of violet liquid with drops of wax in it, a chrome cap at the left and the base with the heater at the
         right. Turned on, the big drop that is the knob slides over to the heater dragging the wax with it, the bulb
         lights the glass and the wax warms up, rises, drifts and comes back as in a real lamp; off, it cools and sinks
         back to the cold end -->
    <div class="tile" data-tile>
      <span class="tile__glow" aria-hidden="true" data-glow></span>
      <span class="tile__text">
        <label class="tile__name" for="lava-switch">Night light</label>
        <span class="tile__room" id="lava-room">Nursery</span>
      </span>
      <button class="lamp" id="lava-switch" type="button" role="switch" aria-checked="false" aria-describedby="lava-room" data-switch data-autoclic>
        <span class="lamp__halo" aria-hidden="true" data-halo></span>
        <canvas class="lamp__wax" aria-hidden="true" data-wax></canvas>
        <canvas class="lamp__wax lamp__wax--on" aria-hidden="true" data-wax-on></canvas>
        <svg class="lamp__frame" viewBox="0 0 184 56" aria-hidden="true" focusable="false" data-frame></svg>
      </button>
    </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.

29 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 lava lamp switch” from Animorio and fit it into my site

What it does

On a black background a dark rounded tile with the words Night light and under them, in grey, Nursery; on the right the switch, 56 pixels tall: a lava lamp lying on its side. At the left a cone of chrome for the cap, at the right the wider chrome base with three vents, and in between the glass, a little wider towards the base, with a gleam along its top edge and a fainter one below. Inside is an almost black liquid and, off, the wax is cold: dark red, dull, lying still at the bottom by the cap, the knob (a big drop) and next to it a lump and two small drops. The name is grey. Under the mouse the bulb warms up a little: the base catches an orange light, the bottom of the glass glows faintly, the wax stirs lazily and the knob leans a few pixels to the right. Turned on, the knob runs to the base with a small bounce, stretching as it goes; the wax nearest to it follows and falls behind, the drops stretch out and let go. The bulb comes on: the vents and the edge of the base turn orange, the glass lights up from the base leftwards and the liquid warms to violet; a warm halo comes up round the lamp, the tile catches its light and the name turns white. The wax by the base warms up, from dark red to magenta, then to orange with an almost yellow heart; warm, it is lighter than the liquid, so it lets go and rises, drifts left along the top of the glass, cools, turns magenta again and sinks, and comes back along the bottom to the base: the slow loop of a real lamp, never twice the same. Drops that touch melt into a single shape and part again with a string that thins out. Turned off, the knob goes back to the cap, the bulb goes out, the liquid cools in a few seconds and the wax, cold and heavy, sinks to the bottom and stops. The knob can be dragged with a finger or the mouse too, and a click on the name turns it on and off.

How it works

A real switch

It is a button with role switch and aria-checked, with its own label for (Night light) and aria-describedby on the room: Enter and Space turn it on and off, the right arrow turns it on and the left one off, and so does a click on the name. Inside are the halo, the canvas of the glass (liquid and wax) and over it an SVG the script draws once, with the gleams on the glass, the chrome cap and base and the vents, which light up through their opacity.

Every drop is a warm mass

The knob and the drops are bodies with a position, a speed, a radius and a temperature. The heat of the liquid at a point comes from the bulb (falling off as e^(−distance/30) from the base, stronger at the bottom) plus the warmth of the liquid itself; each drop takes it at a rate that goes as 1/r, so small ones warm first. Its weight in the liquid is GRAV·(0.4 − T): cold it sinks, warm it rises. The liquid holds it back as in Stokes’ law (less for big drops) and carries it along; the running knob drags the liquid round it a little. Four steps a frame on gsap.ticker, with the time step held to 1/30 of a second.

The liquid turns

As it warms, the liquid turns in a single convection roll, from a stream function sin(πX)·sin(πY) that breathes slowly: up at the base, left along the top, down at the cap and back along the bottom. Warm wax rises, goes left, cools where the bulb does not reach and sinks: the lamp’s loop comes by itself out of heat and weight, it is not a path written down.

Sticky wax

Drops near one another pull together, more the colder they are (warm wax is runnier and lets go), and share part of their speed, like a thick liquid. A position constraint keeps them from going into each other further than a skin: cold, they pack into a lump; warm, they stay round. The knob pulls from further away, but drops pass through it: running, it does not push the wax ahead of it, it goes through it and draws it along.

Metaballs on a canvas

Every frame each drop adds a bump (1 − d²/R²)³ to the field, with R = GOO·r, the knob an ellipse that spreads when pressed and stretches as it runs; a second field weighs the temperature. The wax is where the field is over the threshold of a single drop at its radius; the edge is placed to a fraction of a pixel by dividing by the field’s slope, so it is sharp with no filter. The same slope gives the normal of a dome: light from above, orange light from the base on its side, a glint, and thick wax glowing from inside. The colour comes from a table of 256 temperatures between the four colours in :root.

Light and rest

The heat of the bulb and the warmth of the liquid rise and fall with two different times; the first drives the halo, the light on the tile, the vents, the edge of the base and the name, written only when they change. Before it shows, the lamp is worked out for a few seconds, so the wax is already where it would be. With the lamp off and still, the loop stops working out and drawing until it is touched.

Values to change

NameFileDefaultWhat it changes
HEATscript.js1How hot the bulb runs when the lamp is on: how much the wax rises and moves.
PREHEATscript.js0.3How much the bulb warms the glass under the mouse, as a share of HEAT (0, not at all).
WAXscript.js9How many drops of wax float in the lamp, besides the knob.
VISCOSITYscript.js1How thick the liquid is: higher, the wax drifts slower and stops sooner.
STICKscript.js1How hard the drops of wax pull on one another and on the knob.
GOOscript.js2How far each drop’s field reaches, in its own radii: higher, drops melt together sooner and strings are longer.
FLOWscript.js34How fast the warm liquid turns round the glass, in pixels a second.
WARMUPscript.js1.4The seconds the liquid takes to warm through (a little longer to cool).
SPRINGscript.js110How hard the knob is pulled to its end of the lamp.
PEEKscript.js4How many pixels the knob leans towards the other end under the mouse.

Try them in the Playground

When to use it

For the switch of a mood light: a night light, a bedside lamp, the lights of a living room or a bar in a smart home app, an evening or focus mode in a music or meditation app. The lamp says on and off by itself even without colour (the wax lying at the bottom or drifting round), and when on it never keeps still, so it is best used for a single switch, next to plain switches.

On phones

The tile is 380 pixels wide and shrinks to the screen (358 at 390 pixels), the lamp stays 184 by 56. A tap turns it on and off; a finger dragged sideways carries the knob and the wax, while up and down the page still scrolls (touch-action: pan-y). No leaning under the mouse, which a finger does not have. The field is worked out at the screen’s density, at most 2: about 25 thousand pixels a frame.

With reduced motion

No movement: the lamp cold and the lamp lit are worked out once while the page opens and drawn on two canvases, and turning it on fades the second in over the first in just under half a second (turning it off fades it out); the halo, the light on the tile, the vents and the name change with it in a fade. No drop moves and the knob is not dragged; clicks, taps and keys work as always.

Pitfalls

  • The knob, bumping into the wax, pushed all of it ahead: the cold pool lay right in its way and in a glass this low the drops have no room to step aside, so turning it on brought a single lump to the base. Now the skin between the knob and the drops is much thinner and the constraint never moves the knob: it goes through the wax, which closes behind it, and draws only a little of it along with its pull.
  • The knob’s wake was too strong as well: liquid dragged at 400 pixels a second took every drop away in a tenth of a second. Now the wake is 14 per cent of its speed within about 15 pixels and the liquid holds harder (Stokes), so the wax falls behind and arrives later.
  • Lit, the warm wax piled up in the top corner by the base: there the convection roll, sin(πX), hardly moves, and the pull between the drops held them together. The flow along the glass keeps 60 per cent of its strength at the ends too, and warm wax sticks much less than cold wax: now it lets go and goes round.
  • Cold, the drops lay in a row along the bottom like a caterpillar, with a glint on every hump. The skin now depends on the temperature: cold wax packs into a lump, warm wax keeps its drops round.
  • The edge of the metaballs made with a hard threshold was jagged, and with a fixed smoothstep it was blurred on big drops and jagged on small ones. The edge is worked out as (field − threshold) over the field’s slope: that is the distance from the edge in pixels, and every drop gets exactly one pixel of blend.

Libraries

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 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