diff --git a/GODSTRUMENT_MANUAL_SOURCE.md b/GODSTRUMENT_MANUAL_SOURCE.md index 71b4ec2..df28c04 100644 --- a/GODSTRUMENT_MANUAL_SOURCE.md +++ b/GODSTRUMENT_MANUAL_SOURCE.md @@ -84,7 +84,7 @@ The whole pipeline, then, in four verbs: **SHAPE → CLEAN → BOUND → SPEAK.* ## The sources — the world, listening -Forty-odd feeds, and not one of them was invented for you. Every source in this instrument is a real measurement of a real thing happening right now — a satellite over the Pacific, a trade clearing on Binance, a tremor under Honshu, the Moon at some true longitude computed to the arc-second. The hub does not synthesize the world; it *listens* to it, normalizes what it hears into a clean 0..1 stream, and hands that stream to the matrix as a modulator. What follows is the whole choir, grouped by where in the world each voice lives. For each: what it truly measures, the tempo it naturally moves at, and why *this* — of all things — deserved to be a voice. +Forty-odd feeds, and not one of them was invented to flatter you. **Nearly every source is a real measurement of a real thing happening right now** — a satellite over the Pacific, a trade clearing on Binance, a tremor under Honshu, the Moon at some true longitude computed to the arc-second. A few, honestly, are **models** rather than live gauges: the human birth-and-death tide is a demographic model, and the room sensors (your hand, the light) fall back to a gentle simulator until a real rig is plugged in. Where the world truly is measured the hub does not synthesize it; it *listens*, normalizes what it hears into a clean 0..1 stream, and hands that stream to the matrix as a modulator. What follows is the whole choir, grouped by where in the world each voice lives. For each: what it truly measures, the tempo it naturally moves at, and why *this* — of all things — deserved to be a voice. Read every source through three lenses at once. **The feed**: the actual API or computation behind it. **The character**: how fast it moves, because tempo is destiny in a modulation matrix — a sub-second feed is a rhythm engine, a decade-scale feed is ballast that barely breathes. **The lore**: what it *means* that this signal has a seat in the ensemble. A signal's normalization range (its `lo`/`hi` in **config.json**) is its tuning: it tells the hub what counts as "silent" and what counts as "full open" for that voice. @@ -596,7 +596,7 @@ MIDI-learn any of these at your synth and the planet drives your favorite hardwa 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. -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` and cast the same full state from the ⚙ panel's SPELLS section; MY PATCHES is that power, carried to the cloud and back.) +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.) --- diff --git a/viz/index.html b/viz/index.html index 15dbca4..99d3edb 100644 --- a/viz/index.html +++ b/viz/index.html @@ -547,7 +547,7 @@
Forty-odd feeds, and not one of them was invented for you. Every source in this instrument is a real measurement of a real thing happening right now — a satellite over the Pacific, a trade clearing on Binance, a tremor under Honshu, the Moon at some true longitude computed to the arc-second. The hub does not synthesize the world; it listens to it, normalizes what it hears into a clean 0..1 stream, and hands that stream to the matrix as a modulator. What follows is the whole choir, grouped by where in the world each voice lives. For each: what it truly measures, the tempo it naturally moves at, and why this — of all things — deserved to be a voice.
+Forty-odd feeds, and not one of them was invented to flatter you. Nearly every source is a real measurement of a real thing happening right now — a satellite over the Pacific, a trade clearing on Binance, a tremor under Honshu, the Moon at some true longitude computed to the arc-second. A few, honestly, are models rather than live gauges: the human birth-and-death tide is a demographic model, and the room sensors (your hand, the light) fall back to a gentle simulator until a real rig is plugged in. Where the world truly is measured the hub does not synthesize it; it listens, normalizes what it hears into a clean 0..1 stream, and hands that stream to the matrix as a modulator. What follows is the whole choir, grouped by where in the world each voice lives. For each: what it truly measures, the tempo it naturally moves at, and why this — of all things — deserved to be a voice.
Read every source through three lenses at once. The feed: the actual API or computation behind it. The character: how fast it moves, because tempo is destiny in a modulation matrix — a sub-second feed is a rhythm engine, a decade-scale feed is ballast that barely breathes. The lore: what it means that this signal has a seat in the ensemble. A signal's normalization range (its lo/hi in config.json) is its tuning: it tells the hub what counts as "silent" and what counts as "full open" for that voice.
@@ -1010,7 +1010,7 @@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.
-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 and cast the same full state from the ⚙ panel's SPELLS section; MY PATCHES is that power, carried to the cloud and back.)
+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.)
@@ -1443,16 +1443,24 @@ const n = dests.get(key); if (!n || !n.isNote) continue; const ch = /bass/.test(key) ? 1 : 0, root = seqRootFor(key); + const sq = seqs[key]; // per-step velocity + ratchet ride here const ev = []; for (let b = 0; b < arr.bars; b++) { const st = arr.grid[key][b]; if (st !== "A" && st !== "B" && st !== "C") continue; const steps = arrBank(key, true)[st]; for (let s2 = 0; s2 < 16; s2++) { - const d = stepFirst(steps[s2]); // export the stack's first candidate + const d = stepFirst(steps[s2]); // Play-One → first candidate (a file is deterministic) if (d < 0) continue; - const note = root + SEQ_ROWS[d], tick = (b * 16 + s2) * TICKS16; - ev.push([tick, 0x90 | ch, note, 100], [tick + TICKS16 - 2, 0x80 | ch, note, 0]); + const note = root + SEQ_ROWS[d]; + const vel = Math.round(clamp(sq && sq.vel ? sq.vel[s2] : 0.8, 0.05, 1) * 127); + const rat = clamp(Math.round(sq && sq.rat ? sq.rat[s2] : 1), 1, 4); + const base = (b * 16 + s2) * TICKS16, subLen = TICKS16 / rat; + for (let h = 0; h < rat; h++) { // ratchets → real sub-notes + const t0 = base + Math.round(h * subLen); + const t1 = Math.max(t0 + 1, base + Math.round((h + 1) * subLen) - 2); + ev.push([t0, 0x90 | ch, note, vel], [t1, 0x80 | ch, note, 0]); + } } } if (!ev.length) continue; @@ -2187,6 +2195,10 @@ if (tweaks[key] && tweaks[key].mute >= 0.5) return true; return groupsOf(key).some(g => tweaks["@" + g] && tweaks["@" + g].mute >= 0.5); } + const frozenVals = {}; // freeze: a held photograph per source + function isFrozenLocal(key) { + return groupsOf(key).some(g => tweaks["@" + g] && tweaks["@" + g].freeze >= 0.5); + } function tweakNorm(key, norm) { let v = norm; for (const g of groupsOf(key)) { @@ -2204,6 +2216,10 @@ const sv = {}; for (const [k, n] of sources) { let v = (soloKey && k !== soloKey) ? 0 : tweakNorm(k, n.norm || 0); + if (isFrozenLocal(k)) { // freeze: hold the value it had when the group froze + if (frozenVals[k] == null) frozenVals[k] = v; + v = frozenVals[k]; + } else if (frozenVals[k] != null) delete frozenVals[k]; const sq = seqs[k]; if (sq && sq.on) { // sample & hold: the world only speaks on your steps if (sq.steps[seqCurStep] && sq._sampled !== seqCurStep) { sq.held = v; sq._sampled = seqCurStep; } @@ -5230,8 +5246,10 @@ ctxItem(node.muted ? "unmute" : "mute", () => toggleMuteLocal(node.key), "click"); if (isSrc) ctxItem(soloKey === node.key ? "unsolo" : "solo", () => { soloKey = soloKey === node.key ? null : node.key; }, "⌥-click"); - ctxSlider("level", t0.gain, 0, 2, x => { t0.gain = x; }, x => x.toFixed(2) + "×"); - ctxSlider("trim", t0.offset, -1, 1, x => { t0.offset = x; }); + if (!node.isNote) { // level/trim don't apply to a pitch — no dead sliders + ctxSlider("level", t0.gain, 0, 2, x => { t0.gain = x; }, x => x.toFixed(2) + "×"); + ctxSlider("trim", t0.offset, -1, 1, x => { t0.offset = x; }); + } ctxSep(); // every cable this node carries, each with its amount + unpatch