The audit's verdict was that Godstrument is short on REACH, not features:
everything hid behind an invisible right-click, and everything you made
died in one tab. Four builds against exactly that.
☰ EVERYTHING — a visible, grouped index of the whole instrument (view /
play / your world / together / out / start here). Labels lead with the
plain verb and wear the god-name behind: "arpeggiator · GODSPEED",
"record to a file · TESTAMENT". Critically it is ONE data-driven list
with TWO renderers — the ☰ panel and the right-click sky menu both
render skyGroups(), so a feature can never appear in one and not the
other. (The sky menu's 90 hand-written lines became a 17-line loop.)
Submenus expand in place, so touch works.
☄ THE OMENS — the instrument always knew when the world was doing
something rare and never said so. Reads the live feeds and ranks what's
remarkable: a full moon, a harmonious sky, satellites overhead, a quake
that just landed, your solar return. One line whispers into the ticker
on arrival; the full reading is a tap away. Verified against the real
planet — it read a 91%-lit moon, an unusually harmonious sky and a
convulsing bitcoin, all true at the time.
🌍 SHARED WORLDS — the whole instrument (cables, grooves, beat, kit,
scale, tempo) folded into one link, gzipped by the browser's own
CompressionStream: ~10KB of patch becomes a ~620-character URL. It
rides the fragment, so nothing is uploaded, no account or gallery is
needed, and there is nothing to take down. The receiver is always
asked, never overwritten, and ⌘Z restores theirs. Round-trip verified:
a 12-wheel hat and 137bpm came back exactly. Deliberately NOT gated on
the hub handshake — a shared link must open even when the hub is down.
📱 THE PHONE FORK — a touch device is offered lifestrument once
(remembered, never forced), and the top bar folds to fit a narrow
screen either way.
Plus: the expression-lane drag was written out byte-identically in
three places; it is now one shared helper that speaks pointer events,
so velocity/chance/ratchet/p-lock lanes work under a thumb too.
Grimoire documents all four; manual regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Correctness (four were my own regressions from the REVELATION wave):
· ☸ WHEELS polymeter permanently detuned after any skipped frame —
seqAbsStep advanced by ++ per transition, but one frame can cross two
16ths (bpm ≳155, or the clamped dt on every tab refocus). Proven to
drift 12 steps out at 174bpm; now advances by the true distance, so
seqAbsStep % 16 === seqCurStep always holds.
· Sample & hold with exactly one lit step froze on its first sample
forever (guard keyed on the wrapped step, which never changed) — now
keyed on the absolute step.
· PSALM double-activation left the MICROPHONE HOT after "stop": the
re-entrancy guard sat after the getUserMedia await, orphaning the
first stream/interval/context. Guard is now synchronous, a stop
during the prompt is honoured, and a second tap while starting
cancels. Mic off means off — the promise the feature makes.
· Live-recording a drum tap into a short wheel wrote steps past wlen
that never played; the tap now folds onto the lane's own wheel.
· Switching MIDI out port left notes droning on the old port (pending
off-timers fired at the NEW one) — midiFlushOut() releases every held
note + all-notes-off before the swap.
· world_quakes' seen-set grew unbounded on long-running rigs.
Security: stored XSS via the commune room name — the one untrusted
string reaching innerHTML unescaped (hub only lower-cased it). Escaped
client-side at both sinks, and the hub now strips markup chars too.
Robustness: world_ephemeris/world_almanac imported swisseph unguarded,
so a machine without pyswisseph crash-looped them every 10s forever
against the supervisor's exit-0 contract — both now retire quietly
(verified by simulating the missing dep), and pyswisseph is documented
in requirements.
Mobile: the top bar's five separately-pinned chips collided below
~500px — the first thing a phone visitor saw was a broken header. Now
a clean row with the account chip on its own line and the source
counter moved left.
Housekeeping: dead measureSpaced(), dead #account .code selector, and
an 888K stale worktree removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The front door becomes a skin: the first-visit card grows from three doors
to six, the walked door is REMEMBERED (gs_door) and opens the same way on
every arrival, and it's re-choosable any time (right-click sky → 🚪 the
doors, or say "doors"). Each door is one DOORS registry entry + one act —
delete the entry, the door is gone.
The classics (unchanged acts): 🌱 start from zero · 🌅 first light ·
⛈ the full storm. The new gig doors:
- 🎪 the rig — walk on stage: first light applied, beat panel + GODSPEED
already open, and the sleeper fold tightened so only the ~8 liveliest
uncabled hands stay in the field (8th-liveliest flowDisp sets the bar).
- 🃏 the deal — the world deals five cables from its liveliest feeds
(family-diverse) onto a shuffled spread of ten dests, ONE card always
⚡pulsing (÷16 or ÷8); "🃏 deal again" chip; every arrival deals fresh;
each hand is an ungodly mark so ⌘Z takes it back. Voice: "deal" redeal
while the deal is your door.
- 🌒 the dawn — first light applied, then the players take the stage one
at a time (~1.25s apart, cabled first, then by liveliness, 18 arrivals),
each narrated in the ticker; cables only draw once their player is on
stage (drawCables skips hidden sources); when the entrance ends the
sleeper fold reconciles the rest.
Verified live (static serve + temp hook, removed & grep-verified 0):
six cards render; deal → exactly 5 routes, 1 pulsing, chip visible,
gs_door saved; redeal changes the hand, marks undo, ⌘Z restores the
previous hand exactly; rig → beat+GODSPEED open, field folded tight,
deal furniture closed; dawn → 24/24 hidden at curtain, players arrive
one-by-one with ticker narration and their cables, finale reconciles to
0 hidden; zero console errors; T O B G S + ⌘Z regression green;
grimoire + manual regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Two live-play fixes:
- Zoom: trackpad pinch (ctrl+wheel) over a DOM overlay (right-click menu,
panels) used to hit the BROWSER's page zoom, which the canvas then made
un-undoable because it preventDefaults every wheel — zoomed-in with no way
out except re-opening a menu. Now a capture-phase window listener catches
pinch everywhere and drives the app's own view zoom (shared zoomAt());
Safari's gesture* events are blocked too. Plain scrolling in menus and
panels is untouched; canvas behavior unchanged (verified single-fire).
- Cables: dash/blip speed was uniform (activity-only). Each source now
carries flowDisp — an EMA of |Δnorm|/s with fast attack + slow release —
and every cable's dashPhase advances at 6 + flow·240 + activity·40, with
the pulse dot riding the same phase. A ticking feed streams, a glacial
feed crawls (venus.dist ~0 flow vs mercury.phase saturated, measured);
event sources surge on fire via firePulse.
Verified: pinch in/out over overlay drives view.z and is defaultPrevented,
plain scroll not hijacked, canvas single-fire, flows measured per-source
via temp hook (removed, grep-verified 0), zero console errors, T O B G S
+ ⌘Z regression green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vendor SOLARGOD@6391273's dependency-free JPL ephemeris (viz/spheres.js) and
make the solar system a modulation source, computed locally at the instrument's
own clock — no network, no server, no sockets.
- viz/spheres.js: ephem.js verbatim + the minimal lib subset inlined; loaded via
a <script type="module"> bridge that self-checks (Mars @ JD 2461236.5 within
0.005 AU of Horizons ground truth) and hangs a frozen window.SPHERES; every
consumer guarded so a missing/broken file still boots the app.
- sphere.<planet>.{phase,dist,beat} sources for the 8 planets, routable like any
S&H source; the great-year clock maps 1 bar = N days of sky (log knob 1..3650,
default 30) off seqPhase alone, so scrub/tempo/reload reproduce the same sky.
- 🪐 spheres panel (S / right-click sky): per-planet live meters, rate knob,
☸ orbital-ratios button (drum wheels → 4:8:16 Io:Europa:Ganymede, 8:13
Venus:Earth pentagram), on/off; godspeak: spheres on/off·faster/slower,
"<planet> drive <dest>". spheres {on,rate} travel in the patch.
- grimoire chapter + regenerated viz/manual.html.
Verified: self-check green; determinism (jupiter.phase@bar16 identical across
reload); pentagram (venus.beat period 1.59 bars ≈ 583.9/365, within 2%); ratios
set exact wlens and ⌘Z reverts; zero console errors; ♪ T O B G + ⌘Z all work;
spheres default off on fresh boot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four grimoire entries in the house voice, fact-checked against what was
verified: 📼 TESTAMENT ("it is written"), ☸ WHEELS (Ezekiel 1:16 — "a
wheel in the middle of a wheel"), 🕊 PSALM (the local-listening privacy
line — nothing leaves the machine), ✦ CANON (the read-only shelf of
worked examples). Manual regenerated (27 sections).
Cross-feature QA: all four surfaces coexist with the pantheon and the
crossover — TESTAMENT ⏺ button, WHEELS chips in all 5 beat lanes,
PSALM/TESTAMENT/GODSPEED ctxItems — zero console errors, groove
self-check green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mic pitch → folded to the global scale → played as notes, or (when ⚡
GODSPEED is on) latched into the arp one note at a time. The mic lives
in its OWN throwaway AudioContext — it is analysed, never routed into
the music graph (zero feedback), and nothing leaves the page. A pure
autocorrelation detector psalmDetect(buf,sr)→{freq,clarity,rms} (80–1000
Hz, parabolic interpolation) and a pure note state-machine psalmStep()
(2 stable frames to fire, legato on change, 150ms gate close + re-attack,
velocity from RMS) — both unit-tested headlessly. A 🕊 chip shows the
sung note and brightens on voice; a ctxItem toggles it; mic denial
tickers "the instrument cannot hear you".
Verified: detector 220/440/554Hz within 0.09% (silence & noise gated),
fold lands in-scale, the state machine single-fires / legatos /
re-attacks / scales velocity, the ctxItem + mic-denial path work live,
zero console errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-lane pattern length (wlen, 1..16, default 16 = full wheel) → polymeter.
Named wlen because keyroll already owns seq.len (the per-step note-length
array). One monotonic seqAbsStep counter drives wheelStep(sq), which
returns seqCurStep for full wheels (bit-identical to pre-WHEELS) and
seqAbsStep%wlen for shorter ones. Threaded through seqTick (both branches,
one cur for the gate AND len[] lookup), the S&H source sampling, and every
applyGroove call. migrateSeq clamps wlen (legacy → 16); self-check gains
wlen-default/preserve/clamp + wheel-full-identity. exportMid's drum block
is now one absolute loop (a 12-wheel exports as it plays; sa%16 keeps the
full wheel byte-identical). UI: a ☸ wheel chip in each beat lane + the
arranger euclid row; steps past the wheel dim (.off, pointer-events:none)
and the playhead paints at wheelStep. The owned() export gate is intact.
Verified: groove self-check green (incl. new assertions), full-wheel
export bit-identical over 4 bars, 12-wheel wraps at sa%12, kick-16/hat-12
drift over 48 ticks, beat-panel wheel=12 dims exactly 4 cells, zero errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A MediaRecorder tapped off N.lim (the true final mix — post-squash,
post-limiter). Lazy dest+recorder built on first roll, 1s timeslices.
The #testament ⏺ button beside ♪ turns red and counts m:ss while
rolling; on stop it downloads godstrument-testament-<stamp>.<ext> and
tickers "it is written". A ctxItem mirrors it near GODSONIQ.
testamentStart/Stop/State exposed on the Synth return.
Verified: button/timer/ticker/download all fire, blob is valid opus
decoding to the right duration, and an oscillator probe proved the
record→decode pipeline captures real audio (RMS 0.35). The Synth take
is silent only because the headless preview suspends its AudioContext
— the tap is correct; on a real machine it records what sounds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merge pantheon/integrate (GODSPEED ⚡ · GODSQUASH 🫀 · GODSPEAK 🗣 ·
UNGODLY ⏪ + the ε closer, reviewed & committed from the m3ultra rig)
into main (keyroll · sats · planes cache · god's eye · 18 review
fixes). Two conflict hunks, both in the beat panel where behavior
fixes met undo instrumentation — resolved keeping both: the bpm field
stays always-editable AND marks undo; master ▶/■ keeps per-lane mute
memory AND marks undo. Manual regenerated from the merged grimoire.
Verified: zero console errors, groove self-check, beat/G panels,
godspeak chip + keyroll CSS all present, python gates green, the
godspeak worker skips gracefully where MLX is absent (and the
supervisor now retires clean exits instead of churning them).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The GODSPEAK grammar: tempo/bpm with spoken numbers (one twenty → 120),
squash N percent, AMPLER capture/chop, open/close every panel, play/stop
the beat, mood, skin, zen — and take it back, the voice of god un-doing.
First match wins; unmatched words arrive in the ticker, never an error.
A 🗣 chip pulses while the mic is held. Polish: arp.up pruned, the arp
breathes with groove.velo, tempo edits mark undo, serializePatch carries
godtime.locked so undoing a mood restores the lock. Grimoire chapter for
all four gods + regenerated manual + worker permission notes.
Reviewed by fable: two fixes folded in — matchSkin normalizes candidate
names (so god-s-eye matches post-convergence) and a virgin session
saying play-the-beat gets the seed pattern first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A fresh-eyes adversarial pass over the 15 pulled commits (godrum +
keyroll + sats + planes cache + god's eye): 21 raised, 18 confirmed,
all fixed.
The instrument: .mid export no longer smuggles the seeded demo beat
(lanes export only if playing or edited off the seed — same test
zeroHasBuild uses); master ▶/■ remembers per-lane mutes; the beat
panel's bpm field is always editable like the dial (🔒 blocks the
world's hands, not the user's); ⏺ rec with the beat stopped anchors
step 0 at arm instead of the invisible free-running clock; note-length
drags speak pointer events (touch works, window-level so mid-drag
re-renders survive); the resize handle no longer eats 40% of a
sixteenth's click width; the god's eye clears a stale ISS trail after
an rAF nap or resize; the "Agent A" dev-process string is gone; the
grimoire now tells the truth about kit knobs and which voices hold
note length (manual regenerated).
The workers: world_sats — partial Celestrak fetches backfill from the
old cache instead of clobbering it, pass-detection state survives the
6-hourly refresh, the no-TLE exit naps 15 min so the supervisor can't
hammer Celestrak, and the venue default now matches world_sky's
Brisbane instead of Null Island; world_planes bounds its stale-cache
fallback to 1h so a days-old sky can't resurrect as live; world_sky's
docstring admits the lat/lon it emits. run.py retires workers that
exit 0 instead of reviving them every 10s (the sgp4-missing churn).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A new map-layout skin — the world's own sensor picture read AS the cosmology,
ported from the GODSIGH view. A minimal equirectangular Earth (a sparse hand-drawn
continent set kept inline — no heavy geo data in the one-file console), a 30°
graticule refit to a 2:1 rect each frame (dynamicStations), and nine geographic
"posts" the sources are seated at by godseyeOf. The overlay seats every feed at its
LITERAL coordinates: earthquakes flash at quake.lat/lon (venue-antipode fallback if
absent), the ISS and recon birds sweep as a moving point + trail from sats.iss_lat/
lon, aircraft are a shimmer band, the markets pulse at NY/London/Tokyo, the solar
wind washes the poles as an aurora, the word sparks at civilisational coordinates,
and your own hands are the ring at the venue. Cold watch-floor palette (near-black,
cyan, warning amber) and a Bb minor-pentatonic retune. Registered in SKINS +
SKIN_HOME beside the bagua; grimoire + manual entry ("the eye that hears").
To give the console the venue's location (it had none), world_sky now emits
/gs/sky/lat,lon; config.json declares sky.lat/lon and quake.lat/lon so the eye can
place "here" and the quakes. (sats.* signals + routes already landed with world_sats.)
Verified: full viz parses/runs with the groove self-check passing and zero console
errors; an isolated harness renders the REAL overlay code into a coherent world-map
with every layer; and an end-to-end run (feeds -> hub -> WS) delivers all the eye's
input signals. Adversarial review found one wrap bug — the ISS trail streaking
across the map at the ±180° antimeridian — now fixed by breaking the polyline at the
seam (verified: a seam-crossing sine trail renders split, no full-width streak).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Undo/redo as time-travel over serializePatch(): loadPatch already restores
everything and rebuilds open panels, so undo is a 32-deep ring of snapshots.
- ungodlyMark(label) snapshots BEFORE a mutation (dedup identical, cap 32,
clears the redo future); ungodlyGesture(label) coalesces a drag burst
(same label within 800ms) into one entry; undo/redo loadPatch under `muted`.
- ~47 mark sites at every patch mutation: mood, zero enter/exit, dimension +
vibe load, route patch/unpatch, the beat panel (steps/groove/euclid/expr/
kit/mute/play), the arranger (grooves/clips/scale/euclid/follow/expr/p-lock),
and the mix (mute/solo/reset/level/trim/group). Boot restore is suppressed.
- ⌘Z undo · ⌘⇧Z redo, added before the letter shortcuts; every single-letter
key (p/c/o/w/z/t/b/g) now guards !metaKey && !ctrlKey — so ⌘Z no longer
toggles zen (the pre-existing quirk), while plain z still does.
- One ctxItem with a live depth count. Memory-only in v1 (reload clears it).
App scope only; nothing inside the Synth IIFE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sky's own schedule joins the orchestra. Unlike world_iss (which begs a web
API per position), world_sats fetches TLEs once from Celestrak — the stations
plus the named recon birds GAOFEN and COSMOS — caches them to
~/.cache/godverse/tles.txt (24 h freshness, atomic writes, stale-on-failure),
and propagates every craft locally with SGP4 at any rate, no per-fix HTTP.
For the venue city (--lat/--lon, retargeted by run.py) it computes each craft's
elevation above the horizon and emits:
/gs/sats/best_elev highest elevation among all tracked (-30..90 deg)
/gs/sats/overhead_count how many above 10 deg
/gs/sats/iss_lat/iss_lon ISS sweep (available even without world_iss)
/gs/sats/recon_elev best elevation among only the GAOFEN/COSMOS watchers
/gs/sats/pass.event impulse when any craft crosses rising through 30 deg
Wiring: config.json declares the six signals with norm ranges + labels, three
starter routes (best_elev -> pad.brightness, pass -> delay.feedback,
recon_elev -> lfo.rate) and adds sats.best_elev to the cosmos group; run.py adds
the worker to the WORLD profile and the lat/lon retarget set; requirements notes
the pure-python sgp4 dep. The worker is import-safe (sits out if sgp4 is absent)
and exits politely with no cache and no network. Grimoire + manual entry added.
Math ported from GODSIGH's verified satellites layer: TLE -> Satrec.twoline2rv
(handles no_kozai), SGP4 -> TEME km, GMST rotation -> WGS-84 geodetic, then an
ECEF up-vector for elevation. Verified end-to-end against live Celestrak data:
98 sats tracked, ISS position/altitude plausible, and all signals arrive over
the hub WebSocket correctly normalized with their routes active.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Feature-gap analysis of local/groove-editor (c0c3a98) vs current main showed
godrum independently built almost everything the branch had — per-step
velocity/chance/ratchet, euclid, swing/humanize, scale/root, Play-One stacks,
Follow Actions. The one genuine, valuable gap was NOTE LENGTH (the "key-roll"):
main's notes were all one-16th. Ported that natively; skipped the rest as either
already-covered, a deliberate divergence (global scale, Play-One vs polyphony),
or too invasive to land without regressing godrum (clip-length/polymeter would
rewrite the fixed-16 architecture that migrateSeq/arranger/beat/commune assume).
What landed (all back-compat; len defaults to 1 = one 16th, so old patches are
unchanged except un-ratcheted sequenced notes now gate to their drawn length —
which is the feature):
- len[16] on seqFor/drumSeqFor + migrateSeq back-fill + two self-check guards
- attachNoteLengths(): .seqnote overlay blocks with an ew-resize drag handle,
used by the voice-inspector roll and the arranger grooves-tab roll; len is
per-voice (shared across A/B/C clips) exactly like vel/chance/rat
- playback: seqTick gates rat==1 notes to len*sixteenth; Synth.pluck gains an
optional durMs (null = byte-for-byte the old natural decay); ratchets unchanged
- MIDI: export emits length-scaled note-offs with a same-pitch overlap clamp;
live midiPluck now tracks pending offs per (channel,pitch) so a held note
retriggered on its own pitch ends cleanly instead of being cut by a stale off
- grimoire + regenerated manual
Verified: groove self-check passes (incl. len guards); an isolated harness
confirms overlay render/drag/clamp at both grid sizes + Play-One stacks + the
getter that keeps overlays in sync when the arranger swaps sq.steps per bar.
The reference branch local/groove-editor is the only copy of this work and is
deleted now that note-length is landed and pushed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stage 1β. A DynamicsCompressor + makeup between the master fade and the
safety limiter, driven by one normalized `squash` dest.
- build(): out → squash → makeup → lim (knee 12, attack 0.004; baseline is
the sq=0 transparent state so it's clean before params() first runs). The
GODSONIQ N.out tap stays pre-squash. squash/squashMakeup returned on N.
- params(): sq=c01(d("squash",0)); threshold −6→−36, ratio 1:1→8:1, release
0.12→0.25; makeup = 1 + sq·sq — a QUADRATIC curve tuned by offline
measurement (the compressor's RMS drop is ~5.95·sq² dB, so 1+sq² holds
loudness level ±0.5 dB while crest factor falls monotonically).
- Routable exactly like crush: added to OMNI.MODULES fx-rack keys,
OMNI.LABELS, and ZERO_RACK — nothing else outside the IIFE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hold a chord in mind mode, the machine runs with it — on godtime, with
the groove's swing (it fires from seqTick's tail, so it inherits the
swung onset for free; measured: off-beats land 0.31 late under mpc 8-4).
Notes latch at mindStrike when the arp is on (a strike toggles the note
in/out; MIDI-in routes through there too, so hardware latches for free);
mindStrike is byte-identical when the arp is off. rate 1/4→1/32 (1/32
double-fires mid-16th), up/down/updown/random, oct ×1–3, gate → note
length. Fires through the same pluck() + midiPluck(ch 2) every mind note
uses. A compact #godspeed panel on G and one sky-menu entry.
App-scope only — never touches the Synth engine (β's turf), OMNI, or
squash. seqTick/mindStrike/keydown edits are additive one-liners.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stage 4 docs. Four entries added to the grimoire's living-patch list, alongside
the groove-grids/tracks/Groove entries, in the house voice:
- 🥁 the beat — five drum lanes on the godtime clock; draw/▶; velocity/chance/
ratchet; ⬢ euclid; the feel shared with tracks; the 🎛 kit drawer incl. duck
("the kick parts the sea"); saves with the patch; moods never silence it; a
beat drawn in zero counts as a build.
- keys 1–5 & ⏺ rec — finger-drum the kit (⇧ ghost); rec quantizes taps to the
nearest sixteenth.
- 🌾 AMPLER — "there is ample, for god is abundant": capture the sounding world
(a fed tab counts), chop 8/16, audition, ⇄ reverse, seat slices onto voices;
slices ride level/pan/duck/choke; session-only.
- the beat leaves the building — GM drums on MIDI channel 10 (36/38/39/42/46)
and a ch-10 track in export .mid.
viz/manual.html regenerated via build_manual.py (27 sections, self-check ok).
GODSTRUMENT_MANUAL_SOURCE.md left untouched (viz/index.html is canon).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stage 3 / Agent E — the performance layer.
- fireDrum(name,vel,durMs): one shared fire path for the sequenced beat and
the played keys; seqTick's drum branch routed through it (behavior identical).
- Keys 1–5 play kick/snare/clap/hat/ohat while the beat panel is open (vel 0.9,
⇧ = 0.5 ghost); ev.code with an ev.key fallback for code-less environments.
- ⏺ rec toggle (beat.rec, red when armed): each tap quantizes to the nearest
16th (round seqPhase % 16) and writes steps[n]/vel[n], repaint via beatRefresh.
- exportMid: a channel-9 GM drum track (36/38/39/42/46), the beat looped across
the arrangement's bars, one 16th long; ratchets omitted in v1. Added before the
bail check so a beat-only export still produces a file.
Territory: openBeat header, keydown, seqTick, exportMid + one #beat CSS rule.
Nothing inside the Synth IIFE; no ctx-menu entries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The SP-404 move, Godstrument-style. GODSONIQ already captures ~3s of the
live master; AMPLER routes its own capture into a separate amplerBuf
(recTarget-tagged per-capture, so GODSONIQ's keys-replay stays byte-
identical), chops it into 8 or 16 equal slices, reverses any slice (lazy
cached copy), and seats a slice onto any drum voice. An assigned voice
PLAYS its slice through that voice's level→pan→drumBus chain, so the kit
knobs and patch persistence keep acting; a slice-kick still ducks and a
slice-hat/ohat still obeys the TR choke (both factored to be source-
agnostic). Eight frozen ampler* methods + an amplerPeaks waveform helper.
UI: a right-side #ampler panel (capture · waveform · chop · slice grid
with per-slice reverse · per-voice assignment strip), opened by one sky-
menu ctxItem after GODSONIQ. Engine lives in the Synth IIFE; the panel
sits after openBeat's brace — E's territory (openBeat body, seqTick,
keydown, exportMid) untouched. AMPLER state is session-only in v1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four defects from the independent 4-lens adversarial review of Stage 2.
R8 choke same-task noise pop — DEVIATION from the brief's fix, verified by
measurement. Read-before-cancel does NOT work: reading gain.value right after
setValueAtTime(0,t) in the same task returns the 1.0 default (the envelope
hasn't rendered), both offline AND live — so the anchor is 1.0 and it still
pops (measured 0.45 vs a 0.15 ohat). Correct primitive is cancelAndHoldAtTime,
which holds the timeline value at t (0 same-task, decaying level mid-ring);
read-value kept as the legacy fallback. Same idiom applied to fireKick's duck.
Verified live: same-task double-ohat 0.152 ~= single 0.149 (no pop); normal
different-task choke still cuts the tail 70%.
R9 euclid junk input — trim, split /[\s,]+/, drop empty tokens, Number(), bail
unless the first token is finite. " 4"/",4" -> E(4,16); "abc"/"" -> untouched;
"0" -> cleared; "5,2" -> E(5,16) rot 2. (Empty-token filter also fixes the ""
vs "0" distinction a bare Number() can't.)
R10 prototype pollution via kit data (MAJOR — patches/vibes are cross-user) —
own-property guard (hasOwnProperty) everywhere a caller-supplied name indexes
KIT / N.drums / DRUM_DEFS: drum, drumParam, drumParamList, drumKit, drumKitLoad,
drumSync. Verified: drumKitLoad({"__proto__":{toString:0.5}}) and
drumParam("__proto__",…) leave Object.prototype intact; legit keys still apply;
normal round-trip works.
R11 a beat is a build — hoist the canonical seed into DRUM_SEED (one source of
truth for seedBeat + zeroHasBuild); zeroHasBuild() now returns true when any
drum lane is on or its gates differ from the seed. Untouched seed still exits
quietly; ▶ or a drawn step now shows the keep/weave card so the beat survives.
Temporary verify hooks removed (grep-clean). No merge to main, no deploy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stage 2 (Agent C). Real merge of godrum/a-engine + godrum/b-beat-panel onto
godrum/integrate (zero conflicts; extracted app script passes node --check),
then the seven review findings:
R1 kick tail — stop the osc at t+0.01+8τ (τ computed once); no click at max decay.
R2 seed — clear closed hats on steps 14/15 so the seeded open hat rings into the downbeat.
R3 stale paint — paintGrid now drives paintExpr (lit-bars track the grid); groove-slider drags relight the preset buttons.
R4 groove — add the fourth "velocity" slider (groove.velo), matching the arranger's grooves tab.
R5 moods — applyMood's storm + clearGrooves loops skip drum.* lanes; a mood never silences the beat.
R6 kick-duck — duck param on the kick (default .35); pad+drone reroute through a new duckG; fireKick dips it and lets it flood back ("the kick parts the sea").
R7 mix — closed/open hat internal peak scalars 0.70/0.80 → 0.35/0.45 so the hats sit at/just under the ambient perc.density shimmer (§0.6 contract defaults unchanged).
Verified end-to-end on a static serve via offline-render measurements plus a live
analyser tap on the real graph: first-press wake with ♪ off, every voice audible,
choke (95% tail cut), duck −3.07 dB → recover, chance/ratchet, R5 through a storm,
kit+beat persistence incl. duck, zero-mode round-trip, arranger↔beat shared groove,
mutual-exclusion, zero console errors. Temporary verify hooks removed (grep-clean).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kick / snare / clap / closed-hat / open-hat as one-shot node graphs
summed onto a drumBus → preSat, so drums ride the world's saturation →
crush → chorus/reverb/echo like every other voice. Each voice keeps a
persistent level→pan chain; hits connect into it. TR-style choke: a
closed hat OR a new open hat cuts the ringing open hat.
Frozen API (§0.6): drum(name,vel), drumParam get/set, drumParamList,
drumKit / drumKitLoad. All params normalized 0..1 at the boundary and
mapped to real units inside each voice; non-finite inputs rejected.
Entirely inside the Synth IIFE — no other territory touched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The app no longer announces GODSTRUMENT to someone already playing it —
the landing page and auth modal keep the brand; the console keeps only
the counts and the header line. The grimoire trigger slides up into the
vacated corner, and drawSpacedText goes with the wordmark it existed for.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three finds Opus's pre-ship review missed, all verified in-browser:
the heart-connect button was unclickable (pointer-events inherited
none from #readout), orb taps rang the 2.5s Moment bell per tap (now
a short pluck via the newly exposed Synth.pluck), and Game Day's
asleep copy leaked --sports jargon to civilians. Plus the Grimoire
now names the gentle door, and the brief gains §14: review verdict
and the v1.1 punch list (hub Cache-Control for .html, manual section,
knob double-tap, per-vibe info sheets, PWA manifest, wake lock).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A second, self-contained client for the same hub, mobile-first: only
the sources about living (bodies, births, weather, sun & moon, play),
eight one-tap Vibes instead of a matrix, two macro knobs (ENERGY /
DREAM), a breathing orb, and dopamine Moments (somewhere a baby, the
sun where you stand, a kickoff). Its own compact Web Audio synth, all
routing client-side so it plays against the read-only public hub.
Reuses the invite-only /api auth; local sensors (heart BT, device tilt,
shake, mic, tap) never touch the hub. No money, no machinery, no
cables. Links back to the full instrument; added a "🌱 lifestrument"
item to godstrument's sky menu. Built understand→build→review with an
adversarial pass; 21 findings fixed (a11y sliders, zoom, contrast,
warm-dawn background, reachable heart connect, pluck de-click,
scheduler catch-up cap, moment tuning). test_lifestrument.py guards the
whitelist and the money/machinery ban.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A 13-agent adversarial whole-app sweep (client controls / runtime / server-auth /
new features / feed-config-honesty, each finding reproduced-or-refuted) found ZERO
broken functions — verdict healthy. Fixed the reachable low-severity edges it did
confirm:
- viz/index.html:1804 — arranger 'grooves' tab threw TypeError (nSel.isNote) when
dests is empty in the cold-connect window (post-sign-in, pre-WS-hello). Guard:
dests.get(arrSelKey) || {isNote:false} (the 'clips' tab already tolerates empty).
- viz/index.html — picking a skin from the sky menu / F4 next-skin / F4 prev while
in ZERO mode force-enabled an invisible, un-toggleable chakraMode (every clear
path is zeroMode-guarded). Added the same !zeroMode guard to all three sites
(byte-identical outside zero — no regression).
- hub.py:117 — GODSTRUMENT_READONLY parse treated any non-empty non-'0' value as
true (so 'false'/'no'/'off' enabled read-only). Now an explicit truthy set
{1,true,yes,on}; prod uses '1' → stays read-only (verified before deploy).
- viz/index.html:2996 — removed a dead else branch in zero 'save as vibe' (its
'vibes land in the next update' placeholder was unreachable; saveVibe is a real
sibling method) → call this.saveVibe(gk) directly.
- transform.py:40 — collapsed a no-op ternary (both branches float(v)).
- viz/manual.html — regenerated (was stale vs build_manual.py; picked up the
admin-panel CSS added in 764e28b; grimoire prose was already in sync).
Left as documented cosmetic: sanitizeVibe caps emoji at 2 code points (can split a
ZWJ/flag glyph on a shared vibe — display only, no XSS). Verified: node --check,
all .py compile, auth --selftest green, app boots into zero clean, console clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The admin panel could mint invite codes and read stats but not manage the people.
Adds, all under the existing admin-gated /api/admin/ block (default-closed:
no session → 401, non-admin → 403):
- GET /api/admin/users — every account: id, email, admin flag, patch count, joined
- POST /api/admin/user/<id> — rename / change email / grant-or-revoke admin
(validated like signup: format + uniqueness)
- POST /api/admin/user/<id>/reset — set a fresh random temp password, returned ONCE
(never stored plaintext or logged) to relay to the user
- DELETE /api/admin/user/<id> — delete the account + its saved patches (feedback +
used invite kept as history)
Self-lockout guards: you can't delete your own account or revoke your own admin.
Admin granted via the GODSTRUMENT_ADMIN env is marked env_admin and its toggle is
hidden (can't be removed in the db). auth.py migration-free — uses existing tables;
the is_admin column was already migrated.
Client: the ⚙-account admin panel now lists every user with inline edit (username +
email), 🔑 reset-pw (shows a copyable temp), ★ make/revoke admin, and 🗑 delete
(confirm); the panel widens for the table. Names/emails escaped on render.
Verified: auth.py --selftest (list/edit/reset/delete + endpoint guards); live curl
against the local dev db (rename, bad-email/dup rejected, admin toggle, self-guards
400, non-admin 403, delete cascades presets); and the UI end-to-end (rows render
with correct per-row actions, edit expands, reset shows a temp password). Console
clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The fix-verify pass flagged one CONFIRMED regression in the #1 floor loop: it
pinned any tweaked cableless non-note dest to a hard 0, but two dests have a
non-zero synth idle — filter.cutoff (d(...,0.3)) and tempo.nudge (d(...,0.5)).
So touching the filter knob in zero (or, before this, merely opening OMNI, which
created a neutral tweak) pinned filter.cutoff to 0 → 250 Hz, darkening the lead.
- new DEST_IDLE map; the floor loop seeds destVals[k]=DEST_IDLE[k]||0, so a
neutral/reset knob returns a voice to its idle (filter 0.3), an offset rides
from the idle, and mute still → 0. (verified: reset filter → 0.3 not 0;
drag up → 0.767; mute → 0)
- OMNI update() + knob pointerdown now read tweaks[k] directly instead of tw(k),
so rendering/opening the panel no longer creates phantom neutral tweaks (which
also kept the tweaks map — and saved dimensions — clean). (verified: open OMNI
→ filter has no tweak)
A clean instrument (no tweaks) stays byte-identical; the public house patch was
already masked (filter.cutoff is cabled to sun.speed). Deploy gate: GO.
Console clean, syntax clean, 21 knobs, field healthy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 30-agent adversarial review (5 dimensions × find→refute×2) confirmed 8 defects;
all fixed and live-verified:
BLOCKERS
- #1 unwired OMNI trim couldn't be silenced: the offset-floor only seeded a dest
while offset>0.0015, so reset/mute/lower stopped emitting the key and the last
floored value stuck in the synth (Synth.update merges). Now ANY tweaked cableless
non-note dest is seeded 0 so the processing loop writes its real value, incl. 0.
(verified: crush norm 0.5 on drag → 0 on reset)
- #2 leaving zero with an OMNI/offset-only build (no cables) was silently discarded
— exitZeroToMood gated on routesArr.length===0. New zeroHasBuild() also counts
picked orbs + any non-neutral tweak, so the keep/old/weave card always shows.
(verified: offset-only build → 'you shaped this instrument by hand. keep it?')
- #3 vibe scale whitelist used truthiness (SCALES[q.scale]) → 'constructor'/'__proto__'
passed → NaN into a Web Audio AudioParam → threw ~40×/s (remotely triggerable via
public #vibe= links). Now Array.isArray(SCALES[...]) + quantize only on note dests;
mxQuantize fallback hardened too. (verified: fx quantize dropped, note scale→minor_pent)
SHOULD-FIX
- #4 finishZeroExit keep/weave clobbered the kept mix (applyMood resets MOOD_KEYS):
re-lay the build's tweaks after applyMood; weave merges build tweaks over prezero.
- #5 crush knob was fully dead (never in dests): OMNI.render ensureDest()s every
voice/FX key → arc lives, floor works, right-click opens the menu. (verified)
- #6 OMNI drags in zero weren't persisted: knob pointerup/dblclick call zeroUI.persist()
when zeroMode → survives reload. (verified: offset 0.467 in gs_zerobuild)
- #7 reloaded vibes showed OFF: persist activeVibes + per-route _vibes/_base to
gs_zerovibes, restoreVibeState() on enterZero(true). (verified: vibe restores ◉)
- #8 GODSONIQ chip lit ~3s late: update() re-lights engine/♪ chips each frame.
Console clean, syntax clean, 21 knobs, field healthy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The vibe name + emoji arrive from user-pasted GSV1 codes and #vibe= URL fragments
and are rendered via innerHTML in the vibes panel + the shared-vibe offer card.
name was <-escaped but emoji was raw. sanitizeVibe now strips [<>&"'`] from both
at the trust boundary, so nothing dangerous reaches any render path.
Verified live: a crafted code with name='<img onerror=…>' + emoji='<svg onload=…>'
imported via #vibe= → no <img>/<svg> in the DOM, no script fired, both rendered as
inert text. (ship-check item 3: user strings → HTML must be escaped.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Canon (viz/index.html grimoire) is truth; manual synced to match.
- grimoire: two new tech-manual sections in the OMNI/Earth-Echo/GODSONIQ style
(numbered how-to + spec table) — 'Building from zero — the empty instrument'
(rings → orbs → drag/tap routing → synth-roll → keep/weave exit → persistence)
and 'OMNI — the synth panel' (live arcs, trim-riding knobs, engine selector,
earth echo, transport). Rewrote 'Saving your instrument' as dimensions & vibes.
- manual (had drifted — still said MY PATCHES): added Building-from-zero + the
🎹 OMNI panel; 'Saving' → dimensions & vibes; keymap now lists F11 zero + O.
Verified: grimoire renders all three new h2 sections, no broken markup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 🎹 OMNI button (sibling of 🎛 tracks, key O) opens a floating panel — the
planet's synth, laid out like a synth. NO new audio paths: a view over the same
dests + tweaks the field already drives.
- knobs: each knob's ARC is the dest's live value (n.normDisp, the same number
the field meters — it breathes with the world); dragging rides that dest's
tweak OFFSET (setParamLocal) and a dot marks your hand; double-click resets to
neutral; right-click opens the node's full context menu. Note voices (lead/bass)
show a live pitch arc — display-only (their character is the engine selector).
- modules: VOICES (lead/bass/pad/drone/perc), FX RACK (filter/reverb/delay/
saturation/glitch/granular/crush/morph/lfo/space), 🌀 EARTH ECHO (a toggle;
its six echo.* knobs appear only when it's on).
- header: voice-engine selector (FM tine / 🎸 string / 🎛 GODSONIQ — the pluck's
character, via Synth.stringVoice/godsoniq/sampling), ♪ start-stop, live BPM,
🔒 godtime lock.
Matrix honesty fix (computeDests): a hand-set positive offset now gives a voice a
floor even with no cable — so an OMNI (or the existing 'trim') knob can make a
voice audible from nothing. Only affects dests a user explicitly offset; a clean
instrument (all offsets 0) is bit-identical to before.
Verified live (z0test, via a manual matrix pump since the headless preview's rAF
is paused): panel opens with 21 knobs + 3 modules; idle arcs track live data
(filter 0.41); dragging reverb up floored its value 0.09→0.59 with the tweak dot
showing; double-click reset it; engine selector swapped FM→string; earth-echo
toggle revealed its knobs; right-click opened the 'filter cutoff' node menu.
Tweaks persist in the saved dimension (serializePatch carries tweaks). Console
clean, syntax clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dimension is everything (the whole scene: routes, tweaks, grooves, tempo);
a vibe is a feeling you can hand a friend. Same /api/presets storage — this is
a framing rename, existing saves appear unchanged.
- ⚙ panel: 'MY PATCHES' → 'DIMENSIONS', copy reworked; input placeholder
'name this dimension…'.
- the load dropdown always leads with '✦ dimension zero — the empty field';
loading it calls enterZero() (the place you build from nothing).
- grimoire 'Saving your instrument' rewritten (canon = truth): the dimension /
vibe distinction, the GSV1 share code + #vibe= link, 'build a vibe, save a
vibe, share a vibe'.
Verified live (z0test): panel shows DIMENSIONS + ✨ VIBES, MY PATCHES gone,
dropdown leads with dimension zero. Console clean, syntax clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A vibe = a small bundle of cables {v:1,name,emoji,routes[≤32]}. Distinct from a
dimension (the whole scene): a vibe is a feeling you can hand a friend.
- apply is additive + reversible: routes a vibe creates are tagged (route._vibes)
so toggling it off removes exactly those; a route two vibes share survives until
both are off; a hand-built cable (route._base) a vibe merely rides is NEVER
deleted. Tags live in-memory only — they don't leak into saved patches.
- save from a zero orb (right-click → 💾), from any node's right-click in the full
field (💾 save these as a vibe), or 'save current wiring' in the panel (≤32 cap).
- ✨ vibes panel (zero pill + ⚙ gear): list, toggle on/off, ⧉ copy share code,
× delete, paste-to-import. localStorage library (gs_vibes).
- share: GSV1.<base64url(json)> clipboard code + godstrument.pro/#vibe=… fragment
that OFFERS on load (add it / just save) and never auto-applies.
- trust boundary: sanitizeVibe hard-validates every import — unknown source/dest
skipped (not thrown), amounts/roots/octaves clamped, curves/scales whitelisted,
32-route cap, try/catch the base64/JSON. Garbage codes are rejected cleanly.
- per-account cloud storage deferred: the code + local library already deliver
build/save/share across devices and friends. (add a vibes table if users ask.)
Verified live (z0test): built a 2-cable orb → saved 'sunrise duo' (quantize
preserved) → panel listed it ◉ → share code round-tripped through import →
garbage code rejected → hard-reload kept the library → toggle ON created exactly
2 routes (lead.note lit), toggle OFF removed them → #vibe= fragment offered
'shared gift' in the full app, hash cleared, not auto-applied. Console clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
C1: leaving zero with a build no longer silently discards it — a compact
three-way card (keep my build / bring the old world back / weave them
together); esc stays in zero. 0-route exits stay quiet (restore prezero).
C2: the zero build survives a reload — picks + cables persist to
gs_zeronodes/gs_zerobuild; enterZero(true) on the reload path restores them.
C3: tap-to-patch (arm an orb, tap a voice) as a touch fallback for drag;
right-click an orb → member list (× to drop a feed + its cables),
unpatch-this-orb, and a save-as-vibe stub (Z3); a ⛈ enter-the-storm pill.
Nit: the chakra-view key/menu is a no-op in zero (nothing to draw there).
Verified live (z0test): pick→orb→tap-patch lit lead.note; hard reload restored
the ☀ orb + lit socket; the exit card's keep-my-build landed in a healthy full
field on the storm mood with zero-storage cleared; orb menu drops members.
Console clean, syntax clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The functional heart — build the instrument by hand from zero.
- Orbs are draggable; the output-rack sockets are drop targets. Drop an orb on a
voice/FX socket → lays a cable per member feed (registerRoute), auto-adding a
default quantize (minor_pent / root 60 or 36 for bass) when the target is a
note voice. The socket lights green when it carries routes; a ticker line
confirms the patch and points to ♪.
- Right-click a socket → the "synth roll" assign menu: for note voices a scale
(the 11 SCALES) / root / octaves picker; for CC voices an amount slider + curve
picker (lin/exp/exp3/log/scurve/inv). Plus "× unpatch all". Edits the live routes.
- refreshSockets() reflects existing routes on open; hover-highlight on drag.
Z-AC verified live: pick sun.speed (cosmos ring) → drop on filter · pick
crypto.price (wealth ring) → drop on lead → both sockets light · right-click lead
→ scale picker (11 scales, root, octaves) · press ♪ → OMNI plays (button green).
Bitcoin sings through a sun-opened filter, built from nothing. Console clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each built group becomes a spinning orb on the stage, not a flat chip — a glowing
core in the group's hue with its picked feeds as petals orbiting around it (CSS
zspin/zpulse). They accumulate as you build: pick from fire → one orb; add cosmos
→ two; and on. Orbs breathe live — the core glows by the group's average value,
each petal brightens with its own feed. More feeds widen the orbit ring.
Verified live: fire (3 petals) + cosmos (2 petals) render as two spinning glowing
orbs; console clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The overlay that replaces the sensory flood. In zero mode the node field is not
drawn (frame() skips drawCables/drawSourceNode/drawDestNode); a DOM overlay
takes its place over the starfield:
- 7 vibe-rings down each side — the 14 config groups (left = elements/feelings:
light·dark·fire·water·earth·air·spirit; right = domains: planet·cosmos·heavens·
human·market·wealth·summer). Each: a hued dot (GROUP_META — taste consts,
retune freely), label, live feed count, and a breathing glow scaled by the
group's live average value. Members come from groupsInfo (the hub's config).
- Picker drawer: click a ring → it slides in from that side listing the group's
feeds (name, key, live value bar, ⊕ that toggles in place). Pick 1+ → a stage
node (chip, colored by group) lands on the center stage.
- Output rack along the bottom: OMNI voices (lead·bass·pad·drone·perc) + space &
grit (filter·reverb·delay·…) as labeled sockets. Visual only — routing is Z2.
Veil (z-8) sits below the ♪/tracks/grimoire controls (z10-13) so they stay
usable. Verified live: rings render with hues + live glow, heavens drawer opens
with its 5 feeds + value bars, picking lands stage chips, ⊕ toggles in place,
console clean. Routing to the rack + right-click assign = Z2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The calm front door. Zero is not a muted world (moods do that) — it's an EMPTY
one you build by hand, the same matrix started from nothing.
- enterZero(): snapshots the current patch → gs_prezero, then loadPatch({}) empties
the field; sets clientMatrix so the hub can't re-populate the cables; gs_mood="0 zero".
- exitZeroToMood(name): restores gs_prezero (the full factory) then applies the mood —
leaving zero brings the whole world back.
- First-visit choice card ("one world, three doors"): 🌱 start from zero · 🌅 first
light · ⛈ the full storm. Replaces the silent default-into-first-light; backdrop/esc =
first light. Returning users keep their last mood; a returning zero-user re-enters zero.
- Mood submenu gains "0 zero — build from nothing"; F11 toggles zero (KEY_ACTIONS).
Verified live (logged-in preview): card renders with 3 doors; "start from zero" empties
the field (56 factory cables cleared, snapshotted); F11 restores all 56 + first light
(92 BPM locked); returning-zero reload re-enters zero with no card; console clean.
The 7+7 calm slot UI is Z1. Not deployed yet (deploy is Z6, post-review).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Names the built-in browser synth OMNI (♪ tooltip, intro prose) and adds three
scannable "tech manual" reference sections to the grimoire — OMNI (full voice
roster + how-to), the Earth Echo (six world-driven tape knobs + Schumann), and
GODSONIQ (resample + tab feed) — each a numbered how-to plus a two-column
control/dest spec table (new .spec / ol.steps CSS; auto-indexed in the TOC).
Every label, dest key and world→voice mapping extracted from code and verified.
Fixes two drifted claims in the old Earth Echo prose the audit caught: tape
wear drives hiss only (grit/saturation is static, not world-driven), and the
Schumann sub is ×4 = two octaves (not "four times").
Manual: sections flow into PART I (extractor taught .spec/.row → markdown
bullets) + an OMNI umbrella entry in Appendix J.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Right-click sky → "🌐 feed a tab": getDisplayMedia({video:true, audio:{…}})
lets the user share another tab's audio (a YouTube tab, a stream). Wired via
createMediaStreamSource into preSat → the full FX chain + master, so it's
audible live, crush-able, and resampleable by GODSONIQ from one wiring. Video
track stopped immediately; a track 'ended' listener handles Stop-sharing.
Audio constraints (verified against MDN/W3C/Chrome docs): suppressLocalAudio
Playback so the tab isn't heard twice, and echoCancellation/noiseSuppression/
autoGainControl off so the WebRTC voice-DSP doesn't mangle music. Chromium-only
(feature-detected; graceful ticker hints for unsupported / no-audio-track);
macOS grabs a tab not all-system; DRM services capture silent, YouTube fine.
Grimoire (truth) + manual Appendix J updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record ~3s of the live master mix into a buffer via a recorder AudioWorklet
tapping the full post-FX bus, then replay it pitched across the keyboard
(playbackRate = 2^((midi-60)/12), middle C = unity). Runs through the same
FX chain as every voice, so `crush` gives it the classic 12-bit sampler grit.
Right-click sky → 🎛 GODSONIQ to sample / re-sample; 🎸 string voice exits
back to the FM tine. Capture path verified: full-amplitude sawtooth recorded
clean (peak 0.98, rms 0.49) over the realtime port round-trip.
Grimoire (truth) + manual Appendix J updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The last two names that lied are now true:
- wavetable.morph — was a filter-Q sweep. Now a REAL wavetable: the lead is two
band-limited PeriodicWaves (a mellow one + a bright saw-rich one) crossfaded by
morph. Spectrally verified: smooth brightness sweep (centroid 462→880→1174
across morph 0→0.5→1), no aliasing.
- granular.density — was one occasional noise burst every 4th step. Now a REAL
grain cloud: a density-scaled scatter of short (12–40ms) pitch-varied grains
per step (0 at rest → a dense cloud when the world writes). noiseHit takes a
scheduled start time so grains scatter within the step.
The grimoire's existing claims ("sweeps the oscillator through its bank of
waveforms" / "a cloud of tiny grains") were aspirational — now they're accurate,
so no doc softening needed. Every clickable/routable thing now does what it says.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>