
The inflatable button
Buttons, Physics, WebGL, 3D
NewFree
The bass slider of a speaker made with a puddle of ferrofluid on a glass shelf: under the glass a magnet follows the hand, and more bass means a stronger field, so the black liquid rises from a glossy puddle into a crown of spikes. Dragged fast it falls behind, stretches and its spikes melt and lie back; when the magnet stops it catches up and the spikes spring up again, the middle ones first. The liquid is a 3D shape drawn by a WebGL shader.
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 ferrofluid slider</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/host-grotesk@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the bass of a speaker, set with a puddle of ferrofluid on a glass shelf: a magnet under the glass goes where
the hand takes it, and the stronger its field, the spikier the fluid. The fluid is drawn by a WebGL shader on
the canvas; the glass, its film and the magnet's mark are plain HTML -->
<div class="bass" data-bass>
<div class="bass__head">
<span class="bass__label" id="bass-label">Bass</span>
<span class="bass__value" aria-hidden="true"><span class="bass__number" data-number>+9.0</span><span class="bass__unit">dB</span></span>
</div>
<div class="bass__track" role="slider" tabindex="0" aria-labelledby="bass-label" aria-orientation="horizontal" aria-valuemin="0" aria-valuemax="12" aria-valuenow="9.0" aria-valuetext="+9.0 dB" data-track data-autoclic>
<span class="bass__shelf" aria-hidden="true">
<span class="bass__top"></span>
<span class="bass__edge"></span>
</span>
<span class="bass__film" aria-hidden="true" data-film></span>
<span class="bass__mark" aria-hidden="true" data-mark></span>
<canvas class="bass__fluid" aria-hidden="true" data-canvas></canvas>
</div>
<!-- the two ends of the range: the script writes them from RANGE -->
<div class="bass__scale" aria-hidden="true">
<span data-low></span>
<span data-high></span>
</div>
</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.
27 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 ferrofluid slider” from Animorio and fit it into my siteThe liquid is not a picture: for every pixel a WebGL shader casts a ray from a camera looking down on the shelf by 14 degrees, and moves it on each time by the distance to the nearest shape until it touches it (ray marching). The shape is a squashed ellipsoid, the body of the puddle, plus a cone with hollow flanks for every spike, joined with a smooth minimum: the spikes melt into each other only at their feet (the blend narrows going up) and all of them into the body; the glass cuts it underneath. The shader skips the pixels outside the box the liquid is in.
As in real ferrofluid the spikes stand on a hexagonal grid: the middle one, a ring of 6 and one of 12 (SPIKES). Each one points along the field, away from the pole of the magnet under the glass, so they fan out. Its height is a spring chasing the one the field asks for, TALL times the field to the power of 0.85, taller in the middle; the outer spikes only rise in a stronger field. They go up on a loosely held spring (ERUPT) and overshoot; they come down fast and without bouncing, as surface tension pulls them back.
The magnet follows the hand exactly; the liquid is pulled by it with a damped spring (DRAG), worked out on GSAP's ticker in steps of 1/240 of a second with the frame capped at 1/30. The field it feels falls with the distance from the magnet, e^(−(d/REACH)²): dragged fast the liquid falls behind, stretches towards the magnet, sinks and loses its spikes, which lie back; when the magnet stops it catches up, the field comes back and the spikes rise again. At the ends of the glass a stiff spring stops it: a liquid halts at an edge and does not run over it.
Once the point is found, the normal comes from the slope of the distance; the colour is the reflection of a studio made of numbers (a round warm softbox up on the left, two tall strips, warm on the left and cool on the right, the ceiling, the white room and the table, light at the horizon), weighed with Fresnel's formula: the liquid is black head on and glossy at the sides, with a line of light down every spike. On the glass, round its foot, a soft shadow and the dark reflection of the liquid, found with a second ray. The lights' colours come from the CSS, made linear.
The slider is the whole strip, with role slider, aria-valuenow and aria-valuetext. The hand holds the magnet: past the ends it gives RUBBER pixels at most and the spikes strain and tremble, or the puddle spreads; pressed away from the puddle it runs there on a stiff spring. The arrows move the value by KEYS[0], with Shift or the Page keys by KEYS[1], Home and End go to the ends. A click from a script (detail 0, the one of the library's preview) is a pretend hand and takes the magnet far away. When nothing moves the loop stops, and with the hand held still the canvas is not drawn again.
| Name | File | Default | What it changes |
|---|---|---|---|
START_AT | script.js | 9 | The bass when the page opens, in dB. |
RANGE | script.js | [0, 12] | The lowest and the highest bass, in dB. |
STEP | script.js | 0.5 | How much the bass moves at a time. |
KEYS | script.js | [0.5, 3] | How far an arrow key moves the bass, and with Shift or the Page keys. |
SPIKES | script.js | 19 | How many spikes the liquid can grow: 7, 19 or 37. |
TALL | script.js | 46 | How tall the middle spike grows at the top of the range, in pixels. |
DRAG | script.js | [140, 0.6] | How the liquid follows the magnet: how stiff the spring is and how much it is held back. |
REACH | script.js | 26 | How far behind the magnet the liquid can fall, in pixels, before its spikes melt. |
ERUPT | script.js | [320, 0.28] | The spikes' spring: how stiff it is and how much it is held back (little, so they overshoot). |
RUBBER | script.js | 8 | How far the magnet gives when it is pulled past its two ends, in pixels. |
For the bass or the volume of a speaker or a music app, the strength of an effect, the power of a vacuum cleaner or a vibration, any force: wherever a value means «more energy» and touching it should surprise. It suits audio sites, industrial design, labs and gadgets.
The shelf takes the width of the screen minus the margins (358 pixels at 390) and the liquid keeps its size. On the strip a finger drags the magnet without scrolling the page (touch-action: none); outside it the page scrolls. At a touch the field grows as it does when the mouse presses; the spikes standing up under a passing mouse are not there on a phone.
No lag, no stretching, no bounce or trembling: the liquid always sits on the magnet with its spikes already as tall as the value asks, jumps where you press and goes straight to the value chosen with the keys; when the page opens nothing grows and the number does not count. The shape and the light stay.
GSAP 3.15.0, Host Grotesk (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 14 KB; the libraries: GSAP 28 KB.
Tested on 6 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.