
The elastic button
Buttons, Hover, Text
NewFree
The footer of an evening city guide: under the newsletter and the links stands a skyline drawn at four depths that follow the pointer. The sky goes from sunset to night, the windows come on one after another, a train crosses on the elevated railway and the river mirrors it all, trembling. Every link has its own neon sign in the city: pointing at the link strikes the sign like a real tube, pointing at the sign lights the link.
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 city that lights up</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/big-shoulders-display@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the footer of an evening city guide: under the newsletter and the links, a skyline at dusk drawn at four
depths that follow the pointer. The sky goes from sunset to night, the windows come on one after another,
an elevated train crosses and the river below mirrors it all. Every link has its own neon sign somewhere in
the city: pointing at the link lights the sign, pointing at the sign lights the link -->
<footer class="lc" data-footer data-autoclic>
<canvas class="lc__city" aria-hidden="true" data-city></canvas>
<div class="lc__top" data-top>
<form class="lc__form" action="#" novalidate aria-labelledby="lc-news" data-form>
<h2 class="lc__title" id="lc-news" data-rise>The Friday night list</h2>
<p class="lc__lede" data-rise>Five places worth staying out for, in your inbox at six.</p>
<label class="sr" for="lc-email">Email address</label>
<div class="lc__field" data-field data-rise>
<input class="lc__input" id="lc-email" name="email" type="email" inputmode="email" autocomplete="email" autocapitalize="off" spellcheck="false" enterkeyhint="send" placeholder="you@example.com" aria-describedby="lc-note" aria-invalid="false" data-input>
<button class="lc__send" type="submit" data-send>
<span class="lc__send-bg"></span>
<span class="lc__send-label" data-send-label>Sign up</span>
<svg class="lc__spin" viewBox="0 0 24 24" aria-hidden="true" data-spin><circle cx="12" cy="12" r="8"/></svg>
<svg class="lc__done" viewBox="0 0 24 24" aria-hidden="true" data-done><path d="M6.4 12.6l3.7 3.7 7.6-8.6"/></svg>
</button>
</div>
<p class="lc__note" id="lc-note" aria-live="polite" data-note data-rise><span class="lc__line">One email a week. Leave whenever you like.</span></p>
</form>
<nav class="lc__cols" aria-label="Footer">
<div class="lc__col">
<h3 class="lc__head" data-rise>Eat & drink</h3>
<ul class="lc__list">
<li data-rise><a class="lc__link" href="#" data-link data-sign="BAR" style="--glow: var(--sign-bar)">Bars</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="NOODLES" style="--glow: var(--sign-noodles)">Noodles</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="DINER" style="--glow: var(--sign-diner)">Diners</a></li>
</ul>
</div>
<div class="lc__col">
<h3 class="lc__head" data-rise>Go out</h3>
<ul class="lc__list">
<li data-rise><a class="lc__link" href="#" data-link data-sign="CINEMA" style="--glow: var(--sign-cinema)">Cinemas</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="JAZZ" style="--glow: var(--sign-jazz)">Jazz clubs</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="THEATRE" style="--glow: var(--sign-theatre)">Theatres</a></li>
</ul>
</div>
<div class="lc__col">
<h3 class="lc__head" data-rise>Stay</h3>
<ul class="lc__list">
<li data-rise><a class="lc__link" href="#" data-link data-sign="HOTEL" style="--glow: var(--sign-hotel)">Hotels</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="FERRY" style="--glow: var(--sign-ferry)">Ferries</a></li>
<li data-rise><a class="lc__link" href="#" data-link data-sign="BOOKS" style="--glow: var(--sign-books)">Bookshops</a></li>
</ul>
</div>
</nav>
</div>
<!-- the room the city stands in: the skyline, the elevated railway and the river are drawn on the canvas behind -->
<div class="lc__room" aria-hidden="true" data-room></div>
<div class="lc__base" data-base>
<p class="lc__brand">
<svg class="lc__mark" viewBox="0 0 28 28" aria-hidden="true"><path class="m-tower" d="M8 26V9.5L14 4l6 5.5V26z"/><path class="m-lit" d="M11.2 12.4h2.2v2.8h-2.2zM14.6 17.6h2.2v2.8h-2.2z"/><path class="m-dark" d="M14.6 12.4h2.2v2.8h-2.2zM11.2 17.6h2.2v2.8h-2.2z"/><path class="m-ground" d="M3 26h22"/></svg>
<span class="lc__name">Lamplit</span>
<span class="lc__copy"><svg viewBox="0 0 12 12" aria-hidden="true"><circle cx="6" cy="6" r="5.1"/><path d="M7.9 4.5a2.3 2.3 0 1 0 0 3"/></svg><span class="sr">Copyright</span> 2026 Lamplit Guides</span>
</p>
<nav class="lc__legal" aria-label="Legal">
<a class="lc__small" href="#" data-small>Privacy</a>
<a class="lc__small" href="#" data-small>Terms</a>
<a class="lc__small" href="#" data-small>Contact</a>
</nav>
</div>
</footer>
</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.
73 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 city that lights up footer” from Animorio and fit it into my siteThe city comes from a seeded random number generator (SEED): three rows of buildings, far, middle and near, each with its own widths, heights and roofs (setbacks, spire, dome, gable, water tank, mast). The near row is built around the signs: each link's sign is measured with its own font (measureText) and, at its place along the city, a building is made just wide and tall enough to carry it above the railway, with other buildings in between. The tallest the city may stand is read from the real footer, between the end of the text and the quay, and everything is rebuilt whenever the size changes.
Each row is painted once on a hidden canvas at sunset (lighter facades, dark windows mirroring the warm sky, a line of light on the edges facing the sun) and once at night. On every frame where something changes, the two versions are blended with globalAlpha by the hour (dusk from 0 to 1, a GSAP tween) and the lit windows are drawn on top; the result is kept in a third canvas and drawn again only when a window or the hour changes. So the parallax just moves three ready images.
Every window has a threshold: it switches on when the hour passes it (homes at random, offices floor by floor) and remembers the moment of the switch. Its light is a function of the time since then: a lamp rises in 0.24 s on a soft curve, an office strip light goes on, nearly off, on, a dip, then up (FLICKER). A click on a building changes its mode (all on or all off) with a delay for each floor, and the sign-up changes everyone's mode with a delay growing from left to right: the wave is the same rule.
The scene above the quay is drawn in a canvas a few pixels wider. The river is filled line by line of pixels with drawImage: line k takes the line of the scene 1.45 k higher up (the city is squeezed in the reflection as in perspective), shifted sideways by two sines that grow towards the viewer and a little up and down, so the lights break into dashes. The amplitude is an energy that rises with the pointer's movement and the train and dies away on its own; a veil of water, darker towards the bottom, keeps the words readable.
Each sign is drawn twice, off (panel or frame and dark tubes of its colour) and on: the letters three times, with a wide shadowBlur, a tight shadowBlur and then the almost white core with no shadow. In the city the dark one is drawn and the lit one over it with globalAlpha equal to its light, plus a radial glow in lighter mode on the wall around it; the light is moved by a GSAP timeline with the strike of the gas. A sign is on while someone holds it on: the mouse on the link, keyboard focus, the pointer on the sign, a tap, the sign-up party.
The pointer gives a target from -1 to 1; the view chases it on a damped spring (stiffness 70, damping 12) and each depth moves by PARALLAX times its depth (sky 0.06, far 0.2, middle 0.5, near 0.85, railway 1), a little vertically too. The requestAnimationFrame loop, with the step capped at 1/30 of a second, runs only while the spring, a window, a sign, the train, the water or the wave are moving: then it stops and the city stays drawn.
| Name | File | Default | What it changes |
|---|---|---|---|
DUSK | script.js | 6 | How many seconds the evening takes to fall when the footer comes into view. |
LIT | script.js | 0.62 | The share of windows lit once night has fallen, from 0 to 1. |
PARALLAX | script.js | 16 | How many pixels the nearest buildings slide with the pointer (the far ones less). |
TRAIN | script.js | 5 | How many seconds the elevated train takes to cross the city. |
RIPPLE | script.js | 1 | How much the river ripples: at 0 it is a still mirror. |
FLICKER | script.js | 1 | How much a neon sign stutters as it comes on: at 0 it lights in one go. |
SEED | script.js | 7 | The city itself: another number draws another skyline. |
WAIT | script.js | 1.1 | How long the sign-up takes in the demo, in seconds. |
For the footer of a city guide, a downtown hotel, a bar or a cinema, a night festival, an events agency, a travel or city-life blog: anywhere the end of the page can become the evening, and the links can be real places to light up.
The footer takes the width of the screen: the newsletter goes on top, the three columns stay three, with 44-pixel rows to tap, and the city stands below, taller, with the river and the small print on two lines. The signs become nearly all vertical, smaller, and each still has its own building. A tap on a link lights its sign for a second and a half; a tap on a sign lights the link; a finger dragged sideways moves the depths, and let go it springs back to the middle; a tap on a building turns its floors on.
Nothing moves: no parallax, no train, the river is still and the words fade in. The sky goes from sunset to night in a fade of little more than a second and the windows fade on, with no stutter. The signs fade on and off, the link changes colour without widening; the sign-up fades the whole city on at once.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0, Big Shoulders Display (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 23 KB; the libraries: GSAP 28 KB.
Tested on 17 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.