Skip to content
animorio
IT
All components

The haptics switch

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

The Haptic feedback switch of a phone’s settings, and the track is a Chladni plate: black steel with a film of sand on it. Turned on, the knob buzzes like the phone and the plate rings: in a second the sand runs from where the plate moves, gathers where it stays still and draws one of its figures. Turned off, the knob knocks the end and the sand jumps and lands scattered.

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 haptics switch</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/schibsted-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the Haptic feedback switch of a phone's settings, and the track is a Chladni plate: a slab of black steel with
         a little sand scattered on it. Turned on, the plate buzzes the way the phone would, and the sand runs off
         where the plate moves and gathers where it stays still, drawing one of the plate's figures; turned off, the
         knob knocks the end of the track and the sand jumps and lands scattered -->
    <div class="hap" data-hap>
      <label class="hap__label" for="hap-switch" data-label>Haptic feedback</label>
      <button class="hap__switch" id="hap-switch" type="button" role="switch" aria-checked="false" data-switch data-autoclic>
        <span class="hap__plate" aria-hidden="true"></span>
        <span class="hap__plate hap__fill" aria-hidden="true" data-fill></span>
        <canvas class="hap__sand" aria-hidden="true" data-sand></canvas>
        <canvas class="hap__sand hap__was" aria-hidden="true" data-was></canvas>
        <span class="hap__shade" aria-hidden="true"></span>
        <span class="hap__knob" aria-hidden="true" data-knob>
          <span class="hap__puck" data-puck></span>
        </span>
      </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.

21 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 haptics switch” from Animorio and fit it into my site

What it does

On a white background the words Haptic feedback and next to them a pill switch of satin black steel with a big white knob, and on the plate a film of ivory sand, scattered at random. As the page opens the sand is poured onto the plate from left to right, grain by grain. Turned on, the knob snaps to the other end and bounces a little, the plate turns cobalt blue and the knob buzzes: the sand trembles where the plate moves and slides towards where it stays still, and in under a second it draws a Chladni figure, an oval in the middle with two arcs on each side. Along the rim of the knob, which is the motor, the plate shakes hardest: no line stays there and the sand is thrown off. Then the buzz stops and the figure stays. Turned off, the knob runs back and uncovers the figure that went on under it, the plate turns black again and the knob knocks the end: the knock runs through the plate as a wave and the sand jumps, first by the knob and then farther and farther, and lands scattered and even. Dragging the knob with a finger or the mouse, the plate rings at other frequencies: a third of the way the sand draws three waves, two thirds of the way an oval cut in two, at the end the figure of on. Under the mouse the knob leans towards the other side; pressed, it sinks a little. A click on the words turns it on and off as well.

How it works

A real switch

It is a button with role switch and aria-checked, with its own label for: Enter and Space turn it on and off, so does a click on the words, and its name for screen readers is the words. Inside are the plate twice, off and on (the second one’s opacity follows the knob along the track), two canvases for the sand (the second one is for the reduced motion fade), a layer with the inner shadow, and the knob: an element that runs and buzzes, and inside it a disc that squashes against the end and sinks under a finger.

The ringing plate

The plate rings as two of its modes added together, cos(n π x) cos(m π y) plus MIX times the second one, on the free part of the plate, the part left of the knob when it is on: it is a Chladni figure. Every figure is worked out once only on a one pixel grid, with its exact slopes, normalised to 1; then it is read between the pixels with bilinear interpolation. Where it is zero lies a nodal line: there the plate does not move.

The sand

3200 grains (GRAINS). On every frame each one takes a random jump KICK times as big as the plate moves under it (never less than LINE, which gives the lines their thickness) and slides down the slope of u², towards the nearest line: it is a random walk whose step vanishes on the lines, and the sand comes to rest there by itself. The jump grows with the square root of the time step and the slide with the step, so the speed does not depend on the screen’s hertz. Along the rim of the knob the plate shakes the most and no line stays there; with the knob still at an end the sand is thrown off the rim, and the sand in the strip behind it slips under it. Three batches of little squares are drawn in three tones, over their shadows, and they tremble in the drawing as much as the plate under them.

The knock

At the off end the knob’s speed as it arrives sets the strength of the knock, which runs through the plate at 650 pixels a second: every grain it reaches jumps along a parabola (in the air it is drawn higher and bigger, and its shadow stays on the plate) and lands the farther the nearer it is. A hard knock also evens the sand out along the track again: the grains, in their order along x, take one by one the places, in order, of freshly scattered sand, the optimal transport in one dimension. Whatever goes past the edge bounces back in.

The knob and the hand

The knob is a mass on a spring (SPRING, damped below critical) with two walls: it bounces (BOUNCE) and squashes on the wall’s side. Dragged past 5 pixels it captures the pointer and follows the hand on a stiffer spring, and the plate buzzes at a frequency that depends on where it is: the three figures each have a resonance along the track, at 0.36, at 0.68 and at the end, and their weight is a Lorentz curve. Let go, a fast flick decides by its direction, otherwise the half it is in, and the click that follows is ignored. The focus ring shows for the keyboard, not after a click.

Values to change

NameFileDefaultWhat it changes
GRAINSscript.js3200The grains of sand on the plate.
MODESscript.js[4, 2, 6, 0]The figure the sand draws: two ways the plate rings, each as waves across and up.
MIXscript.js0.65How much of the second way rings with the first: below zero the figure turns inside out.
BUZZscript.js1.2The seconds the plate buzzes once it is turned on.
KICKscript.js2.6How far the buzz throws a grain where the plate moves most, in pixels a frame.
LINEscript.js0.07How thick the lines of sand are: the little shake left where the plate stays still.
KNOCKscript.js14How far the knock at the off end throws the sand, in pixels.
SPRINGscript.js170How hard the spring pulls the knob to its end of the track.
BOUNCEscript.js0.3How much of its speed the knob keeps bouncing off the end (0, no bounce).
PEEKscript.js4How far the knob leans towards the other side under the mouse, in pixels.

Try them in the Playground

When to use it

For a setting you feel under your fingers: the vibration and haptic feedback of a phone, a watch or a controller, keyboard clicks, a metronome. The sand falling into order says by itself what the switch does. In a list of settings it is best used for one entry only, next to plain switches.

On phones

Same size as on a computer, 168 by 56 pixels: at 390 pixels the words and the switch still sit on one line, and on a narrower screen the words go above the switch. A tap turns it on and off; a finger dragged sideways takes the knob along and makes the plate ring, while up and down the page still scrolls (touch-action: pan-y). No leaning under the mouse, which a finger does not have. The sand is drawn at the screen’s density (up to 2) and worked out only while something moves.

With reduced motion

The sand is a still picture: scattered when off, the figure already drawn when on (worked out ahead without drawing it). At a click the old picture fades away over the new one in three tenths of a second, the colour of the plate changes in a fade and the knob fades out at one end and back in at the other. No sand poured as the page opens, no buzz and no knock; pressed, it does not sink and cannot be dragged; clicks and keys work as always.

Pitfalls

  • After a few rounds of on and off all the sand sat in the left two thirds: the figure pulls it there and the knock only moved it a few pixels. Now a hard knock takes the grains, in their order along the track, to the places of freshly scattered sand: at every off the sand is even again, even after twelve rounds of the preview.
  • A line of sand along the rim of the knob, at the end of the track, was a ring round it, the very dot with a ring we do not want: along the rim the plate shakes the most and no line stays there, and with the knob still at the end the sand in the strip behind it slips under it.
  • Clearing the whole area under the knob left, when it was turned off, a hole as big as the knob and a line too heavy beside it; dragging it, the sand pushed away left an empty wake and the figure came out with half an oval missing. Now only the rim shakes, under the knob the figure goes on (and shows when the knob leaves), and on the move under the hand the knob does not push the sand.
  • Arriving at the on end gave a knock too: the grains in the air did not feel the buzz and the figure started half a second later. Now only the off end knocks.
  • With MIX at 1 the figure touched zero in single points, at the left tip and at the top of the oval, and there the sand made round lumps like dots: with MIX at 0.65 the oval closes and floats in the middle.
  • With a finger the drag stopped after a few pixels: a touch captures the pointer on the element touched, the plate inside the button, and when the capture was handed to the button the plate got lostpointercapture, which bubbles up to the button and ended the drag. Now the plate does not take the pointer and only a capture lost by the button itself counts.
  • The second knock of the bounce aimed the grains still in the air at where they were in flight, and the evened out sand stopped halfway: now a grain in the air keeps its landing place and only gets a little scatter. Grains thrown past the edge piled up along the outline of the plate: now they bounce back in.

Libraries

GSAP 3.15.0, Schibsted Grotesk (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