
The water button
Buttons, SVG, Physics, Hover
NewFree
The controls of a slideshow, with a tiny projector in every button: the tooltip is a frosted screen that rises out of the bar into the beam and catches the projected name, which comes into focus as it rises. Going to another button the names stay where the lenses throw them: the old lamp dies with a red tail, the new one lights and the screen slides from one beam to the next.
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 projected tooltip</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/radio-canada-big@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the projected tooltip: the controls of a slideshow, and in every button a tiny projector with a filament
lamp. The tooltip is a frosted screen that rises out of the bar into the beam and catches the name the button
projects; going on to another button, the screen slides out of one beam into the next. The beams are drawn
on the svg, the pictures on the screen by the script, from the names and the keys of the buttons -->
<div class="booth" data-booth data-autoclic>
<svg class="beams" aria-hidden="true" data-beams></svg>
<div class="screen" role="tooltip" id="slideshow-tip" data-screen>
<div class="screen__light" aria-hidden="true" data-light></div>
<span class="screen__text" data-tip-text></span>
</div>
<div class="bar" role="toolbar" aria-label="Slideshow controls" data-bar>
<button class="tool" type="button" data-tool data-key="P">
<span class="tool__name">Previous slide</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6 8.5 12l6 6"/></svg>
</button>
<button class="tool" type="button" data-tool data-key="N">
<span class="tool__name">Next slide</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m9.5 6 6 6-6 6"/></svg>
</button>
<span class="bar__split" aria-hidden="true"></span>
<button class="tool" type="button" data-tool data-key="D" data-toggle="draw" aria-pressed="false">
<span class="tool__name">Pen</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 19.2 6.1 15 15.9 5.2a1.9 1.9 0 0 1 2.7 0l.2.2a1.9 1.9 0 0 1 0 2.7L9 17.9z"/><path d="m14.2 6.9 2.9 2.9"/></svg>
</button>
<button class="tool" type="button" data-tool data-key="L" data-toggle="draw" aria-pressed="false">
<span class="tool__name">Laser pointer</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8.6 8.2 18.8 12.6l-4.4 1.5-1.6 4.4z"/><path d="M5.6 5.6 3.9 3.9M8.5 4.6V2.8M4.6 8.5H2.8"/></svg>
</button>
<span class="bar__split" aria-hidden="true"></span>
<button class="tool" type="button" data-tool data-key="B" data-toggle="black" aria-pressed="false">
<span class="tool__name">Black screen</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5.8h16v10.4H4z"/><path d="M9 19.6h6M5.2 4.4l13.6 13.2"/></svg>
</button>
<button class="tool" type="button" data-tool data-key="V">
<span class="tool__name">Presenter view</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.8 5.5h16.4v11H3.8z"/><path d="M13.6 5.5v11M6.6 9h4.2M6.6 12h3M9.5 19.8h5"/></svg>
</button>
<span class="bar__split" aria-hidden="true"></span>
<button class="tool" type="button" data-tool data-key="Esc">
<span class="tool__name">End show</span>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/></svg>
</button>
</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.
32 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 projected tooltip” from Animorio and fit it into my siteThe temperature of each filament, as a part of KELVIN, follows FILAMENT·dθ/dt = power − θ⁴: the energy coming in and the energy going out as radiation, which goes with the fourth power of the temperature. This is why the lamp lights in a flash and dies with a long tail: in 50 milliseconds it is at 1900 K, orange, and the red still shows for another tenth of a second. It is worked out on GSAP's ticker in steps of 2 milliseconds; when the filaments are steady and the springs still, nothing is drawn again.
The colour and brightness of each temperature come from Planck's law summed over the visible light every 5 nanometres against the eye's CIE 1931 functions (in the gaussian form by Wyman, Sloan and Shirley), then taken into sRGB and balanced as an eye used to a slightly hotter lamp sees them: a table every 10 kelvin, made when the page opens. The brightness is seen as an eye in the dark sees it, on a logarithmic scale, so the red tail, which gives less than a hundredth of the light, stays visible.
Every button has its slide: the name and the key in black on a soft light, wider than the screen with edges that fade (mask-image). At a height h above the lens it is h/THROW as large, as bright as (THROW/h)² and blurred by the circle of confusion APERTURE·|h − THROW|/THROW. The picture is where the lens throws it, above its button, shifted by the lens if it would leave the page: it is the screen that moves under the pictures, not the pictures that travel.
The slides sit in an isolated group, with mix-blend-mode: plus-lighter, so where two fall together their light adds up and the letters of one are lit by the other; the group is cut to the screen's shape, and over it lies the grain of the acrylic (feTurbulence) in multiply, which shows mostly where there is light. The screen's edge takes the colour of the light striking it, and a little light spills around it.
Each beam is an svg trapezium from the slit to the width of the picture and beyond, with a gradient of its lamp's colour and a haze that stays put (feTurbulence inside the filter), so a beam that moves always crosses the same air. The screen stops the rays that meet it: its shadow is the fan leaving the lens through its two lower corners, a mask for each lamp, and above the screen the beam goes on only where the screen is not.
The first time after DELAY seconds of the pointer resting; while the screen is up, and for GRACE seconds after, the new name comes at once. Leaving the bar it waits LINGER seconds, so the pointer can go up onto the screen. Between a lamp going out and the next one lighting there are 80 milliseconds, as in the dissolve between two projectors: the screen crosses the dark and takes the new light as it arrives. The screen is a role tooltip and the button's description (aria-describedby): it says the key, which the button also has in aria-keyshortcuts. A click from a script (the library's preview) is a pretend pointer that passes over three buttons, leaves, then presses two.
| Name | File | Default | What it changes |
|---|---|---|---|
DELAY | script.js | 0.5 | The seconds the pointer rests on a button before the first tooltip. |
GRACE | script.js | 0.6 | The seconds after the screen has gone down in which the next name comes without waiting. |
LINGER | script.js | 0.15 | The seconds the tooltip waits after the pointer has left the bar, so it can be reached. |
KELVIN | script.js | 3100 | How hot the burning filaments are, in kelvin: lower gives a warmer, dimmer light. |
FILAMENT | script.js | 0.06 | How quickly a filament heats and cools, in seconds: the red tail lasts about three times this. |
THROW | script.js | 54 | How high above the lens the screen stands, in pixels: the picture is sharp there. |
APERTURE | script.js | 9 | How wide the lens is: how blurred the picture gets away from that height. |
SPRING | script.js | [300, 0.82] | The screen's spring as it slides into another beam: how stiff it is and how much it is held back. |
HAZE | script.js | 0.24 | How much the beams show in the air. |
data-key | index.html | P, N, D, L, B, V, Esc | Each button's key, written next to its projected name. |
For the toolbars of an app on a dark background: the controls of a slideshow or a video player, the interfaces of a cinema, a theatre, a concert hall or a photography app, anywhere a button made only of a drawing needs its name and the dark is part of the mood.
The buttons go down to 39 pixels so the bar fits the screen. A touch lights the button's lamp at once, without waiting, and presses it; keeping the finger down it can slide along the bar and the screen follows from beam to beam. The name goes by itself two seconds later, or with a touch somewhere else. The names of the buttons near the edges are shifted by the lens to stay inside the screen, and their beam leans.
No rise, no slide, no red tail: the screen shows already in place with a fade while the lamp lights and the beam brightens; changing button the screen is at once above the new one and the new light comes in with a fade; leaving, everything fades out. When the page opens the lamps are not tried.
GSAP 3.15.0, Radio Canada Big (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 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.