diff --git a/viz/index.html b/viz/index.html index 2985aa7..9cf026c 100644 --- a/viz/index.html +++ b/viz/index.html @@ -1123,11 +1123,13 @@
-A patch you have played into being — the cables you dragged, the amounts you leaned, the mutes and freezes, the tempo you locked — is a whole instrument, and it deserves to survive the tab closing.
+A patch you have played into being — the cables you dragged, the amounts you leaned, the mutes and freezes, the tempo you locked — is a whole instrument, and it deserves to survive the tab closing. There are two sizes of keepsake: a dimension is everything; a vibe is a feeling you can hand a friend.
-Open ⚙ → MY PATCHES and save. The entire live state is written to your account, keyed to your login (monsterrobotparty@gmail.com): every route with its amount and curve, every group and orbit, the running mix of gains and mutes and freezes, and the godtime tempo. From any device, load it back and the exact instrument returns — the same planet, wired the same way, pulsing at the same locked BPM. (Locally, spells live as patches/*.json on the host; MY PATCHES is that same power for signed-in players, carried to your account and back.)
+Dimensions — the whole world, saved. Open ⚙ → DIMENSIONS and save. The entire live state is written to your account, keyed to your login (monsterrobotparty@gmail.com): every route with its amount and curve, every group and orbit, the running mix of gains and mutes and freezes, and the godtime tempo. From any device, load it back and the exact instrument returns — the same planet, wired the same way, pulsing at the same locked BPM. The top of the list is always ✦ dimension zero — the empty field, the place you build from nothing. (Locally, spells live as patches/*.json on the host; DIMENSIONS is that same power for signed-in players, carried to your account and back.)
+ +Vibes — a feeling, shareable. A dimension is a hard scene save of the whole instrument; a vibe is smaller and lighter — a little bundle of specific source→voice cables, the exact combo that made a moment sing. Right-click a spinning orb in zero (or any node in the full field) → 💾 save as vibe, or open ✨ vibes (the zero pill, or ⚙). Applying a vibe is additive — it lays its cables on top of whatever you already have and can be toggled straight back off, and it never touches a cable you built by hand. Then ⧉ copy the vibe's code (a short GSV1… string) and paste it to a friend, or share a godstrument.pro/#vibe=… link — they see “someone shared a vibe” and choose whether to add it. Build a vibe, save a vibe, share a vibe.
@@ -7473,15 +7475,16 @@ clr.onclick = () => { for (const k in midiBindings) delete midiBindings[k]; saveBindings(); midiStatusEl.textContent = "mappings cleared."; }; panelEl.appendChild(clr); - // My patches — personal presets saved to your account (the full patch: - // routes, tweaks, tempo). Sign in to save; loads restore your instrument. - const th = document.createElement("h3"); th.textContent = "MY PATCHES"; th.style.marginTop = "8px"; + // Dimensions (Z4) — personal full-scene saves to your account (the whole patch: + // routes, tweaks, tempo). A dimension is everything; a vibe is a feeling you can + // hand a friend. Same /api/presets storage — this is a framing rename only. + const th = document.createElement("h3"); th.textContent = "DIMENSIONS"; th.style.marginTop = "8px"; panelEl.appendChild(th); const pMsg = document.createElement("div"); pMsg.className = "hint"; pMsg.style.margin = "2px 0 4px"; - pMsg.textContent = "your own routing, saved to your account."; + pMsg.textContent = "your whole instrument, saved to your account. a dimension is everything."; panelEl.appendChild(pMsg); const nameRow = document.createElement("div"); nameRow.className = "row"; nameRow.style.margin = "4px 0"; - const nameIn = document.createElement("input"); nameIn.type = "text"; nameIn.placeholder = "name this patch…"; nameIn.className = "tplname"; + const nameIn = document.createElement("input"); nameIn.type = "text"; nameIn.placeholder = "name this dimension…"; nameIn.className = "tplname"; const saveBtn = document.createElement("button"); saveBtn.textContent = "save"; saveBtn.className = "midibtn"; saveBtn.onclick = async () => { const n = nameIn.value.trim(); if (!n) return; @@ -7496,6 +7499,7 @@ const loadBtn = document.createElement("button"); loadBtn.textContent = "load"; loadBtn.className = "midibtn"; loadBtn.style.marginLeft = "6px"; loadBtn.onclick = async () => { const n = templateSelect.value; if (!n) return; + if (n === "__zero__") { enterZero(); pMsg.textContent = "entered dimension zero — the empty field"; return; } const r = await gsFetch("/api/presets/" + encodeURIComponent(n), "GET"); if (r.ok && r.data.data) { loadPatch(r.data.data); pMsg.textContent = "loaded “" + n + "”"; } else pMsg.textContent = "load failed"; @@ -7519,10 +7523,11 @@ if (!templateSelect) return; templateSelect.innerHTML = ""; const opt = (v, t) => { const o = document.createElement("option"); o.value = v; o.textContent = t; templateSelect.appendChild(o); }; - if (!(typeof Accounts !== "undefined" && Accounts && Accounts.user)) { opt("", "— sign in to load —"); return; } + opt("__zero__", "✦ dimension zero — the empty field"); // always first: the place you build from nothing + if (!(typeof Accounts !== "undefined" && Accounts && Accounts.user)) { opt("", "— sign in to load your own —"); return; } const r = await gsFetch("/api/presets", "GET"); const names = (r.ok && r.data.presets) ? r.data.presets.map(p => p.name) : []; - if (!names.length) opt("", "— none saved —"); + if (!names.length) opt("", "— none saved yet —"); else names.forEach(nm => opt(nm, nm)); }