
Le colonne di colore
Hover, SVG
NuovaGratis
Nello stile ricco: il lettore di una web radio fatto come una radio a valvole degli anni Quaranta, in noce lucido. Il tasto On scalda le valvole piano, con un tremolio; i tasti di bachelite, la manopola zigrinata, la rotella o il dito sul vetro spostano la lancetta rossa sul quadrante illuminato. Fra una stazione e l’altra il vetro frigge e la luce trema; sulla stazione giusta l’occhio magico verde si chiude, il nome e il brano compaiono sul vetro e la tela dell’altoparlante batte a tempo.
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">
<title>The vintage radio to tune</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<header class="top">
<span class="brand" data-brand>Hearth</span>
<span class="tag">Live radio, all night long</span>
</header>
<!-- the player of an invented web radio, drawn as a tabletop valve radio from the forties: a walnut cabinet with
its grain, a woven speaker cloth behind wooden fretwork, a glass dial lit from behind with the stations and a
red needle, a green magic eye, two knurled bakelite knobs and a row of bakelite keys. The grain, the knurling,
the ticks and the static are drawn by the script. No sound: the cloth beats instead -->
<section class="radio" aria-label="Vintage radio player" data-radio>
<svg class="set" viewBox="0 0 600 430" data-svg>
<defs>
<linearGradient id="walnut" x1="0" y1="0" x2="0.35" y2="1">
<stop offset="0" style="stop-color:var(--wood-light)"/>
<stop offset="0.45" style="stop-color:var(--wood)"/>
<stop offset="1" style="stop-color:var(--wood-dark)"/>
</linearGradient>
<linearGradient id="veneer" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" style="stop-color:var(--wood)"/>
<stop offset="0.5" style="stop-color:var(--wood-light)"/>
<stop offset="1" style="stop-color:var(--wood)"/>
</linearGradient>
<linearGradient id="lacquer" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#fff" stop-opacity="0.55"/>
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
</linearGradient>
<radialGradient id="valves" cx="0.5" cy="0.55" r="0.6">
<stop offset="0" style="stop-color:var(--valve)" stop-opacity="1"/>
<stop offset="0.55" style="stop-color:var(--valve)" stop-opacity="0.45"/>
<stop offset="1" style="stop-color:var(--valve)" stop-opacity="0"/>
</radialGradient>
<linearGradient id="backlight" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color:var(--amber-light)"/>
<stop offset="1" style="stop-color:var(--amber)"/>
</linearGradient>
<linearGradient id="glassShine" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#fff" stop-opacity="0"/>
<stop offset="0.45" stop-color="#fff" stop-opacity="0.32"/>
<stop offset="0.55" stop-color="#fff" stop-opacity="0.05"/>
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
</linearGradient>
<radialGradient id="bakelite" cx="0.36" cy="0.3" r="0.8">
<stop offset="0" style="stop-color:var(--bakelite-light)"/>
<stop offset="0.6" style="stop-color:var(--bakelite)"/>
<stop offset="1" style="stop-color:var(--bakelite-dark)"/>
</radialGradient>
<linearGradient id="keyFace" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color:var(--bakelite-light)"/>
<stop offset="1" style="stop-color:var(--bakelite)"/>
</linearGradient>
<linearGradient id="brass" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f6dc93"/>
<stop offset="0.5" style="stop-color:var(--brass)"/>
<stop offset="1" stop-color="#8a6420"/>
</linearGradient>
<radialGradient id="eyeGlow" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#d9ffd9"/>
<stop offset="0.55" style="stop-color:var(--eye)"/>
<stop offset="1" stop-color="#1f6b38"/>
</radialGradient>
<pattern id="cloth" width="6" height="6" patternUnits="userSpaceOnUse">
<rect width="6" height="6" style="fill:var(--cloth-dark)"/>
<path d="M0 1.5h6M0 4.5h6" style="stroke:var(--cloth)" stroke-width="1.6"/>
<path d="M1.5 0v6M4.5 0v6" style="stroke:var(--cloth-light)" stroke-width="1.1" stroke-opacity="0.85"/>
<path d="M0 1.5h3M3 4.5h3" style="stroke:var(--cloth-light)" stroke-width="1.6" stroke-opacity="0.5"/>
</pattern>
<clipPath id="cabClip"><path data-cab-path/></clipPath>
<clipPath id="frontClip"><rect x="46" y="46" width="508" height="328" rx="26"/></clipPath>
<clipPath id="grilleClip"><rect x="72" y="78" width="214" height="214" rx="20"/></clipPath>
<clipPath id="glassClip"><rect x="314" y="78" width="214" height="118" rx="14"/></clipPath>
</defs>
<ellipse class="shadow" cx="300" cy="418" rx="290" ry="12"/>
<rect class="foot" x="78" y="396" width="56" height="16" rx="5"/>
<rect class="foot" x="466" y="396" width="56" height="16" rx="5"/>
<!-- the cabinet: walnut with its grain, a lacquer shine on the shoulders and a bevel -->
<g data-light>
<path class="cab" data-cab-path fill="url(#walnut)"/>
<g clip-path="url(#cabClip)" data-grain></g>
<path class="bevel" data-cab-path/>
<path class="lacquer" fill="url(#lacquer)" data-shine/>
</g>
<!-- the front panel: book-matched veneer, a shade lighter -->
<rect class="front" x="46" y="46" width="508" height="328" rx="26" fill="url(#veneer)"/>
<g clip-path="url(#frontClip)" data-veneer></g>
<rect class="front-edge" x="46" y="46" width="508" height="328" rx="26"/>
<!-- the speaker: woven cloth, the valves' glow through it, the wooden fretwork over it -->
<g clip-path="url(#grilleClip)">
<rect x="72" y="78" width="214" height="214" style="fill:var(--cloth-dark)"/>
<g data-cone>
<rect x="62" y="68" width="234" height="234" fill="url(#cloth)"/>
<circle class="cone" cx="179" cy="185" r="62" data-ring/>
</g>
<ellipse cx="179" cy="205" rx="130" ry="120" fill="url(#valves)" opacity="0" class="valve-glow" data-glow/>
<rect x="72" y="78" width="214" height="214" class="cloth-shade"/>
</g>
<rect class="grille-rim" x="72" y="78" width="214" height="214" rx="20"/>
<g class="fret" data-fret></g>
<!-- the brass badge -->
<g transform="translate(144 300)">
<rect width="70" height="16" rx="3" fill="url(#brass)" stroke="#6d4c14" stroke-width="0.8"/>
<text x="35" y="11.6" class="badge" data-badge>HEARTH</text>
</g>
<!-- the dial: glass lit from behind, the scale, the stations, the needle, the static -->
<rect x="310" y="74" width="222" height="126" rx="17" class="dial-rim"/>
<g clip-path="url(#glassClip)">
<rect x="314" y="78" width="214" height="118" style="fill:var(--glass-off)"/>
<rect x="314" y="78" width="214" height="118" fill="url(#backlight)" opacity="0" data-back/>
<g data-scale></g>
<g data-stations></g>
<text class="now-station" x="421" y="176" data-now-station></text>
<text class="now-song" x="421" y="189" data-now-song></text>
<g data-noise></g>
<g data-needle>
<line class="needle" x1="0" y1="84" x2="0" y2="164"/>
<rect class="needle-foot" x="-3" y="160" width="6" height="5" rx="1"/>
</g>
<rect x="314" y="78" width="214" height="118" fill="url(#glassShine)" data-glass/>
</g>
<rect x="314" y="78" width="214" height="118" rx="14" class="glass-edge"/>
<!-- the magic eye -->
<g transform="translate(421 232)">
<circle r="15" class="eye-rim"/>
<circle r="11.5" fill="#183020" data-eye-dark/>
<circle r="11.5" fill="url(#eyeGlow)" opacity="0" data-eye-glow/>
<path fill="#0f2416" data-eye-wedge/>
<circle r="3.4" fill="#0c1a10"/>
<circle r="11.5" class="eye-glass"/>
</g>
<!-- the knobs: volume and tuning -->
<g class="knob" transform="translate(352 254)" role="slider" tabindex="0" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" data-knob="vol">
<ellipse cx="2" cy="6" rx="27" ry="25" class="knob-shadow"/>
<g data-turn><circle r="26" fill="url(#bakelite)"/><g data-knurl></g><circle r="17" class="knob-cap"/><circle r="11" class="knob-ring"/><line class="knob-mark" x1="0" y1="-24" x2="0" y2="-14"/></g>
<ellipse cx="-8" cy="-9" rx="9" ry="5" class="knob-shine"/>
</g>
<g class="knob" transform="translate(490 254)" role="slider" tabindex="0" aria-label="Tuning" aria-valuemin="88" aria-valuemax="108" data-knob="tune">
<ellipse cx="2" cy="6" rx="27" ry="25" class="knob-shadow"/>
<g data-turn><circle r="26" fill="url(#bakelite)"/><g data-knurl></g><circle r="17" class="knob-cap"/><circle r="11" class="knob-ring"/><line class="knob-mark" x1="0" y1="-24" x2="0" y2="-14"/></g>
<ellipse cx="-8" cy="-9" rx="9" ry="5" class="knob-shine"/>
</g>
<text class="small" x="352" y="296">VOLUME</text>
<text class="small" x="490" y="296">TUNING</text>
<!-- the bakelite keys: power and five stations -->
<g data-keys></g>
</svg>
</section>
<p class="onair"><span class="onair__label">On air</span> <span data-caption>Press On, then a station</span></p>
<p class="sr" aria-live="polite" data-live></p>
</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.
50 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 radio d’epoca da sintonizzare» da Animorio e adattala al mio sitoTutto viene da tre valori: l’accensione (da 0 a 1), il posto della lancetta sul quadrante (da 0 a 1, da 88 a 108 MHz) e il volume. GSAP li anima e un solo ciclo del ticker disegna il resto a ogni fotogramma.
Ogni stazione è una campana attorno al suo posto: il segnale è la campana più alta sotto la lancetta, il disturbo è uno meno il segnale. Il disturbo decide quanti graffi bianchi compaiono sul vetro, quanto trema la luce e quanto vibra la lancetta; il segnale chiude lo spicchio dell’occhio magico e accende il nome sul vetro.
Il tasto On non accende di colpo: una timeline fa due o tre tremolii brevi dell’accensione e poi la sale piano fino a 1 in due secondi e mezzo. Luce del quadrante, bagliore dietro la tela e occhio magico seguono lo stesso numero.
Sulla stazione la tela dell’altoparlante si gonfia a ogni battito: un esponenziale che parte a ogni colpo, al tempo della stazione, moltiplicato per il segnale e per il volume. Le notizie sono parlato: un prodotto di sinusoidi lente che dà colpi irregolari.
La manopola si gira trascinando: l’angolo del puntatore attorno al suo centro, misurato nelle coordinate dell’SVG, sposta la lancetta di un giro e mezzo per tutto il quadrante. Il dito sul vetro porta la lancetta dove tocca, la rotella la sposta poco alla volta, le frecce della tastiera di un decimo di MHz.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
STATIONS | script.js | [['Jazz', 'Blue Hour Session', 92], …] | Le stazioni sul quadrante: nome, cosa suona e battiti al minuto (0 per il parlato). |
BRAND | script.js | 'Hearth' | Il nome della radio, nella testata e sulla targhetta. |
WARM | script.js | 2.4 | I secondi che le valvole impiegano a scaldarsi. |
SWEEP | script.js | 1.1 | I secondi che la lancetta impiega ad arrivare a una stazione da un tasto. |
STATIC | script.js | 1 | Quanto frigge il quadrante fra le stazioni. |
BEAT | script.js | 1 | Quanto batte la tela dell’altoparlante su una stazione. |
LIGHT | script.js | 1 | Quanto il riflesso sulla lacca segue il puntatore. |
Per il lettore di una web radio o di un podcast, il sito di un negozio di modernariato o di un bar con la musica dal vivo, una pagina «ascolta» con poche stazioni o playlist, un caffè con un’atmosfera d’altri tempi.
La radio si stringe con lo schermo ed è alta al massimo quanto lascia la finestra; tutto è un SVG solo, quindi i dettagli restano gli stessi. Col dito si preme un tasto, si gira la manopola o si scorre sul vetro.
Niente tremolii né graffi sul vetro e la tela non batte: l’accensione è una dissolvenza e la lancetta salta sulla stazione; l’occhio magico e il nome sul vetro cambiano lo stesso.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 11 KB; le librerie: GSAP 28 KB.
Provata il 22 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.