
Stacking certificates
Hover, Images
NewFree
The mirror of a made-up funfair, with a gilded frame and lit bulbs: inside is the reflection of a big top. The pointer swells, drags and ripples the picture, which springs back with an elastic; a click changes the curve of the glass: concave, convex, waves.
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 funhouse mirror</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<h1 class="sign" data-sign>Hall of Mirrors</h1>
<!-- the button is exactly the glass: the frame, the crest and the plaque hang outside it -->
<button class="mirror" type="button" data-mirror aria-label="Funhouse mirror: click to change the curve of the glass">
<span class="mirror__drop" data-drop>
<span class="mirror__tilt" data-tilt>
<span class="glass" data-glass>
<canvas class="glass__canvas" data-canvas></canvas>
<span class="glass__sheen" data-sheen></span>
<span class="glass__shade"></span>
</span>
<svg class="frame" data-frame aria-hidden="true"></svg>
</span>
</span>
</button>
<p class="hint" data-hint>Move over the glass, click to bend it</p>
<p class="sr" aria-live="polite" data-said></p>
</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.
21 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 funhouse mirror” from Animorio and fit it into my siteThe fair scene is painted once on a canvas nobody sees, with the 2D API: a gradient sky, stars, the moon, the title letter by letter on an arc, the wheel, the balloons, the boardwalk, the big top with its stripes clipped inside its outline. Every size is in hundredths of the width, so the picture is the same at any size. Then getImageData takes its pixels: they are the source the mirror reads.
On every frame, for every pixel of the visible canvas, the script works out which point of the picture it should read. The curve of the glass is a simple function of coordinates between minus one and one: u plus k times u times (1 minus u squared). With k positive across and negative down the middle gets narrow and tall (concave), the other way round short and wide (convex); at the edges the function is always one, so the picture never comes away from the frame. The waves are two sines, damped towards the edges, with a phase that moves on with time.
Around the pointer a Gaussian bell moves the reading towards the middle (the picture swells) or away (it squashes): one number does it, swell, which GSAP raises with elastic.out when the pointer comes in and brings to zero when it leaves, and the elastic, dipping below zero, makes the squash. The pointer's speed moves a small spring that drags the picture along, and sets off circular ripples. The sums are done only in the square around the pointer, and the reading is bilinear, so stretched parts stay smooth.
The three curves are three weights in one object: a click takes the new weight to one and the others to zero with elastic.out, so the glass overshoots the shape and comes back into it. At the same moment a ring of ripples starts from the point of the click and spreads to the edges, the frame squashes and bounces, a glint runs across the gold (a soft rectangle inside a clipPath of the frame) and the plaque closes with scaleY and opens again with the new name. A delayedCall changes the curve by itself until someone really clicks (isTrusted).
The frame is an SVG the script builds to the size of the mirror: the outer and inner arch in one path with fill-rule evenodd, a gold gradient, the bevels, the shell on top and the plaque. The bulbs sit on the middle line of the frame, lined up with getPointAtLength, skipping the ones that would fall under the shell or the plaque. They light up once in the entrance and then stay steady. The button is as big as the glass and everything inside it has pointer-events none, so the pointer always meets a single element.
| Name | File | Default | What it changes |
|---|---|---|---|
TITLE | script.js | 'Marigold Pier' | The title painted on an arc in the sky of the reflection: if it is long, the letters shrink to fit. |
CURVE | script.js | 1 | How curved the glass is: 0 is a flat mirror, 1.5 bends it a lot. |
RADIUS | script.js | 0.3 | The size of the bubble under the pointer, in mirror widths. |
SWELL | script.js | 0.42 | How much the bubble magnifies; on the way back the elastic turns it into a squash. |
WOBBLE | script.js | 1 | How much the picture is dragged and ripples when the pointer moves (0 = the bubble only). |
SETTLE | script.js | 1.6 | The seconds of the elastic when the curve changes or the pointer leaves. |
AUTO | script.js | 2.4 | How many seconds between automatic curve changes, until someone clicks (0 = only on click). |
For the page of a funfair, a circus, a festival or a summer event, for a game or a welcome page that should make people smile, or to show an illustration in a way that asks to be touched. Any picture can be painted in place of the scene: the mirror reads the pixels of a canvas.
The same animation with the mirror almost as wide as the screen. A finger touching and sliding on the glass makes the bubble, drags it and lets it spring back when it lifts; a tap without sliding changes the curve.
Nothing moves: the sign, the mirror and the line fade in with the glass already concave. The pointer does not bend the picture; when the curve changes the glass dims for a moment and comes back with the new curve already in place.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.
Tested on 11 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.