<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Il fiocco di neve del tuo nome</title>
<style>
  :root { --notte: #0b1630; --notte2: #050a18; --ghiaccio: #eaf3ff; --tenue: #9fb3d6; }
  * { box-sizing: border-box; }
  body { margin: 0; min-height: 100vh; display: grid; place-items: center; padding: 20px 16px;
         background: radial-gradient(ellipse at 50% 30%, var(--notte), var(--notte2) 70%);
         color: var(--ghiaccio); font-family: "Iowan Old Style", Palatino, Georgia, serif; }
  main { width: min(560px, 100%); text-align: center; }
  h1 { font-weight: 400; font-style: italic; font-size: clamp(24px, 6vw, 34px); margin: 0 0 4px; }
  p { color: var(--tenue); margin: 0 0 18px; font-size: 15px; }
  canvas { width: 100%; aspect-ratio: 1; display: block; border-radius: 18px; }
  .riga { display: flex; gap: 10px; margin-top: 16px; }
  input { flex: 1; min-width: 0; font: inherit; font-size: 18px; padding: 12px 14px; border-radius: 12px;
          border: 1px solid #2c4170; background: #0d1a38; color: var(--ghiaccio); outline: none; }
  input:focus { border-color: var(--tenue); }
  button { font: inherit; font-size: 16px; padding: 12px 16px; border-radius: 12px; border: 0; cursor: pointer;
           background: var(--ghiaccio); color: var(--notte); }
</style>
</head>
<body>
<main>
  <h1>Il fiocco di neve del tuo nome</h1>
  <p>Ogni nome cresce in un cristallo diverso. Lo stesso nome dà sempre lo stesso fiocco.</p>
  <canvas id="tela" width="1200" height="1200"></canvas>
  <div class="riga">
    <input id="nome" maxlength="40" placeholder="Scrivi un nome…" value="Natale" aria-label="Nome">
    <button id="scarica">Scarica</button>
  </div>
</main>
<script>
// ---------------------------------------------------------------------------
// Dal nome a un numero (FNV-1a) e da quel numero a una sequenza di "dadi"
// (mulberry32): tutto deterministico, nessuna casualità vera.
// ---------------------------------------------------------------------------
const pulisci = (s) => s.trim().toLowerCase().replace(/\s+/g, " ") || "neve";

function seme(testo) {
  let h = 2166136261;
  for (const c of testo) { h ^= c.codePointAt(0); h = Math.imul(h, 16777619); }
  return h >>> 0;
}

function dado(s) {
  return () => {
    s = (s + 0x6d2b79f5) | 0;
    let t = Math.imul(s ^ (s >>> 15), 1 | s);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

// ---------------------------------------------------------------------------
// Il progetto di un braccio. I cristalli veri crescono a 60 gradi:
// rami e rametti seguono solo quelle direzioni, e ogni braccio è speculare.
// ---------------------------------------------------------------------------
function progetto(nome) {
  const r = dado(seme(pulisci(nome)));
  const tra = (a, b) => a + (b - a) * r();
  const rami = [];
  const n = 3 + Math.floor(r() * 4);                  // da 3 a 6 coppie di rami
  let pos = tra(0.14, 0.26);
  for (let i = 0; i < n && pos < 0.9; i++) {
    const lung = (1 - pos) * tra(0.3, 0.8) * (pos < 0.3 ? 0.7 : 1);
    rami.push({
      pos, lung,
      sub: r() < 0.6 ? 1 + Math.floor(r() * 4) : 0,   // rametti sui rami
      piastra: r() < 0.2,                             // piccola piastra sull'asse
      gemma: r() < 0.3,                               // esagono in punta al ramo
    });
    pos += tra(0.09, 0.22);
  }
  return {
    rami,
    centro: tra(0.08, 0.16),                           // raggio della piastra centrale
    anelli: 1 + Math.floor(r() * 3),
    punta: ["esagono", "rombo", "nulla"][Math.floor(r() * 3)],
    spessore: tra(0.011, 0.016),
    settori: r() < 0.35,                               // "piastre a settori" tra i rami
    felce: r() < 0.3,                                  // dendriti fitti, come una felce
  };
}

// ---------------------------------------------------------------------------
// Disegno. t va da 0 a 1 e fa crescere il cristallo dal centro verso fuori.
// ---------------------------------------------------------------------------
const esagono = (ctx, x, y, raggio) => {
  ctx.beginPath();
  for (let k = 0; k < 6; k++) {
    const a = Math.PI / 6 + (k * Math.PI) / 3;
    ctx.lineTo(x + raggio * Math.cos(a), y + raggio * Math.sin(a));
  }
  ctx.closePath();
};

function braccio(ctx, p, L, t) {
  const cresci = (da, a) => Math.max(0, Math.min(1, (t - da) / (a - da)));
  const spina = L * cresci(0.0, 0.65);
  const sx = Math.sin(Math.PI / 3), sy = -Math.cos(Math.PI / 3);
  ctx.beginPath(); ctx.moveTo(0, -L * p.centro); ctx.lineTo(0, -Math.max(spina, L * p.centro)); ctx.stroke();

  if (p.felce) {                                       // dendriti corti lungo tutto l'asse
    ctx.save(); ctx.lineWidth *= 0.6;
    for (let u = p.centro + 0.06; u < 0.95; u += 0.055) {
      const g = cresci(u * 0.65, u * 0.65 + 0.2), l = L * 0.07 * (1 - u * 0.5) * g;
      if (g <= 0) continue;
      for (const lato of [-1, 1]) { ctx.beginPath(); ctx.moveTo(0, -L * u); ctx.lineTo(lato * sx * l, -L * u + sy * l); ctx.stroke(); }
    }
    ctx.restore();
  }

  for (const ramo of p.rami) {
    const y = -L * ramo.pos;
    const g = cresci(ramo.pos * 0.65, ramo.pos * 0.65 + 0.35);
    if (g <= 0) continue;
    const l = L * ramo.lung * g;
    for (const lato of [-1, 1]) {                       // specchio: sinistra e destra
      const dx = lato * sx, dy = sy;
      if (p.settori && g > 0.6) {                        // velo di ghiaccio tra asse e ramo
        ctx.save(); ctx.globalAlpha *= 0.18;
        ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(dx * l * 0.75, y + dy * l * 0.75); ctx.lineTo(0, y - l * 0.75); ctx.fill();
        ctx.restore();
      }
      ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(dx * l, y + dy * l); ctx.stroke();
      for (let s = 1; s <= ramo.sub; s++) {             // rametti: uno verso la punta, uno verso fuori
        const q = (s / (ramo.sub + 1)) * l;
        const lr = (l - q) * 0.5;
        const bx = dx * q, by = y + dy * q;
        ctx.beginPath(); ctx.moveTo(bx, by); ctx.lineTo(bx, by - lr); ctx.stroke();
        ctx.beginPath(); ctx.moveTo(bx, by); ctx.lineTo(bx + lato * lr * sx, by + lr * 0.5); ctx.stroke();
      }
      if (ramo.gemma && g > 0.8) { esagono(ctx, dx * l, y + dy * l, L * 0.022); ctx.stroke(); }
    }
    if (ramo.piastra && g > 0.5) { esagono(ctx, 0, y, L * 0.05 * g); ctx.stroke(); }
  }

  const fine = cresci(0.6, 0.75);
  if (fine > 0 && p.punta !== "nulla") {
    const s = L * 0.045 * fine;
    if (p.punta === "esagono") esagono(ctx, 0, -L - s * 0.4, s);
    else { ctx.beginPath(); ctx.moveTo(0, -L - s * 2); ctx.lineTo(s, -L); ctx.lineTo(0, -L + s * 0.6); ctx.lineTo(-s, -L); ctx.closePath(); }
    ctx.stroke();
  }
}

function disegna(ctx, nome, t, conNome) {
  const W = ctx.canvas.width, C = W / 2, L = W * (conNome ? 0.335 : 0.42);
  const cy = conNome ? W * 0.45 : C;
  const p = progetto(nome);

  const sfondo = ctx.createRadialGradient(C, cy, 0, C, cy, W * 0.75);
  sfondo.addColorStop(0, "#16295a"); sfondo.addColorStop(1, "#060c1d");
  ctx.fillStyle = sfondo; ctx.fillRect(0, 0, W, W);

  ctx.save();
  ctx.translate(C, cy);
  ctx.lineCap = "round"; ctx.lineJoin = "round";
  for (const passata of [0, 1]) {                      // alone largo, poi linea viva
    ctx.strokeStyle = ctx.fillStyle = passata ? "#f4f9ff" : "rgba(160, 205, 255, 0.35)";
    ctx.lineWidth = L * p.spessore * (passata ? 1 : 3.2);
    ctx.shadowColor = "rgba(150, 200, 255, 0.9)"; ctx.shadowBlur = passata ? W * 0.012 : 0;
    for (let k = 0; k < 6; k++) { ctx.save(); ctx.rotate((k * Math.PI) / 3); braccio(ctx, p, L, t); ctx.restore(); }
    const rc = L * p.centro * Math.min(1, t * 4);
    for (let a = 0; a < p.anelli; a++) { esagono(ctx, 0, 0, rc * (1 - a * 0.3)); ctx.stroke(); }
  }
  ctx.restore();

  if (conNome) {
    ctx.fillStyle = "#eaf3ff"; ctx.textAlign = "center";
    ctx.font = `italic ${W * 0.055}px "Iowan Old Style", Palatino, Georgia, serif`;
    ctx.fillText(nome.trim() || "neve", C, W * 0.9);
  }
}

// ---------------------------------------------------------------------------
// Pagina: il fiocco ricresce a ogni lettera; "Scarica" salva un PNG con il nome.
// ---------------------------------------------------------------------------
const tela = document.getElementById("tela"), ctx = tela.getContext("2d");
const campo = document.getElementById("nome");
let inizio = 0, fotogramma = 0;

function anima(ora) {
  if (!inizio) inizio = ora;
  const t = Math.min(1, (ora - inizio) / 1600);
  disegna(ctx, campo.value, 1 - Math.pow(1 - t, 3), true);
  if (t < 1) fotogramma = requestAnimationFrame(anima);
}
function riparti() { cancelAnimationFrame(fotogramma); inizio = 0; fotogramma = requestAnimationFrame(anima); }

campo.addEventListener("input", riparti);
document.getElementById("scarica").addEventListener("click", () => {
  const grande = document.createElement("canvas");
  grande.width = grande.height = 2000;
  disegna(grande.getContext("2d"), campo.value, 1, true);
  grande.toBlob((b) => {
    const a = document.createElement("a");
    a.href = URL.createObjectURL(b);
    a.download = `fiocco-${pulisci(campo.value).replace(/[^a-z0-9àèéìòù]+/g, "-")}.png`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(a.href), 2000);
  });
});

// Un nome nell'indirizzo (pagina.html#Giulia) apre direttamente il suo fiocco.
let daLink = "";
try { daLink = decodeURIComponent(location.hash.slice(1)); } catch (e) { daLink = ""; }
if (daLink) campo.value = daLink.slice(0, 40);
riparti();
</script>
</body>
</html>

Il fiocco di neve del tuo nome

Scrivi un nome e guardalo crescere in un cristallo di neve. Ogni nome ha il suo, e lo stesso nome dà sempre lo stesso fiocco. Rami a 60 gradi come nei cristalli veri, braccia speculari, piastre e dendriti. Un solo file HTML senza librerie: aprilo nel browser e premi «Scarica» per avere il PNG con il nome, da usare come biglietto o immagine del profilo.

Tool
Claude
Model
Opus 5.5
Type
Code
Date
Licence
Free to use, no conditions

Prompt

Scrivi una pagina HTML con JavaScript, senza librerie, che trasformi un nome in un fiocco di neve unico. Il nome diventa un seme numerico; da quel seme si decide la forma di un braccio (rami a 60 gradi, rametti, piastre esagonali, dendriti) che viene ripetuto sei volte e specchiato, come nei cristalli veri. Lo stesso nome deve dare sempre lo stesso fiocco. Il fiocco cresce con un’animazione a ogni lettera digitata e un pulsante «Scarica» salva un PNG grande con il nome sotto. Commenti in italiano.