
L’indicatore a lancetta
Contatori, Fisica, SVG
NuovaGratis
L’abbinamento di una lampadina smart nell’app della casa, e la barra di avanzamento è la lampadina stessa, vista di fianco: mentre il collegamento avanza il filamento di tungsteno si scalda passando per i colori veri di un corpo caldo, dal rosso scuro all’arancio al bianco caldo, il vetro si riempie di luce e la spirale trema un poco sui suoi fili. Se il collegamento cade il filamento sfarfalla e si raffredda; a lampadina abbinata si accende piena, con un lampo, e la sua luce si allarga sul pannello.
Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>The pairing bulb</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the pairing bulb: a smart home app pairing a new filament bulb, and the bulb is the progress. Seen from the side,
its tungsten coil warms through the real colours of a hot body as the pairing goes on, from a dull red to
orange to a warm white, while the glass fills with light and the coil trembles on its wires. If the link
drops, the coil flickers and cools; once paired, the bulb comes on in full and its light spreads over the
panel. The coil, the halo and the light are drawn by script.js -->
<section class="pair" aria-labelledby="pair-title" data-pair>
<div class="pair__light" aria-hidden="true" data-light></div>
<header class="pair__head">
<div class="pair__words">
<h2 class="pair__title" id="pair-title"><span class="pair__swap" data-title>Living room lamp</span></h2>
<p class="pair__meta"><span class="pair__swap" data-meta>New bulb nearby · Not paired</span></p>
</div>
<p class="pair__kelvin" aria-hidden="true"><span data-kelvin>Off</span></p>
</header>
<div class="pair__bulb" role="progressbar" aria-labelledby="pair-title" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Not paired" data-bar>
<svg class="pair__art" viewBox="10 30 180 232" aria-hidden="true" data-art>
<defs>
<clipPath id="pb-glass-clip"><path d="M75 188C75 166 38 144 38 98A62 62 0 0 1 162 98C162 144 125 166 125 188Z"/></clipPath>
<radialGradient id="pb-halo-wide" gradientUnits="userSpaceOnUse" cx="100" cy="96" r="84">
<stop offset="0" stop-color="#ffb070" stop-opacity="0" data-stop="wide0"/>
<stop offset="0.38" stop-color="#ffb070" stop-opacity="0" data-stop="wide1"/>
<stop offset="1" stop-color="#ffb070" stop-opacity="0" data-stop="wide2"/>
</radialGradient>
<radialGradient id="pb-halo-core" gradientUnits="userSpaceOnUse" cx="100" cy="96" r="26">
<stop offset="0" stop-color="#fff4e6" stop-opacity="0" data-stop="core0"/>
<stop offset="1" stop-color="#fff4e6" stop-opacity="0" data-stop="core1"/>
</radialGradient>
<radialGradient id="pb-halo-mid" gradientUnits="userSpaceOnUse" cx="100" cy="96" r="36">
<stop offset="0" stop-color="#ffb070" stop-opacity="0" data-stop="mid0"/>
<stop offset="1" stop-color="#ffb070" stop-opacity="0" data-stop="mid1"/>
</radialGradient>
<linearGradient id="pb-glass-edge" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.34"/>
<stop offset="0.5" stop-color="#ffffff" stop-opacity="0.08"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0.22"/>
</linearGradient>
<linearGradient id="pb-glass-fill" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.07"/>
<stop offset="0.55" stop-color="#ffffff" stop-opacity="0.015"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0.05"/>
</linearGradient>
<linearGradient id="pb-metal" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" style="stop-color: var(--metal-lo)"/>
<stop offset="0.2" style="stop-color: var(--metal-mid)"/>
<stop offset="0.36" style="stop-color: var(--metal-hi)"/>
<stop offset="0.62" style="stop-color: var(--metal-mid)"/>
<stop offset="1" style="stop-color: var(--metal-lo)"/>
</linearGradient>
<linearGradient id="pb-stem" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.2"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0.05"/>
</linearGradient>
<linearGradient id="pb-base-lit" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffb070" stop-opacity="0.55" data-stop="base0"/>
<stop offset="1" stop-color="#ffb070" stop-opacity="0" data-stop="base1"/>
</linearGradient>
<filter id="pb-bloom" x="-60%" y="-160%" width="220%" height="420%" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" stdDeviation="1.4" result="near" data-blur="near"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="far" data-blur="far"/>
<feMerge><feMergeNode in="far"/><feMergeNode in="far"/><feMergeNode in="near"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<!-- the screw base: a collar, four threads, the black insulator and the contact -->
<g class="pair__base">
<path d="M74 186h52v8c0 3-2 5-5 5H79c-3 0-5-2-5-5z" fill="url(#pb-metal)"/>
<rect x="78" y="198" width="44" height="40" style="fill: var(--metal-lo)"/>
<g data-threads>
<rect x="73" y="199" width="54" height="9" rx="4.5" fill="url(#pb-metal)" transform="rotate(-3 100 203.5)"/>
<rect x="74" y="209" width="52" height="9" rx="4.5" fill="url(#pb-metal)" transform="rotate(-3 100 213.5)"/>
<rect x="75" y="219" width="50" height="9" rx="4.5" fill="url(#pb-metal)" transform="rotate(-3 100 223.5)"/>
<rect x="77" y="229" width="46" height="8" rx="4" fill="url(#pb-metal)" transform="rotate(-3 100 233)"/>
</g>
<path d="M80 238h40l-4 9H84z" style="fill: var(--insulator)"/>
<path d="M90 247h20c0 5-4 8-10 8s-10-3-10-8z" fill="url(#pb-metal)"/>
<path d="M74 186h52v14H74z" fill="url(#pb-base-lit)" style="mix-blend-mode: screen" data-base-lit/>
</g>
<!-- the glass and everything inside it -->
<path d="M75 188C75 166 38 144 38 98A62 62 0 0 1 162 98C162 144 125 166 125 188Z" fill="url(#pb-glass-fill)"/>
<g clip-path="url(#pb-glass-clip)">
<rect x="0" y="0" width="200" height="200" fill="url(#pb-halo-wide)"/>
<rect x="0" y="0" width="200" height="200" fill="url(#pb-halo-mid)"/>
<rect x="0" y="0" width="200" height="200" fill="url(#pb-halo-core)"/>
</g>
<!-- the stem: a glass tube flaring into the neck, with the press that holds the wires -->
<path d="M84 192C88 184 90 168 91 150Q100 145 109 150C110 168 112 184 116 192Z" fill="url(#pb-stem)"/>
<path d="M98 150V120" style="stroke: var(--stem)" stroke-width="2.6" stroke-linecap="round" opacity="0.55"/>
<g class="pair__wires" fill="none" stroke-linecap="round" stroke-linejoin="round" data-wires>
<path d="M94 156L94 140L74 100L74 93" data-wire="0"/>
<path d="M106 156L106 140L126 100L126 93" data-wire="1"/>
<path d="M98 121L100 104" data-wire="2"/>
</g>
<g fill="none" stroke-linecap="round" stroke-linejoin="round">
<path class="pair__coil pair__coil--back" data-coil="back"/>
<g filter="url(#pb-bloom)" data-glow>
<path class="pair__coil pair__coil--glow" data-coil="glow"/>
</g>
<path class="pair__coil pair__coil--front" data-coil="front"/>
<path class="pair__coil pair__coil--core" data-coil="core"/>
</g>
<!-- the glass's own edge and its two highlights, over everything -->
<path d="M75 188C75 166 38 144 38 98A62 62 0 0 1 162 98C162 144 125 166 125 188" fill="none" stroke="url(#pb-glass-edge)" stroke-width="1.2"/>
<path d="M75 188C75 166 38 144 38 98A62 62 0 0 1 162 98C162 144 125 166 125 188" fill="none" stroke="#ffb070" stroke-width="1.3" opacity="0" data-rim/>
<path d="M50 78C56 60 70 48 86 43" fill="none" stroke="#ffffff" stroke-width="3.2" stroke-linecap="round" opacity="0.26"/>
<path d="M47 96C47 92 47.5 89 48.5 86" fill="none" stroke="#ffffff" stroke-width="2.4" stroke-linecap="round" opacity="0.2"/>
<path d="M150 122C146 132 140 140 133 147" fill="none" stroke="#ffffff" stroke-width="1.6" stroke-linecap="round" opacity="0.12"/>
</svg>
</div>
<footer class="pair__foot">
<div class="pair__readout" aria-hidden="true">
<p class="pair__pct" data-pct-row>
<svg class="pair__tick" viewBox="0 0 24 24" data-tick><path d="M5 12.8l4.2 4.2L19 7.2" pathLength="1"/></svg>
<span class="pair__swap" data-pct>Smart bulb</span>
</p>
<p class="pair__sub"><span class="pair__swap" data-sub>E27 · 806 lm · dimmable</span></p>
</div>
<button class="pair__btn" type="button" data-action data-autoclic><span class="pair__label" data-label>Pair</span></button>
</footer>
<p class="sr" role="status" aria-live="polite" data-live></p>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.
31 valori da cambiare
Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:
Prendi «La lampadina che si abbina» da Animorio e adattala al mio sitoIl colore del filamento non è una sfumatura scelta a mano. Per ogni temperatura lo script somma la legge di Planck, B(λ, T) = 1 ÷ (λ⁵ (e^(c₂ ÷ λT) − 1)), su tutto lo spettro visibile a passi di 5 nanometri, pesata con le tre curve dell’osservatore CIE 1931 (l’approssimazione analitica di Wyman, Sloan e Shirley: qualche gaussiana storta per curva). Ne escono X, Y e Z, che una matrice porta in sRGB lineare; il canale più forte si porta a 1 e si applica la curva gamma. Così a 900 K viene un rosso cupo, a 1500 un arancio, a 2700 il bianco caldo di una lampadina, senza averli scritti da nessuna parte. Il calcolo si fa una volta all’avvio, una tabella ogni 10 kelvin.
La luminanza Y di un corpo caldo cresce in modo enorme con la temperatura: a 1000 K è un milionesimo di quella a 2700. Lo script ne prende il logaritmo fra il primo rosso visibile (circa 700 K) e il colore della lampadina, come fa un occhio che si abitua: questa intensità decide quanto è acceso il filamento, quanto il suo cuore va verso il bianco, la forza del bagliore, dei tre aloni nel vetro e della luce sul pannello. La temperatura vera insegue quella voluta con un ritardo del primo ordine, più svelto a scaldare che a raffreddare, così un filamento spento sfuma dal giallo al rosso al grigio.
La spirale è un’elica lungo una catenaria fra i due fili: per ogni punto θ = 2πNt, l’altezza è il raggio per sin θ e la profondità cos θ, che guardata di sbieco sposta il punto di lato. I punti con la profondità positiva vanno nel tracciato davanti, gli altri in quello dietro (più scuro e sottile), e ogni tratto ricomincia dall’ultimo punto dell’altro, così le spire si chiudono senza buchi. Sopra c’è una copia di tutta la spirale con un filtro SVG di bagliore (due feGaussianBlur fusi con feMerge, che si allargano con l’intensità) e un’anima sottile che diventa bianca quando la lampadina è piena. Scaldandosi il tungsteno si allunga e la spirale si incurva un po’ di più; il filo di sostegno in mezzo la segue.
La spirale trema come una corda tesa fra due punti: due modi su e giù (mezza onda e onda intera, a 6 e 11,5 vibrazioni al secondo) e uno di lato, ognuno una molla smorzata calcolata a passi di 1/480 di secondo, spinta da un rumore che cresce col lavoro. Ogni pezzo di firmware che arriva dà un colpetto e un piccolo aumento di calore; quando il collegamento cade, ogni sfarfallio della corrente scuote la spirale.
Dentro il vetro, ritagliati sulla sua forma, ci sono tre gradienti radiali che si colorano a ogni fotogramma: uno largo che riempie il bulbo, uno medio intorno alla spirale e un cuore quasi bianco che si accende solo vicino alla piena potenza; il bordo del vetro e la parte alta dell’attacco prendono il colore della luce. Fuori, dietro la lampadina, due gradienti radiali sul pannello: uno stretto intorno al vetro e uno largo che, a lampadina accesa, si allarga su tutto il pannello con una curva expo.out mentre un lampo breve porta il filamento a circa 3200 K e lo lascia tornare a 2700.
La lampadina è un role=progressbar con aria-valuenow e un aria-valuetext che dice la fase, il pannello ha aria-busy mentre lavora e una riga aria-live dice quando la lampadina viene trovata, si collega, perde il collegamento ed è abbinata. Il pulsante cambia parola (Pair, Cancel, Retry, Pair again) e la pillola prende la larghezza nuova con un piccolo rimbalzo; un clic da script, quello dell’anteprima della libreria, non annulla mai.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
SECONDS | script.js | 7 | Quanto dura un abbinamento nella demo, in secondi. |
KELVIN | script.js | 2700 | La temperatura di colore della lampadina accesa, in kelvin (2700 è il bianco caldo di una lampadina a filamento). |
WARMEST | script.js | 2250 | Quanto si scalda il filamento poco prima della fine, in kelvin. |
TURNS | script.js | 15 | Quante spire ha la spirale di tungsteno. |
TREMBLE | script.js | 1 | Quanto trema la spirale sui suoi fili mentre lavora: 0 per niente. |
FLASH | script.js | 1 | Il lampo quando la lampadina si accende piena: 0 niente, 1 ben visibile. |
FAIL_EVERY | script.js | 2 | Nella demo, un abbinamento su quanti perde il collegamento (0 mai). |
FAIL_AT | script.js | 0.66 | Fin dove arriva l’abbinamento che fallisce, da 0 a 1 (sempre durante l’aggiornamento del firmware). |
FIRMWARE | script.js | '4.2.1' | La versione del firmware che riceve la lampadina. |
NETWORK | script.js | 'Home Wi-Fi' | La rete a cui si collega la lampadina. |
Per l’abbinamento o la configurazione di un dispositivo della casa (una lampadina, una presa, un termostato), l’aggiornamento del firmware di un oggetto, o qualunque attesa di qualche secondo in un prodotto dal tono caldo, dove l’attesa si può raccontare come «la stiamo accendendo». Il colore che si scalda dice a che punto si è anche senza leggere la percentuale.
Il pannello prende la larghezza dello schermo meno 32 pixel (358 a 390) e resta alto 349; la lampadina è vettoriale e resta nitida a ogni densità, e la luce sul pannello si ricentra sulla lampadina quando la finestra cambia misura. Il pulsante è una pillola alta 38: col dito si schiaccia e rimbalza, e la scritta si allarga sull’asse della larghezza solo sotto il mouse.
La spirale non trema e non sfarfalla, niente lampo: quando il collegamento cade la corrente si spegne in dissolvenza e il filamento si raffredda; a lampadina accesa la luce sul pannello compare in dissolvenza invece di allargarsi. Il filamento si scalda lo stesso con l’avanzamento, perché il suo colore è l’indicatore; le parole nuove arrivano in dissolvenza.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 14 KB; le librerie: GSAP 28 KB.
Provata il 8 ottobre 2026
Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.
Con l’email e la password del tuo account.
oppure con l’email
Hai fatto l’accesso come .
Con l’account scarichi lo zip di ogni animazione e di ogni pacchetto, gratis.