diff --git a/viz/index.html b/viz/index.html
index 424d5f0..13fc5ab 100644
--- a/viz/index.html
+++ b/viz/index.html
@@ -1808,9 +1808,11 @@
const MOOD_KEYS = ["glitch", "granular.density", "saturation", "tempo.nudge", "wavetable.morph",
"lfo.rate", "reverb.size", "delay.feedback", "drone.voices", "pad.brightness",
"perc.density", "master.space", "lead.note", "bass.note"];
+ let moodAppliedThisLoad = false; // guard: apply a mood once per page load, not per reconnect
function applyMood(name) {
const m = MOODS[name];
if (!m) return;
+ try { localStorage.setItem("gs_mood", name); } catch (e) {} // your vibe survives a reload
for (const k of MOOD_KEYS) { const t2 = tw(k); t2.mute = 0; t2.gain = 1; t2.offset = 0; }
if (m.storm) {
for (const k in seqs) seqs[k].on = false;
@@ -2244,13 +2246,20 @@
if (Array.isArray(msg.routes)) {
preLayoutRoutes(msg.routes);
clientMatrix = true; // from here YOUR patch is computed locally
- // first visit: greet gently — a song-shaped mood, not the full flood
- let seen = false; try { seen = !!localStorage.getItem("gs_seen"); } catch (e) {}
- if (!seen) {
- try { localStorage.setItem("gs_seen", "1"); } catch (e) {}
+ // greet with your last mood (default: the gentle "first light"). Applied
+ // once per page load — never on a mid-session reconnect, so it can't
+ // clobber what you're playing. A reload no longer drops you into the wall.
+ if (!moodAppliedThisLoad) {
+ moodAppliedThisLoad = true;
+ let firstEver = false, saved = "first light";
+ try { firstEver = !localStorage.getItem("gs_seen"); } catch (e) {}
+ try { saved = localStorage.getItem("gs_mood") || "first light"; } catch (e) {}
setTimeout(() => {
- applyMood("first light");
- eventTicker.unshift({ text: "🌅 first light — a gentle arrangement to start you off. right-click the sky → mood → the full storm when you're ready", tAdded: now(), src: "sys" });
+ applyMood(saved);
+ if (firstEver) {
+ try { localStorage.setItem("gs_seen", "1"); } catch (e) {}
+ eventTicker.unshift({ text: "🌅 first light — a gentle arrangement to start you off. right-click the sky → 🌗 mood for more (or the full storm when you're ready)", tAdded: now(), src: "sys" });
+ }
}, 900);
}
}
@@ -5694,7 +5703,15 @@
function paint() { btn.style.color = on ? "#7cffb2" : "#9fb4d8";
btn.style.borderColor = on ? "rgba(124,255,178,0.5)" : "rgba(120,150,200,0.25)"; }
if (!AC) { btn.style.opacity = "0.3"; btn.title = "Web Audio not supported"; }
- else btn.addEventListener("click", () => { on ? stop() : start(); paint(); });
+ else btn.addEventListener("click", () => {
+ const wasOff = !on;
+ on ? stop() : start(); paint();
+ if (wasOff) { // starting into the raw factory wall? point the way out
+ const anyGroove = Object.keys(seqs).some(k => seqs[k] && seqs[k].on);
+ if (!anyGroove)
+ eventTicker.unshift({ text: "♪ this is the full world at once — for a gentle song, right-click the sky → 🌗 mood → first light", tAdded: now(), src: "sys" });
+ }
+ });
return { update(dests) { if (dests) Object.assign(D, dests); }, pluck };
})();