
The water button
Buttons, SVG, Physics, Hover
NewFree
The card of an architect’s lamp in a small design shop: the lamp stands on a desk at night and points its light where you are. The shade aims at the pointer (or the finger, or the arrow keys) and the two arms bend to bring it there, worked out like a real arm with two bones and the elbow always on top; each joint reaches its angle on a spring of its own, so the lamp has weight and comes to rest with a small nod. The cone of light and the pool on the desk follow it; a click switches it off and on again with a little catch of the filament, and choosing another finish repaints the lamp.
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 desk lamp card</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/jost@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the card of an architect's desk lamp in a small design shop. The lamp stands on a dark desk and points its
light where you are: its two arms bend at the elbow, worked out like a real arm, and swing there on springs,
the shade turns to face the pointer, and the cone of light and the pool on the desk follow. A click (or the
switch) turns it off and on; choosing another finish repaints it. The lamp and its light are drawn by the
script at every frame -->
<article class="lamp" aria-labelledby="lamp-name" data-lamp>
<div class="desk" tabindex="0" role="group" aria-label="The lamp on the desk. Aim it with the arrow keys; Enter switches it" data-desk>
<svg class="desk__svg" viewBox="0 0 300 214" preserveAspectRatio="xMidYMid meet" aria-hidden="true" data-svg>
<defs>
<linearGradient id="lamp-beam" gradientUnits="userSpaceOnUse" data-beam>
<stop offset="0" class="glow" stop-opacity="0.7" />
<stop offset="1" class="glow" stop-opacity="0" />
</linearGradient>
<radialGradient id="lamp-pool">
<stop offset="0" class="glow" stop-opacity="0.9" />
<stop offset="1" class="glow" stop-opacity="0" />
</radialGradient>
</defs>
<g class="light" data-light>
<path class="light__beam" fill="url(#lamp-beam)" data-cone />
<ellipse class="light__pool" fill="url(#lamp-pool)" data-pool />
</g>
<path class="desk__top" d="M0 196.5H300" />
<g class="body" data-body>
<path class="body__base" d="M40 196c0-7 6-10 24-10s24 3 24 10z" />
<path class="body__post" d="M64 186v-8" />
<path class="body__spring" data-spring1 />
<path class="body__spring" data-spring2 />
<path class="body__arm" data-arm1 />
<path class="body__arm" data-arm2 />
<circle class="body__joint" r="4.2" data-j0 />
<circle class="body__joint" r="4.2" data-j1 />
<g data-head>
<path class="body__mouth" d="M27 -15.5 L27 15.5 L29.5 15.5 L29.5 -15.5 Z" data-mouth />
<path class="body__shade" d="M-5 -5.5 L7 -6.5 L29 -17 L29 17 L7 6.5 L-5 5.5 Z" />
<circle class="body__joint" r="4.2" />
</g>
</g>
</svg>
<button class="desk__switch" type="button" aria-pressed="true" data-switch>
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 1.8v5.4M4.6 3.9a5.2 5.2 0 1 0 6.8 0" /></svg>
<span data-switch-word>On</span>
</button>
</div>
<div class="info">
<div class="info__head">
<h2 class="info__name" id="lamp-name">Arca desk lamp</h2>
<p class="info__price">€240</p>
</div>
<p class="info__kind">Steel and cast iron · <span data-finish-name>Graphite</span></p>
<div class="finishes" role="radiogroup" aria-label="Finish" data-finishes></div>
</div>
<p class="sr" aria-live="polite" data-live></p>
</article>
</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.
27 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 desk lamp card” from Animorio and fit it into my siteThe lamp points where you are: the head stands REACH units from the aimed point, on the line back to the shoulder, above the desk top and inside the drawing. The aimed point chases the pointer with a small lag, so the lamp never snaps.
Shoulder, elbow and head are a two-bone arm (ARMS): the distance from shoulder to head, capped to what the arms can cover, gives the elbow’s angle through the law of cosines. Of the two solutions the one with the elbow on top is taken, as on a real lamp, unless that elbow would end up outside the drawing.
The two arms and the shade each have their own angle spring (SPRING for the arms, HEAD for the shade, quicker), integrated on the GSAP ticker in four sub-steps with the step capped at 1/30 of a second. The angles are unwrapped towards the target, so a joint never goes the long way round; when everything is still the ticker lets go.
From the mouth of the shade two rays open by BEAM degrees: where they meet the top they stop, otherwise they go far, and between them is the cone, filled with a linear gradient along the axis that fades to nothing. Where the axis meets the desk there is the pool, an ellipse with a radial gradient as wide as the cone there. All the light is in mix-blend-mode screen over the dark ground.
Switching on, the light rises to 55%, drops back to 40% and then rises fully, like a filament warming up; switching off it fades in a fifth of a second. Either way the shade gets a small push and nods. The finish changes the --lamp variable with a colour tween.
The desk takes the focus: the arrows move the aimed point by 14 units (40 with Shift), Enter and Space switch it on and off, Escape sends it back to looking at the top. The switch is a button with aria-pressed and the finishes a radiogroup. On a phone a finger tapping or dragging on the desk aims the lamp (touch-action: none) and the switch turns it on.
| Name | File | Default | What it changes |
|---|---|---|---|
FINISHES | script.js | [{ name: 'Chalk', color: '#ebe7e0' }, …] | The finishes on sale: the name and the colour of the paint. Five at most; light or bright colours work best, as the desk is dark. |
ARMS | script.js | [92, 86] | The length of the two arms, in the drawing’s units (the desk is 300 wide). |
REACH | script.js | 74 | How far the shade stays from the point it aims at. |
SPRING | script.js | [70, 0.55] | The arms’ spring: how stiff it is and how much it is held back (1 = no bounce). |
HEAD | script.js | [150, 0.6] | The shade’s spring. |
BEAM | script.js | 21 | Half the opening of the cone of light, in degrees. |
For the card of a lamp, a spotlight or a piece of furniture in a design shop; also for a smart home app, a reading lamp in a catalogue or the page of a studio that designs objects. It works wherever the product should answer the hand as it would in a shop.
The card is 340 pixels wide, or the screen minus 32, and the drawing fits through its viewBox. A finger touching the desk moves the aim and dragging it the lamp follows (over the desk the finger does not scroll the page); the switch, 34 pixels tall, turns it off and on. Springs and light are the same as on a computer.
The lamp does not chase the pointer along the way: when the aim changes the drawing dims for a moment and comes back with the lamp already turned. No entrance, no catch: the light fades on and off, and the finishes change colour in a moment.
GSAP 3.15.0, Jost (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 8.6 KB; the libraries: GSAP 28 KB.
Tested on 19 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.