
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
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.
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>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
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 siteIt 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.
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.
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.
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.
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.
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.
| Name | File | Default | What it changes |
|---|---|---|---|
HEAT | script.js | 1 | How hot the bulb runs when the lamp is on: how much the wax rises and moves. |
PREHEAT | script.js | 0.3 | How much the bulb warms the glass under the mouse, as a share of HEAT (0, not at all). |
WAX | script.js | 9 | How many drops of wax float in the lamp, besides the knob. |
VISCOSITY | script.js | 1 | How thick the liquid is: higher, the wax drifts slower and stops sooner. |
STICK | script.js | 1 | How hard the drops of wax pull on one another and on the knob. |
GOO | script.js | 2 | How far each drop’s field reaches, in its own radii: higher, drops melt together sooner and strings are longer. |
FLOW | script.js | 34 | How fast the warm liquid turns round the glass, in pixels a second. |
WARMUP | script.js | 1.4 | The seconds the liquid takes to warm through (a little longer to cool). |
SPRING | script.js | 110 | How hard the knob is pulled to its end of the lamp. |
PEEK | script.js | 4 | How many pixels the knob leans towards the other end under the mouse. |
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.
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.
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.
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.
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.