
The colour columns
Hover, SVG
NewFree
A long piano keyboard drawn in 3D with CSS: as the pointer passes, the keys go down in true perspective and spring back up, and a drawn note flies up from each one, coloured by pitch. A glissando makes a wave, a click a chord.
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 piano keyboard</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="sala">
<header class="insegna">
<p class="insegna__sopra" data-entra>Piano, strings and voice for every age</p>
<h1 class="insegna__titolo" data-titolo>Larkspur School of Music</h1>
<p class="insegna__sotto" data-entra>Open studio every Saturday morning</p>
</header>
<!-- la tastiera: tasti bianchi e neri in 3D con il CSS, costruiti dallo script -->
<div class="pianoforte" data-pianoforte aria-hidden="true">
<div class="piano" data-piano>
<div class="piano__parete"><i class="piano__filetto"></i><i class="piano__feltro"></i></div>
<div class="piano__letto"></div>
<div class="piano__tasti" data-tasti></div>
<div class="piano__listello"></div>
</div>
</div>
<p class="invito" data-entra data-invito>Glide across the keys. Click for a chord.</p>
</main>
<!-- le note che volano: disegni nostri, usati con use -->
<div class="cielo" data-cielo aria-hidden="true"></div>
<svg class="disegni" aria-hidden="true" focusable="false">
<symbol id="semiminima" viewBox="0 0 40 64">
<ellipse cx="14" cy="52" rx="9" ry="6.4" transform="rotate(-22 14 52)" />
<rect x="20.6" y="7" width="3.4" height="45" rx="1.2" />
</symbol>
<symbol id="minima" viewBox="0 0 40 64">
<ellipse cx="14" cy="52" rx="8.2" ry="5.6" transform="rotate(-22 14 52)" fill="none" stroke="currentColor" stroke-width="3.2" />
<rect x="20.6" y="7" width="3.4" height="43" rx="1.2" />
</symbol>
<symbol id="croma" viewBox="0 0 40 64">
<ellipse cx="14" cy="52" rx="9" ry="6.4" transform="rotate(-22 14 52)" />
<rect x="20.6" y="7" width="3.4" height="45" rx="1.2" />
<path d="M21 7 C 23 18 37 22 33 40 C 32 31 28 27 21 25 Z" />
</symbol>
<symbol id="semicroma" viewBox="0 0 40 64">
<ellipse cx="14" cy="52" rx="9" ry="6.4" transform="rotate(-22 14 52)" />
<rect x="20.6" y="5" width="3.4" height="47" rx="1.2" />
<path d="M21 5 C 23 13 35 16 33 30 C 31 23 27 21 21 19 Z" />
<path d="M21 16 C 23 24 37 28 33 44 C 32 35 28 32 21 30 Z" />
</symbol>
<symbol id="crome" viewBox="0 0 58 64">
<ellipse cx="12" cy="52" rx="9" ry="6.4" transform="rotate(-22 12 52)" />
<ellipse cx="42" cy="46" rx="9" ry="6.4" transform="rotate(-22 42 46)" />
<rect x="18.6" y="13" width="3.4" height="39" rx="1.2" />
<rect x="48.6" y="7" width="3.4" height="39" rx="1.2" />
<path d="M18.6 12 L 52 5 L 52 13 L 18.6 20 Z" />
</symbol>
<symbol id="chiave-sol" viewBox="0 0 40 84">
<path d="M21 52 C 15 52 14 44 20 42 C 28 40 32 48 29 54 C 25 62 9 61 7 51 C 5 40 16 33 22 27 C 28 21 31 12 27 6 C 23 1 17 7 18 16 L 23 72 C 23 80 12 80 12 74" fill="none" stroke="currentColor" stroke-width="3.6" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="15" cy="73" r="4.2" />
</symbol>
<symbol id="chiave-fa" viewBox="0 0 52 64">
<path d="M11 22 C 11 12 19 8 25 8 C 35 8 39 16 39 24 C 39 40 25 52 8 58" fill="none" stroke="currentColor" stroke-width="4.2" stroke-linecap="round" />
<circle cx="12" cy="22" r="5.4" />
<circle cx="46" cy="16" r="3" />
<circle cx="46" cy="30" r="3" />
</symbol>
</svg>
<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.
20 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 piano keyboard” from Animorio and fit it into my siteThe container has the perspective; inside it, the keybed is tilted with rotateX around its front edge and keeps transform-style: preserve-3d. The script builds the keys: for every octave seven whites side by side and five blacks, centred on the border between two whites with a small offset (C sharp a little to the left, D sharp a little to the right), 0.58 of a white wide and 64% as long. A real key is about six and a half times as long as it is wide: that is the length, if the screen allows it.
Every key has its front face, a child turned by −90° at the bottom, hanging into the bed: that is its thickness. The blacks stand up with translateZ, also have two sides (rotateY ±90°) and a shadow lying on the whites, which gets lighter when the black goes down. The red felt is a strip over the tails, the lacquered wall a standing panel, the front rail a hanging panel. The floor between the keys lies lower than the tip of a pressed key, so it never covers it.
A key is an object with a value p, from 0 (up) to 1 (down), driven by GSAP: rotateX(−p × DEPTH) around the tail, and a veil that lights up with p, dark at the back and coloured at the tip. Pressing takes seven hundredths; letting go is an elastic.out, springier or stiffer according to BOUNCE, and the upward bounce (p below zero) counts only six tenths.
On every movement elementFromPoint is asked which key lies under the pointer, and also along the segment from the previous point, in steps of a third of a key, so a fast hand skips no key. Speed in keys per second decides the wave: from three upwards the four whites ahead, in the direction of the hand, bend a little, each less than the one before and a moment later. A click, a tap or a dive from above faster than 320 pixels a second plays the triad starting from the key.
The notes are symbols in a hidden SVG, drawn with ellipses, rectangles and curves, and used with use. Each one starts from its key, picked at random, coloured with gsap.utils.interpolate between the three colours by pitch; a timeline swells it with back.out, carries it up with power2.out and makes it sway and turn with sine.inOut keyframes, then fades it. The title letters are spans: a key picks the one standing above it in proportion, which jumps and takes its colour; a chord makes them all jump, one after the other.
| Name | File | Default | What it changes |
|---|---|---|---|
KEYS | script.js | 3 | How many octaves the keyboard has (two at most on a phone). |
NOTES | script.js | 1 | How many notes fly up from each key pressed: 0 none, 0.5 one every two, 2 a little flock. |
BOUNCE | script.js | 1 | How much a key bounces when it comes back up, and the title letters too (0 = no bounce). |
FLOAT | script.js | 1 | How high and how long the notes fly. |
WAVE | script.js | 1 | How big the wave of a fast glissando is (0 = no wave). |
DEPTH | script.js | 6 | How far a key goes down, in degrees. |
--nota-grave, --nota-media, --nota-acuta | style.css | #2E44B4, #D6432E, #DE8C0F | The note colours, from bass to treble: every key takes the one its pitch calls for. |
--feltro, --avorio, --ebano, --cassa | style.css | #A51F2B, #F8F3E8, #1A1612, #15110E | The red felt, the white keys, the black keys and the lacquer of the case. |
For the opening of the site of a music school, a piano teacher, an instrument shop, a festival or a composer: a first screen you play with your hand. It also works as a toy on a contact page or at the bottom of a page.
The same animations with two octaves at most, narrower keys and smaller notes. You play by sliding a finger over the keys (the page does not scroll on the keyboard) and a tap without dragging plays a chord.
Nothing moves: the keys do not go down but veil and colour themselves, the notes fade in a little above the key and fade away without flying, the title letters change colour without jumping. The entrance is a fade.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 9.8 KB; the libraries: GSAP 28 KB.
Tested on 10 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.