
The layered button
Buttons, Hover, Physics
Free
The Skip intro button of a video player, made as a little ceramic dish of clear water over sand, with the sun's net of light on the bottom. Pressed, a flat stone flies in from the left and skips: five or six hops, each shorter than the one before, each with its own rings spreading and setting the light dancing on the sand; then it sinks, stays on the bottom and the words turn to Intro skipped.
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 skipping stone button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/figtree@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the skipping stone button: the Skip intro button of a video player, made as a little ceramic dish of clear,
sunlit water over pale sand. Pressed, a flat stone flies in from the left and skips across the water, every
hop shorter than the one before, then sinks to the bottom and stays there. The water is drawn on the first
canvas, the stone and the splashes on the second; the words stay on top -->
<div class="skip" data-skip>
<button class="skip__dish" type="button" data-dish data-autoclic>
<canvas class="skip__water" aria-hidden="true" data-water></canvas>
<canvas class="skip__air" aria-hidden="true" data-air></canvas>
<span class="skip__label" data-label>
<span class="skip__words" data-words>Skip intro</span>
<span class="skip__icons" aria-hidden="true" data-icons>
<svg class="skip__icon" viewBox="0 0 24 24" data-skip-icon>
<path class="skip__tri" d="M6.4 6.6v10.8c0 .9 1 1.4 1.7.9l7.7-5.4c.6-.4.6-1.4 0-1.8L8.1 5.7c-.7-.5-1.7 0-1.7.9z"/>
<path d="M18.8 5.8v12.4"/>
</svg>
<svg class="skip__icon skip__icon--done" viewBox="0 0 24 24" data-check>
<path d="M5 12.6l4.4 4.3L19.2 7.2" data-tick/>
</svg>
</span>
</span>
</button>
<p class="sr" aria-live="polite" data-live></p>
</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.
26 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 skipping stone button” from Animorio and fit it into my siteThe stone has a place in the dish's space (across, away and in height above the water), a velocity, a spin and a pitch held by a spring. It starts over the page, on the left, with the upward push worked out so that it first touches the water just inside the rim. At every touch the water takes away the same amount of energy, LOSS times that of the throw (THROW), and throws it up again in proportion to the speed it has left (LIFT): so every hop is shorter than the one before by about the same length, as in the simple model physicists use for stone skipping. When too little speed is left it ploughs the water, stops and sinks.
The water is a grid of heights, one cell for each pixel, moved by the wave equation: every cell is pulled towards its neighbours, and those towards the back count half, because at 45 degrees distances away from us look shortened by sin 45; so the rings come out oval by themselves. Against the wall a cell uses itself as a neighbour, and the wave comes back. At every step the wave loses a little of its height (CALM) and a touch of viscosity dims the finest ripples first; the steps a second grow with the speed of the waves (WAVE), so the sums stay stable. A touch is a bell-shaped dent, as deep as the blow.
Every pixel of the water is worked out as a ray: from the pixel comes the point of the surface (on screen 4 pixels lower than the rim, which is why the dry wall shows at the far side), the slope there bends the eye's ray (BEND) and sends it onto the sand a little farther on, or onto the wall under the water. The sand's colour, lit, goes through the water: --water says how much of each colour gets through, --glow how much light the water adds by itself. On top, a veil of sky that grows on the slopes turned away (Fresnel) and the glints where the surface sends the sun to the eye. The picture is made at two pixels for each css pixel, with the slopes interpolated.
The net of light on the sand is real: every cell of the surface sends its ray of sunlight down to the bottom, moved by its slope, and lays it on the sand between the four nearest cells; where many rays land together the sand is bright, where few land it is darker. At rest there is always a very faint long swell, five fixed sine waves, enough to lay a soft net; the stone's rings set it dancing in rings. The walls cast a shadow where the ray of sunlight, bent in the water, would meet them before reaching the rim: it is worked out once.
The sand is drawn once on a canvas: the ripple marks, the pebbles with their shadows, the grain. The sinking stone is drawn on the canvas above, tinted by the water, at the depth where the sand is seen; when it reaches the bottom it becomes part of the picture of the sand, so the light and the waves play over it as over the rest. STONES of them stay: the oldest fade as if covered with sand.
After every frame the highest wave is checked: when for a few frames it is under a few thousandths of a pixel and nothing is flying, the heights go back to zero, the picture at rest is drawn and the loop draws nothing more. Along the edge of the water every pixel mixes water and wall by how much of it each covers, and a pale thread makes the meniscus. On a slow device, if a frame costs more than 9 milliseconds, the water drops to one pixel for each css pixel.
| Name | File | Default | What it changes |
|---|---|---|---|
THROW | script.js | 340 | How hard the stone is thrown, in pixels a second: harder skips more times. |
LIFT | script.js | 0.46 | How much the water throws the stone up at every touch: more, higher and longer hops. |
LOSS | script.js | 0.13 | The share of the throw's energy every touch takes away: less, more hops. |
SPIN | script.js | 30 | How fast the stone spins on itself, in radians a second. |
RIPPLE | script.js | 1 | How strong the rings are where the stone touches the water. |
WAVE | script.js | 150 | How fast the rings spread, in pixels a second. |
CALM | script.js | 1 | How quickly the water settles again: more is sooner. |
CAUSTICS | script.js | 1 | How strong the net of sunlight on the sand is (0, no net). |
BEND | script.js | 1 | How much the water bends the sand seen under it. |
WAKE | script.js | 1 | How much the fingertip, the pointer, stirs the water going over it (0, not at all). |
STONES | script.js | 5 | How many stones stay on the bottom before the oldest is covered with sand. |
DONE | script.js | Intro skipped | The words once the stone has sunk. |
BACK | script.js | 1.6 | The seconds before the words go back. |
For the button that skips something in a player or a flow: Skip intro, Skip recap, Skip ad in a video or podcast player, Skip for now on the welcome screens of an app. It belongs where summer, water, travel and nature belong: documentary platforms, travel and holiday apps, sites for beach clubs, pools and campsites. It is a button to show off, one to a page, not one for a toolbar.
The dish is 60 pixels tall like the other buttons and 320 wide, or the screen minus 48; the water is worked out again at the real size. A tap throws the stone and starts a ring where the finger touches; a finger dragged across leaves its wake in the water (touch-action: pan-y, so up and down the page still scrolls). The stone comes in from the left over the page's margin, which does not scroll sideways. On a slow phone the water is drawn at one pixel for each css pixel.
No flight and no waves: at a click the new stone already lies on the bottom and the old picture of the water fades away over it; the words and the icon change in a fade, with no slide and no blur. The pointer does not stir the water, no drop falls when the page opens, and the dish pressed darkens a little instead of shrinking.
GSAP 3.15.0, Figtree (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 18 KB; the libraries: GSAP 28 KB.
Tested on 12 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.