
Il pulsante di vetro liquido
Pulsanti, WebGL, Hover, Fisica
NuovaGratis
Il pannello di sincronizzazione di un’app di foto, con una corda colorata per ogni dispositivo: man mano che arrivano i byte le tre corde tese vengono intrecciate da sinistra in una treccia sola, che è la barra di avanzamento. Se un dispositivo va offline la sua corda esce dal morsetto e resta lenta e grigia finché Retry non la riaggancia; alla fine il morsetto si chiude sulla treccia, una stretta la percorre all’indietro e un riflesso in avanti.
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 sync braid</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/radio-canada@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the sync panel of a photo app: one cord for every device, plaited into a single braid as the bytes arrive.
The braid is the progress bar; the loose cords still running to the clamp on the right are what is left.
The cords are painted by a WebGL shader on the canvas; the words, the clamps and the button are HTML -->
<section class="sync" aria-labelledby="sync-title" data-sync>
<div class="sync__head">
<div class="sync__words">
<h2 class="sync__title" id="sync-title"><span class="sync__swap" data-title>Photos</span></h2>
<p class="sync__meta"><span class="sync__swap" data-meta>Last synced yesterday</span></p>
</div>
<p class="sync__figure" aria-hidden="true">
<span class="sync__pct" data-pct>2.4 GB</span>
<svg class="sync__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.1 4.1L18.6 7.4" pathLength="1"/></svg>
</p>
</div>
<div class="sync__bar" role="progressbar" aria-labelledby="sync-title" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" data-bar>
<canvas class="sync__paint" aria-hidden="true" data-paint></canvas>
<span class="sync__cap sync__cap--start" aria-hidden="true"></span>
<span class="sync__cap sync__cap--end" aria-hidden="true" data-clamp></span>
</div>
<div class="sync__foot">
<p class="sync__devices" data-devices></p>
<button class="sync__btn" type="button" data-action data-autoclic><span class="sync__label" data-label>Sync now</span></button>
</div>
<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.
32 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 treccia della sincronizzazione» da Animorio e adattala al mio sitoLe corde non si disegnano a tratti: per ogni colonna di pixel del canvas lo script calcola a che altezza passa ognuna delle tre corde e quanto è profonda, e scrive i numeri in una piccola texture di tre righe (l’altezza in due byte, con un 256° di pixel di precisione, la profondità in uno, più un byte che dice se lì la corda c’è). A ogni fotogramma la texture si aggiorna con texSubImage2D: poche centinaia di colonne e nessuna geometria.
Per ogni pixel lo shader cerca il punto più vicino di ogni corda fra le colonne intorno (i segmenti da una colonna alla successiva) e ne prende la distanza d. Dentro il raggio r la corda è un tubo: la sua altezza lì è la profondità del capo più la curva del tubo, √(r² − d²); vince il tubo più alto, e i tre si posano dal più basso al più alto con il bordo sfumato di mezzo pixel. Così gli incroci della treccia sono esatti al pixel, anche dove due corde si toccano di sbieco. Dalla normale del tubo vengono la luce diffusa e il riflesso, dei trefoli inclinati girano intorno a ogni corda, e le ombre cadono in basso a destra sul pannello e sulla corda di sotto, tanto più lontane quanto più la corda sta sopra.
Il capo k segue y = A sin θ e z = sin 2θ, con θ = 2π (x ÷ P + k ÷ 3): vista di fronte è la treccia classica, e il seno doppio della profondità fa passare ogni capo una volta sopra e una volta sotto gli altri. Fino alla punta (la parte già arrivata) i capi seguono la treccia; dopo, in un tratto poco più lungo di un giro, si aprono verso la loro corsia con una smootherstep e la profondità si spegne insieme. La punta segue i byte su una molla con lo smorzamento critico, che non torna mai indietro.
Il tratto sciolto di ogni corda è una fila di 48 punti che si muove di lato secondo l’equazione delle onde, con una spinta verso la curva in cui dovrebbe stare e uno smorzamento, calcolata a passi di 1/480 di secondo. La tensione cambia tutto: tesa, le onde corrono veloci e la corda è tirata dritta; lenta, le onde sono pigre e la corda torna alla curva che ricorda (le due esterne si piegano verso fuori, quella in mezzo a S). Tesa di colpo vibra: è lo schiocco. Mentre i dispositivi si confrontano parte un impulso alla volta, con la posizione e la velocità di un’onda che va a sinistra, w = g(x + ct), così corre in una direzione sola; durante l’intreccio ogni incrocio dà alle corde una piccola spinta.
Quando un dispositivo va offline la sua corda si stacca: l’estremo torna indietro con back.out, diventa libero (la corda ha un punto fantasma a specchio invece del capo fermo) e lo shader la ingrigisce solo oltre la punta della treccia. Retry la riporta nel morsetto, le ridà il colore e la tende. Alla fine il morsetto si chiude con uno schiacciamento elastico mentre le corsie si stringono sulla treccia; poi una stretta, l’ampiezza della treccia che cala sotto una campana, corre da destra a sinistra, e un riflesso da sinistra a destra.
La barra è un role=progressbar con aria-valuenow e aria-valuetext in gigabyte, il pannello ha aria-busy mentre lavora e una riga aria-live dice quando la sincronizzazione parte, si ferma, perde un dispositivo e finisce. Il pulsante cambia parola (Sync now, Pause, Resume, Retry, Sync again) e la pillola prende la larghezza nuova con un piccolo rimbalzo; un clic da script, quello dell’anteprima della libreria, non mette mai in pausa. Senza WebGL le corde diventano tre strisce disegnate dal CSS, e tutto il resto funziona.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
SECONDS | script.js | 5.5 | Quanto dura una sincronizzazione nella demo, in secondi (una vera segue i suoi byte). |
SIZE | script.js | 2.4 | I gigabyte da sincronizzare. |
DEVICES | script.js | ['Laptop', 'Tablet', 'Phone'] | I tre dispositivi, uno per corda, dall’alto. |
FAIL_EVERY | script.js | 2 | Nella demo, una sincronizzazione su quante va storta (0 mai). |
FAIL_AT | script.js | 0.58 | Fin dove arriva la sincronizzazione che va storta, da 0 a 1. |
OFFLINE | script.js | 2 | Quale dispositivo va offline allora (0 quello in alto, 2 quello in basso). |
CORD | script.js | 9 | Lo spessore di una corda, in pixel. |
PITCH | script.js | 4.2 | Quanto è lungo un giro della treccia, in spessori di corda. |
SPREAD | script.js | 1.65 | Quanto corrono distanti le corde sciolte, in spessori di corda. |
TWANG | script.js | 1 | Quanto vibra una corda quando viene tesa: 0 per niente, 1 molto. |
FOLLOW | script.js | 0.35 | Quanto ci mette la treccia a raggiungere i byte, in secondi. |
SHEEN | script.js | 0.8 | Il riflesso che corre sulla treccia finita: 0 niente, 1 forte. |
Per un’attesa di qualche secondo che ha un esito: sincronizzare dei dispositivi, unire delle librerie, un backup, un’importazione da più fonti. Le tre corde dicono bene «tre cose che diventano una», e il dispositivo che si stacca si vede subito; per un file solo da caricare è meglio una barra più semplice.
Il pannello prende la larghezza dello schermo meno 32 pixel (358 a 390) e resta alto 183; la treccia si accorcia con lui e lo shader dipinge alla densità vera dello schermo, fino a due volte. Il pulsante è una pillola alta 34: col dito si schiaccia e rimbalza, e la scritta si allarga sull’asse della larghezza solo sotto il mouse.
Niente schiocchi, impulsi né sussulti: le corde passano subito dalla curva lenta alla linea tesa, la corda che va offline è già fuori dal morsetto, il morsetto si chiude senza schiacciarsi, senza stretta e senza riflesso, la spunta e le parole nuove arrivano in dissolvenza. La treccia cresce lo stesso, perché è lei che dice a che punto si è.
GSAP 3.15.0, Radio Canada (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 16 KB; le librerie: GSAP 28 KB.
Provata il 5 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.