diff --git a/viz/index.html b/viz/index.html index ad7130d..b751040 100644 --- a/viz/index.html +++ b/viz/index.html @@ -2566,6 +2566,83 @@ if (typeof p.bpm === "number") godtime.bpm = clamp(p.bpm, 40, 180); layoutDirty = true; } + + // ---- zero mode β dimension zero ------------------------------------------- + // The calm front door: not a muted world (moods do that) but an EMPTY one β no + // cables, nothing sounding β that you build by hand. It is the SAME matrix + // (routesArr/tweaks/synth), just started from nothing; leaving zero restores + // the world you had. Client-local, like moods β the hub/other users untouched. + let zeroMode = false; + function enterZero() { + if (!zeroMode) { // snapshot what you had, so exiting can restore it + try { localStorage.setItem("gs_prezero", JSON.stringify(serializePatch())); } catch (e) {} + } + clientMatrix = true; // own the matrix so the hub can't re-populate the cables + loadPatch({ routes: [], tweaks: {}, seqs: {} }); // empty the field + zeroMode = true; + try { localStorage.setItem("gs_mood", "0 zero"); } catch (e) {} + eventTicker.unshift({ text: "π± zero β the field is empty. build your own instrument: right-click a source β patch to β a voice, then press βͺ", tAdded: now(), src: "sys" }); + layoutDirty = true; + } + function exitZeroToMood(name) { // leave zero back into the full world, then a mood + if (zeroMode) { + zeroMode = false; + try { const pz = localStorage.getItem("gs_prezero"); if (pz) loadPatch(JSON.parse(pz)); } catch (e) {} + } + applyMood(name); + } + + // The three doors, shown once on a first-ever visit (replaces the silent drop + // into first light). Backdrop/esc = first light, the gentle default. + function showFirstVisitCard() { + if (document.getElementById("firstcard")) return; + const wrap = document.createElement("div"); + wrap.id = "firstcard"; + Object.assign(wrap.style, { position: "fixed", inset: "0", zIndex: "70", display: "flex", + alignItems: "center", justifyContent: "center", background: "rgba(6,10,20,0.72)", + backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" }); + const card = document.createElement("div"); + Object.assign(card.style, { maxWidth: "640px", width: "88%", textAlign: "center", + color: "#d6e2f4", fontFamily: "inherit" }); + const h = document.createElement("div"); + h.textContent = "one world, three doors"; + Object.assign(h.style, { fontWeight: "800", fontSize: "clamp(22px,4vw,32px)", letterSpacing: "2px", marginBottom: "6px" }); + const sub = document.createElement("div"); + sub.textContent = "the planet is already playing. choose how you meet it."; + Object.assign(sub.style, { color: "rgba(165,185,218,0.75)", fontStyle: "italic", marginBottom: "26px", fontSize: "14px" }); + card.appendChild(h); card.appendChild(sub); + const row = document.createElement("div"); + Object.assign(row.style, { display: "flex", gap: "14px", flexWrap: "wrap", justifyContent: "center" }); + const doors = [ + ["π±", "start from zero", "an empty field. build the instrument yourself, one cable at a time.", () => enterZero()], + ["π ", "first light", "a gentle arrangement β a bass line, soft hats, a small melody. ease in.", () => applyMood("first light")], + ["β", "the full storm", "every one of the world's hands on it at once. the factory instrument.", () => applyMood("the full storm")], + ]; + for (const [emoji, title, desc, act] of doors) { + const b = document.createElement("div"); + Object.assign(b.style, { flex: "1 1 170px", minWidth: "160px", maxWidth: "200px", cursor: "pointer", + padding: "20px 16px", borderRadius: "16px", background: "rgba(20,28,44,0.7)", + border: "1px solid rgba(120,150,200,0.28)", transition: "transform .12s, border-color .12s" }); + b.onmouseenter = () => { b.style.transform = "translateY(-3px)"; b.style.borderColor = "rgba(150,200,255,0.7)"; }; + b.onmouseleave = () => { b.style.transform = ""; b.style.borderColor = "rgba(120,150,200,0.28)"; }; + b.innerHTML = '