🎪 gig layer: pulse cables, speed-at-a-glance, sleeper fold, no ghost cables

Four hands-on changes from live-play feedback (Jono's "what speed are those
things going at?"):

- ghost cables: a patched cable whose source is silent no longer vanishes —
  alpha/width floor (va = max(activeDisp, 0.10)) on the idle line, dashes and
  dot; glow still reserved for active cables.
- speed at a glance: flowLabel() speaks each source's rate in bars of the
  current godtime ("streaming · ≈ 1/8 bar · ≈ every 5 bars · asleep, still
  12m · glacial"); shown in the node right-click live readout and as a second
  line in every loupe source row. Sources track _lastMoveT (last real data).
-  PULSE: per-cable clocked throb — r.pulse ∈ {16,8,4,1} pulses/bar; the
  shaped value rides a raised-cosine synced to seqPhase, so a glacial feed
  (debt, declination) becomes a ≥1/16 groove whose DEPTH is the slow value.
  Cycled via the  chip on each cable row (off → ÷16 → ÷8 → ÷4 → bar),
  serialized with the patch, undo-marked, whitelisted on load.
- 💤 sleeper fold: cableless sources with no signal (norm ≤ 0.02) and no
  movement for 45s fold out of the field; they wake THEMSELVES when their
  feed speaks (lastMove refresh). Bottom-left chip shows the count and
  toggles the fold (localStorage gs_fold). Field view only — skins keep
  their full cosmologies, zero stays zero, disconnected dev view unaffected.

Verified live (static serve + temp hooks, removed & grep-verified 0): pulse
active == norm·window to 0 error over 12 same-frame samples and cycles
÷16→÷8→÷4→bar→off; ghost cable visible on a dead venus.dist route;
labels discriminate (mercury "≈ every 5 bars", neptune "glacial", earth.beat
honestly "waiting…" — it never moves); fold hides dead feeds with chip
"💤 2 quiet feeds folded", chip toggle restores, woken feed rejoins field;
zero console errors; T O B G S + ⌘Z regression green; grimoire + manual
regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
monsterrobotparty 2026-07-25 12:00:05 +10:00
parent 7c6d44b010
commit 12a296f284
2 changed files with 109 additions and 20 deletions

View File

@ -664,6 +664,11 @@
@media (max-width: 520px){ #grimoire .spec .row { grid-template-columns: 1fr; gap: 3px; } }
body.zen > :not(#stage):not(#ctxmenu) { display: none !important; } /* zen — nothing but the sky */
body.perform > :not(#stage):not(#ctxmenu) { display: none !important; } /* performance — pure visuals, no chrome */
/* 💤 the sleeper fold — quiet feeds tuck out of the field; this chip is the door back */
#sleepchip { position: fixed; left: 14px; bottom: 52px; z-index: 12; display: none; cursor: pointer;
background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22);
border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
#sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); }
</style>
<canvas id="stage"></canvas>
<div id="gear" title="controls"></div>
@ -1235,6 +1240,8 @@
<li><b>🕊 PSALM — sing, and the instrument answers.</b> Right-click the sky → <b>🕊 PSALM</b>, allow the microphone, and <b>sing</b>. The pitch is heard, <b>folded to the scale you're in</b> (so you're never out of key), and played as notes — a <b>🕊</b> chip shows the note leaving your throat. And the duet with <b>⚡ GODSPEED</b>: with the arp running, each note you sing <b>latches into the held chord</b>, one at a time — hum four notes and the machine arpeggiates the chord you sang. <b>It listens locally</b>: the analysis happens in the page, in its own silence, and <i>nothing ever leaves the machine</i> — no recording, no upload, just your voice becoming the instrument's.</li>
<li><b>✦ CANON — the shelf of worked examples.</b> Open <b>⚙ → DIMENSIONS</b> and at the top of the list, above your own saved worlds, sit four <b>✦ factory dimensions</b> that ship with the instrument — <i>the heartbeat</i> (a boom-bap kit with the swing and the kick's duck), <i>the wheel within the wheel</i> (WHEELS, made audible), <i>the squeeze</i> (the market pumping GODSQUASH), and <i>quiet arrival</i> (the earth echo, slow and sparse). Load one to hear a feature in its element, then save your own version under a new name — the ✦ ones are <b>read-only</b>, the same for everyone, a place to start rather than a thing to overwrite.</li>
<li><b>🪐 spheres — the music of the spheres.</b> Press <kbd>S</kbd> (or right-click the sky → <b>🪐 spheres</b>) and turn it <b>on</b>: the eight planets join the field as <b>modulation sources</b>, the slowest LFOs there are. Each planet offers three signals — <b>phase</b> (its heliocentric longitude, a sawtooth one full turn per orbit), <b>dist</b> (its eccentric breathing, in and out between perihelion and aphelion — Mercury and Mars swing hard, Venus barely), and <b>beat</b> (its <i>synodic</i> dance against Earth — Venus and Earth clasp every 583.9 days, the pentagram cycle). Route any of them like any source, or just say <i>"mars drive filter."</i> One knob rules time: the <b>rate</b>, from <b>1 to 3650 days of sky per bar</b> — at the default thirty, Venus' year is about seven and a half bars and Jupiter's a hundred and forty-four, real orbits compressed into slow musical arcs. And the <b>☸ orbital ratios</b> button sets the drum lanes' wheels to the true resonances — <b>4 : 8 : 16</b> (Io, Europa and Ganymede, locked at 1 : 2 : 4) and <b>8 : 13</b> (Venus to Earth, the pentagram in wheel form), so the polymeter you hear is one the solar system actually keeps. It is all <b>computed here, at the instrument's own clock</b> — the same JPL ephemeris SOLARGOD uses, vendored in, no network and no server, so the sky is deterministic: the same bar always shows the same heaven, and a saved patch reloads the identical sky.</li>
<li><b>⚡ Pulse — make the slow world playable.</b> Some of the planet's truest signals barely move — the national debt, the Moon's declination, a glacier of a currency. Right-click a node, open its cables, and tap the <b></b> chip on any cable to cycle <b>÷16 · ÷8 · ÷4 · bar</b>: the cable now <i>throbs on the godtime clock</i>, and the slow value becomes the <b>depth</b> of that throb. The debt stops being a frozen offset and becomes a sixteenth-note pressure whose weight is the debt itself — slow data as character, the clock as motion. Off by default; the world's own tempo is still the truth unless you ask for the heartbeat.</li>
<li><b>💤 The sleeper fold — and speed at a glance.</b> The field folds away <b>quiet feeds</b> — sources with no cables, no signal and nothing to say for a minute — behind a small <b>💤 chip</b> (bottom-left) that shows the count and brings them back with one click. The moment a folded feed <i>speaks</i>, it wakes itself and rejoins the field: the world breathes in and out of view. And every cable now moves at <b>the data's own rate</b> — streaming feeds race, glacial ones crawl — with the words to match: hover a column and each source row reads its speed (<i>streaming · ≈ every 3 bars · asleep, still 12m</i>), and a node's right-click readout says the same. What speed is that thing going at? Now it just tells you.</li>
<li><b>📽 The stage — send the visual to any screen.</b> Right-click the sky → <b>the stage</b> and a clean black mirror window opens, fed live from the canvas. Drag it to a projector, an AirPlay display, a second monitor — macOS treats it like any window — and <b>double-click for fullscreen</b>. The stage has a <b>camera</b>: the whole view by default (whatever you're in — a skin, the wheel, mind mode's POV), or right-click any node → <b>project this node</b> and the camera glides after that one sphere through its orbits, or <b>project its station</b> to frame a whole wheel and its satellites. Change cameras live from the menus while the projector runs; the audience sees the camera breathe from subject to subject.</li>
<li><b>🕸 The commune — play together, properly.</b> Right-click the sky → <b>the commune</b>. This is for <b>signed-in players</b>: your session rides the connection, so the hub knows exactly who everyone is — no impostors, no setup, no Bluetooth. One of you <b>hosts</b>: name the room, then <b>✉ invite</b> the others by their username (they must be on the site — invitations reach the present). The invited see the invitation arrive live and <b>join or decline</b>; anyone may <b>leave</b> whenever they wish, and if the host leaves, the room closes with them. Then the sections: members <b>claim voices</b> (right-click any voice, or the panel's picker), or the <b>host allocates</b> them — this bass to her, those pads to him, <b>the godtime itself</b> to whoever conducts — and the hub enforces it: only the section's holder can speak for it. From that moment your levels, mutes, grooves and arranger lanes for what you hold follow your hands on every screen, same living room or opposite sides of the earth. The world — the Source — stays identical for everyone, because it always was. Three people, one planet, one instrument.</li></ul>
@ -3592,8 +3599,11 @@
}
r.active = Math.abs(amt) * shaped; continue;
}
destVals[r.dest] = (destVals[r.dest] || 0) + amt * shaped;
r.active = Math.abs(amt * shaped);
let sh = shaped;
if (r.pulse) // ⚡ PULSE — a glacial value rides a clocked wave: slow data = depth, godtime = motion
sh *= 0.5 - 0.5 * Math.cos(((seqPhase * r.pulse / 16) % 1) * Math.PI * 2);
destVals[r.dest] = (destVals[r.dest] || 0) + amt * sh;
r.active = Math.abs(amt * sh);
}
for (const k in destVals) destVals[k] = clamp(destVals[k], 0, 1);
for (const k in notes) destVals[k] = notes[k];
@ -3779,7 +3789,7 @@
function serializePatch() {
return {
routes: routesArr.map(r => ({ source: r.source, dest: r.dest, amount: r.amount,
curve: r.curve || "lin", gate: r.gate, quantize: r.quantize, label: r.label })),
curve: r.curve || "lin", gate: r.gate, quantize: r.quantize, pulse: r.pulse || undefined, label: r.label })),
tweaks: JSON.parse(JSON.stringify(tweaks)),
seqs: JSON.parse(JSON.stringify(seqs)), // your grooves travel with the patch
arr: { grid: JSON.parse(JSON.stringify(arr.grid)), banks: JSON.parse(JSON.stringify(arr.banks)),
@ -4849,8 +4859,8 @@
const botPad = FOOTER_H + 20;
const usableH = Math.max(120, H - topPad - botPad);
// group sources by family for nicer grouping order
const srcList = Array.from(sources.values());
// group sources by family for nicer grouping order (💤 sleepers sit out of the field)
const srcList = Array.from(sources.values()).filter(n => !n.hidden);
const famOrder = ["body","planet","sky","markets","human","other"];
srcList.sort((a, b) => {
const fa = famOrder.indexOf(a.family), fb = famOrder.indexOf(b.family);
@ -4883,6 +4893,41 @@
const loupe = { on: true, active: false, side: null, y: 0, rows: [], frozen: false, panel: null };
try { loupe.on = localStorage.getItem("gs_loupe") !== "0"; } catch (e) {}
// ---- 💤 the sleeper fold — a calm first load ------------------------------
// A hundred sources where thirty are near-silent is the deep-god view answering
// a question nobody asked yet. Cableless sources with no signal (norm ≈ 0) and
// no recent movement fold out of the field; they wake THEMSELVES the moment
// their feed speaks (lastMove refreshes), and the 💤 chip is the door to all of
// them. Field view only — the skins keep their full cosmologies, zero is zero.
let sleepFold = true;
try { sleepFold = localStorage.getItem("gs_fold") !== "0"; } catch (e) {}
let sleepChipEl = null;
function sleepChip(nAsleep) {
if (!sleepChipEl) {
sleepChipEl = document.createElement("div"); sleepChipEl.id = "sleepchip";
sleepChipEl.onclick = () => { sleepFold = !sleepFold;
try { localStorage.setItem("gs_fold", sleepFold ? "1" : "0"); } catch (e) {}
layoutDirty = true; };
document.body.appendChild(sleepChipEl);
}
const txt = nAsleep ? (sleepFold ? "💤 " + nAsleep + " quiet feeds folded — show" : "💤 fold " + nAsleep + " quiet feeds") : "";
if (sleepChipEl._t !== txt) { sleepChipEl._t = txt; sleepChipEl.textContent = txt; sleepChipEl.style.display = txt ? "block" : "none"; }
}
function sleepFoldPass(t) {
const canFold = !chakraMode && !zeroMode && connected;
const cabled = new Set();
for (const r of routesArr) cabled.add(r.source);
let slept = 0;
for (const n of sources.values()) {
const asleep = canFold && !cabled.has(n.key) && !selection.has(n.key) && soloKey !== n.key &&
(n.norm || 0) <= 0.02 && (t - (n._lastMoveT != null ? n._lastMoveT : -1e9)) > 45;
if (asleep) slept++;
const hide = asleep && sleepFold;
if (hide !== !!n.hidden) { n.hidden = hide; layoutDirty = true; }
}
sleepChip(canFold ? slept : 0);
}
function updateLoupe(mx, my) {
if (!loupe.on || chakraMode || performMode || zenMode || !columnInfo
|| inspectEl.classList.contains("open") || arr.open) { loupe.active = false; return; }
@ -4950,7 +4995,14 @@
ctx.fillText(truncate(n.label, 22), panelX + 30, ry + ROWH / 2);
ctx.textAlign = "right"; ctx.font = "500 10px 'Helvetica Neue', Arial, sans-serif";
ctx.fillStyle = "rgba(150,170,200,0.7)";
ctx.fillText(n.isNote ? midiToName(n.raw) : fmtRaw(n.rawDisp != null ? n.rawDisp : n.raw), panelX + panelW - 8, ry + ROWH / 2);
if (n.kind === "source") { // sources answer Jono's question right in the row: value, then speed
ctx.fillText(fmtRaw(n.rawDisp != null ? n.rawDisp : n.raw), panelX + panelW - 8, ry + 8);
ctx.font = "500 8px 'Helvetica Neue', Arial, sans-serif";
ctx.fillStyle = "rgba(150,170,200,0.55)";
ctx.fillText(flowLabel(n), panelX + panelW - 8, ry + ROWH - 6);
} else {
ctx.fillText(n.isNote ? midiToName(n.raw) : fmtRaw(n.rawDisp != null ? n.rawDisp : n.raw), panelX + panelW - 8, ry + ROWH / 2);
}
rows.push({ node: n, x0: panelX, y0: ry, x1: panelX + panelW, y1: ry + ROWH });
}
ctx.strokeStyle = "rgba(150,180,230,0.3)"; ctx.lineWidth = 1;
@ -5274,6 +5326,7 @@
curve: r.curve || "lin",
gate: (r.gate != null ? r.gate : undefined),
quantize: r.quantize || undefined,
pulse: ([16, 8, 4, 1].indexOf(r.pulse) >= 0 ? r.pulse : 0), // ⚡ PULSE — throb this cable on the clock (pulses per bar; 0 = off)
label: r.label || "",
dashPhase: Math.random() * 40
};
@ -5449,6 +5502,7 @@
}
}
sleepFoldPass(t); // 💤 quiet feeds fold; they wake themselves when the world speaks
if (layoutDirty) doLayout();
spheresFrame(); // 🪐 RESONANCE — refresh the sphere sources on the great-year clock
if (spheresMeterEls) spheresPanelTick(); // live meters while the panel is open
@ -5466,6 +5520,7 @@
n._flowPrev = n.norm;
const ft = Math.max(fv, n.firePulse || 0); // an event firing IS data arriving
n.flowDisp = approach(n.flowDisp || 0, ft, ft > (n.flowDisp || 0) ? 0.5 : 0.05, dt);
if (ft > 0.03) n._lastMoveT = t; // remember when this feed last actually spoke
if (n.y !== n.targetY && n.targetY != null) n.y = approach(n.y, n.targetY, 0.12, dt);
if (n.firePulse > 0) n.firePulse = Math.max(0, n.firePulse - dt * 1.8);
}
@ -5493,7 +5548,7 @@
drawShockwaves(t, dt);
drawParticles(dt);
if (chakraMode) drawChakraView(t);
else for (const n of sources.values()) drawSourceNode(n, t);
else for (const n of sources.values()) { if (!n.hidden) drawSourceNode(n, t); } // 💤 sleepers sit out
drawAspectCables(t); // the sky's geometry, drawn between its spheres
for (const n of dests.values()) drawDestNode(n, t);
if (!chakraMode) drawLoupe(t); // the hover zoom over a dense column
@ -5535,6 +5590,7 @@
const d = dests.get(r.dest);
if (!s || !d) continue;
const a = r.activeDisp;
const va = Math.max(a, 0.10); // a patched cable NEVER vanishes: a silent source still shows its wire
const x1 = s.x + NODE_R + 6, y1 = s.y;
const x2 = d.x - NODE_R - 6, y2 = d.y;
@ -5549,21 +5605,21 @@
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(cx1, y1, cx2, y2, x2, y2);
ctx.lineWidth = 1 + a * 3.5;
ctx.strokeStyle = rgba(col, 0.08 + a * 0.30);
ctx.lineWidth = 1 + va * 3.5;
ctx.strokeStyle = rgba(col, 0.08 + va * 0.30);
ctx.setLineDash([]);
ctx.stroke();
if (a > 0.02) {
// glowing flowing dashes
{
// glowing flowing dashes — drawn for EVERY cable; a dead source just crawls
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(cx1, y1, cx2, y2, x2, y2);
ctx.lineWidth = 1.4 + a * 4.5;
ctx.strokeStyle = rgba(glow, 0.35 + a * 0.5);
ctx.setLineDash([2 + a * 8, 10 - a * 4]);
ctx.lineWidth = 1.4 + va * 4.5;
ctx.strokeStyle = rgba(glow, 0.30 + va * 0.5);
ctx.setLineDash([2 + va * 8, 10 - va * 4]);
ctx.lineDashOffset = r.dashPhase;
if (fxLevel > 1) { // shadow blur on 50+ beziers is the #1 cost
if (fxLevel > 1 && a > 0.02) { // shadow blur on 50+ beziers is the #1 cost; no glow on silent wires
ctx.shadowColor = rgba(col, 0.6);
ctx.shadowBlur = 6 + a * 14;
}
@ -5574,9 +5630,9 @@
const pt = ((-r.dashPhase * 0.004) % 1 + 1) % 1;
const bp = bezierPoint(x1, y1, cx1, y1, cx2, y2, x2, y2, pt);
ctx.beginPath();
ctx.arc(bp.x, bp.y, 1.5 + a * 3, 0, Math.PI * 2);
ctx.fillStyle = rgba(rgbMix(col, 0.5), 0.6 + a * 0.4);
if (fxLevel > 1) {
ctx.arc(bp.x, bp.y, 1.5 + va * 3, 0, Math.PI * 2);
ctx.fillStyle = rgba(rgbMix(col, 0.5), 0.5 + va * 0.4);
if (fxLevel > 1 && a > 0.02) {
ctx.shadowColor = rgba(col, 0.9);
ctx.shadowBlur = 8 + a * 10;
}
@ -7712,6 +7768,7 @@
function nodeAt(mx, my) {
let hit = null, best = (NODE_R + 12) * (NODE_R + 12);
for (const n of sources.values()) {
if (n.hidden) continue; // 💤 a folded sleeper is not a click target
const dx = mx - n.x, dy = my - n.y, d2 = dx * dx + dy * dy;
if (d2 < best) { best = d2; hit = n; }
}
@ -7988,6 +8045,23 @@
if (a >= 10) return v.toFixed(1);
return v.toFixed(3);
}
// Jono's question, answered in words: "what speed is that thing going at?"
// Inverts the flow curve (flow = √(rate·8)) into ≈seconds per full 0..1 swing,
// then speaks it in bars of the CURRENT godtime — the musician's unit.
const fmtAgo = (s) => s < 90 ? Math.round(s) + "s" : s < 5400 ? Math.round(s / 60) + "m" : Math.round(s / 3600) + "h";
function flowLabel(n) {
const f = n.flowDisp || 0;
if (f >= 0.85) return "streaming";
if (f <= 0.02) {
const still = lastFrame - (n._lastMoveT != null ? n._lastMoveT : lastFrame);
return still > 60 ? "asleep · still " + fmtAgo(still) : "waiting…";
}
const bars = (8 / (f * f)) / (60 / godtime.bpm * 4);
if (bars < 0.7) return " 1/" + clamp(Math.round(1 / bars), 2, 32) + " bar";
if (bars < 1.5) return " every bar";
if (bars < 300) return " every " + Math.round(bars) + " bars";
return "glacial";
}
let tickerPins = new Set(); // your watchlist — live values riding the ticker
try { tickerPins = new Set(JSON.parse(localStorage.getItem("gs_pins") || "[]")); } catch (e) {}
function savePins() { try { localStorage.setItem("gs_pins", JSON.stringify([...tickerPins])); } catch (e) {} }
@ -8043,7 +8117,8 @@
const sc = spark.getContext("2d");
const drawLive = () => {
lv.textContent = "live: " + fmtVal(node.raw) +
(typeof node.norm === "number" ? " · " + node.norm.toFixed(2) + " into the matrix" : "");
(typeof node.norm === "number" ? " · " + node.norm.toFixed(2) + " into the matrix" : "") +
" · " + flowLabel(node);
const h = node.hist || [];
sc.clearRect(0, 0, spark.width, spark.height);
if (h.length > 1) {
@ -8092,9 +8167,16 @@
sl.type = "range"; sl.min = "0"; sl.max = "1"; sl.step = "0.01";
sl.value = String(rt.amount != null ? rt.amount : 0.5); sl.title = "amount";
sl.oninput = () => setRouteAmountLocal(rt.source, rt.dest, parseFloat(sl.value));
const pu = document.createElement("span"); pu.className = "x"; // ⚡ PULSE — cycle off → ÷16 → ÷8 → ÷4 → bar
pu.title = "⚡ pulse — throb this cable on the clock (slow data becomes depth on a synced wave)";
const PULSE_STEPS = [0, 16, 8, 4, 1];
const paintPu = () => { pu.textContent = rt.pulse ? "⚡" + (rt.pulse === 1 ? "bar" : "÷" + rt.pulse) : "⚡"; pu.style.opacity = rt.pulse ? "1" : "0.4"; };
paintPu();
pu.onclick = () => { ungodlyMark("pulse");
rt.pulse = PULSE_STEPS[(PULSE_STEPS.indexOf(rt.pulse || 0) + 1) % PULSE_STEPS.length]; paintPu(); };
const x = document.createElement("span"); x.className = "x"; x.textContent = "×"; x.title = "unpatch";
x.onclick = () => { ungodlyMark("unpatch"); removeRouteLocal(rt.source, rt.dest); row.remove(); };
row.appendChild(lbl); row.appendChild(sl); row.appendChild(x);
row.appendChild(lbl); row.appendChild(sl); row.appendChild(pu); row.appendChild(x);
cb.appendChild(row);
}
if (rel.length) ctxItem("💾 save these as a vibe", () => { // this node's cables → a shareable vibe (Z3)

View File

@ -668,6 +668,11 @@
@media (max-width: 520px){ #grimoire .spec .row { grid-template-columns: 1fr; gap: 3px; } }
body.zen > :not(#stage):not(#ctxmenu) { display: none !important; } /* zen — nothing but the sky */
body.perform > :not(#stage):not(#ctxmenu) { display: none !important; } /* performance — pure visuals, no chrome */
/* 💤 the sleeper fold — quiet feeds tuck out of the field; this chip is the door back */
#sleepchip { position: fixed; left: 14px; bottom: 52px; z-index: 12; display: none; cursor: pointer;
background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22);
border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
#sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); }
</style>
<style>
/* standalone-manual overrides — render the grimoire as a normal scrolling page */
@ -1215,6 +1220,8 @@
<li><b>🕊 PSALM — sing, and the instrument answers.</b> Right-click the sky → <b>🕊 PSALM</b>, allow the microphone, and <b>sing</b>. The pitch is heard, <b>folded to the scale you're in</b> (so you're never out of key), and played as notes — a <b>🕊</b> chip shows the note leaving your throat. And the duet with <b>⚡ GODSPEED</b>: with the arp running, each note you sing <b>latches into the held chord</b>, one at a time — hum four notes and the machine arpeggiates the chord you sang. <b>It listens locally</b>: the analysis happens in the page, in its own silence, and <i>nothing ever leaves the machine</i> — no recording, no upload, just your voice becoming the instrument's.</li>
<li><b>✦ CANON — the shelf of worked examples.</b> Open <b>⚙ → DIMENSIONS</b> and at the top of the list, above your own saved worlds, sit four <b>✦ factory dimensions</b> that ship with the instrument — <i>the heartbeat</i> (a boom-bap kit with the swing and the kick's duck), <i>the wheel within the wheel</i> (WHEELS, made audible), <i>the squeeze</i> (the market pumping GODSQUASH), and <i>quiet arrival</i> (the earth echo, slow and sparse). Load one to hear a feature in its element, then save your own version under a new name — the ✦ ones are <b>read-only</b>, the same for everyone, a place to start rather than a thing to overwrite.</li>
<li><b>🪐 spheres — the music of the spheres.</b> Press <kbd>S</kbd> (or right-click the sky → <b>🪐 spheres</b>) and turn it <b>on</b>: the eight planets join the field as <b>modulation sources</b>, the slowest LFOs there are. Each planet offers three signals — <b>phase</b> (its heliocentric longitude, a sawtooth one full turn per orbit), <b>dist</b> (its eccentric breathing, in and out between perihelion and aphelion — Mercury and Mars swing hard, Venus barely), and <b>beat</b> (its <i>synodic</i> dance against Earth — Venus and Earth clasp every 583.9 days, the pentagram cycle). Route any of them like any source, or just say <i>"mars drive filter."</i> One knob rules time: the <b>rate</b>, from <b>1 to 3650 days of sky per bar</b> — at the default thirty, Venus' year is about seven and a half bars and Jupiter's a hundred and forty-four, real orbits compressed into slow musical arcs. And the <b>☸ orbital ratios</b> button sets the drum lanes' wheels to the true resonances — <b>4 : 8 : 16</b> (Io, Europa and Ganymede, locked at 1 : 2 : 4) and <b>8 : 13</b> (Venus to Earth, the pentagram in wheel form), so the polymeter you hear is one the solar system actually keeps. It is all <b>computed here, at the instrument's own clock</b> — the same JPL ephemeris SOLARGOD uses, vendored in, no network and no server, so the sky is deterministic: the same bar always shows the same heaven, and a saved patch reloads the identical sky.</li>
<li><b>⚡ Pulse — make the slow world playable.</b> Some of the planet's truest signals barely move — the national debt, the Moon's declination, a glacier of a currency. Right-click a node, open its cables, and tap the <b></b> chip on any cable to cycle <b>÷16 · ÷8 · ÷4 · bar</b>: the cable now <i>throbs on the godtime clock</i>, and the slow value becomes the <b>depth</b> of that throb. The debt stops being a frozen offset and becomes a sixteenth-note pressure whose weight is the debt itself — slow data as character, the clock as motion. Off by default; the world's own tempo is still the truth unless you ask for the heartbeat.</li>
<li><b>💤 The sleeper fold — and speed at a glance.</b> The field folds away <b>quiet feeds</b> — sources with no cables, no signal and nothing to say for a minute — behind a small <b>💤 chip</b> (bottom-left) that shows the count and brings them back with one click. The moment a folded feed <i>speaks</i>, it wakes itself and rejoins the field: the world breathes in and out of view. And every cable now moves at <b>the data's own rate</b> — streaming feeds race, glacial ones crawl — with the words to match: hover a column and each source row reads its speed (<i>streaming · ≈ every 3 bars · asleep, still 12m</i>), and a node's right-click readout says the same. What speed is that thing going at? Now it just tells you.</li>
<li><b>📽 The stage — send the visual to any screen.</b> Right-click the sky → <b>the stage</b> and a clean black mirror window opens, fed live from the canvas. Drag it to a projector, an AirPlay display, a second monitor — macOS treats it like any window — and <b>double-click for fullscreen</b>. The stage has a <b>camera</b>: the whole view by default (whatever you're in — a skin, the wheel, mind mode's POV), or right-click any node → <b>project this node</b> and the camera glides after that one sphere through its orbits, or <b>project its station</b> to frame a whole wheel and its satellites. Change cameras live from the menus while the projector runs; the audience sees the camera breathe from subject to subject.</li>
<li><b>🕸 The commune — play together, properly.</b> Right-click the sky → <b>the commune</b>. This is for <b>signed-in players</b>: your session rides the connection, so the hub knows exactly who everyone is — no impostors, no setup, no Bluetooth. One of you <b>hosts</b>: name the room, then <b>✉ invite</b> the others by their username (they must be on the site — invitations reach the present). The invited see the invitation arrive live and <b>join or decline</b>; anyone may <b>leave</b> whenever they wish, and if the host leaves, the room closes with them. Then the sections: members <b>claim voices</b> (right-click any voice, or the panel's picker), or the <b>host allocates</b> them — this bass to her, those pads to him, <b>the godtime itself</b> to whoever conducts — and the hub enforces it: only the section's holder can speak for it. From that moment your levels, mutes, grooves and arranger lanes for what you hold follow your hands on every screen, same living room or opposite sides of the earth. The world — the Source — stays identical for everyone, because it always was. Three people, one planet, one instrument.</li></ul>