Godstrument/ZERO_OMNI_BRIEF.md
type-two b6f1c59e15 📖 brief: STATUS + CONTINUATION — Z0-Z2 approved, C1-C4 instructions (Fable review)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:42:05 +10:00

19 KiB
Raw Blame History

ZERO MODE · VIBES · DIMENSIONS · the OMNI panel

Build brief for the implementing model (Opus). Read fully before writing code.

John's ask, verbatim spirit: the full UI is an intense sensory assault. Give people a zero mode — 7 sources left, 7 right, click a slot to see the feeds in that grouping (same colors/chakra energy as the main UI), pick one or more, route them by hand to an output, right-click to assign it (synth role / MIDI / …). They build everything from zero. Save the whole thing as a dimension (a full hardcore scene save); save a specific source→route→output combo as a vibe — "build a vibe, save a vibe, share a vibe". And give OMNI a NICE UI panel.


0. Ground rules (unchanged from every Godstrument session)

  • Canon: viz/index.html is truth; the grimoire prose inside it must be updated to match whatever ships (never the other way). Regenerate GODSTRUMENT_MANUAL_SOURCE.md with the builder afterwards (build_grimoire.py pattern — carry-forward + dedupe).
  • Honesty rule: nothing decorative. Every slot, knob, and control maps to a real feed/dest/tweak. If a thing isn't wired, it doesn't render.
  • Verify with your own eyes: preview server + login (invite + /api/signup recipe — see memory godstrument-local-verify), console clean, then screenshot proof.
  • Deploy: rsync git-tracked files to botchat (humanjing@100.71.119.27, app dir /home/humanjing/godstrument), never --delete, never push godstrument_users.db / auth_secret / patches/; sudo -n systemctl restart godstrument; curl-verify cache-busted. First load post-deploy is slow (cold start).
  • auth.py changes must be additive (new table / new column with default) — the server's SQLite holds real user accounts. Never a destructive migration.
  • Commit per milestone with the Co-Authored-By line. Adversarially review at the end (a Workflow, as in previous sessions) and fix confirmed findings before deploy.

1. What already exists — build on it, don't reinvent

Thing Where (verify at build time) Use it for
14 concept groups with label, orbit, members config.json:31-46 ("groups"); grimoire "The vibes — grouping by feeling" (~line 848-900, has the emojis: 💰 wealth, 💧 water, …) The 7+7 zero slots. They are ALREADY called vibes in canon.
MOODS + applyMood viz/index.html:2275-2322 The pattern for entering/leaving a named state; zero joins this family
First-visit flow (gs_seen, gs_mood) viz/index.html:2841-2842 Zero as the new-user front door
serializePatch / loadPatch viz/index.html:2532-2567 Dimensions ARE this; a blank dimension = {routes:[], …}
addRouteLocal / registerRoute / removeRouteLocal viz/index.html:2452-2468, 2921 Zero-mode routing + vibe apply/remove
"patch to →" context submenu viz/index.html:5521 Reuse for assigning a node to an output
Node tweaks (mute/gain/offset), setParamLocal viz/index.html:2036+, 2451 OMNI panel knobs = views over these
MY PATCHES (account presets, one JSON blob) auth.py:46 (presets table), :249+ (endpoints); ⚙ panel UI Rename surface to Dimensions; vibes get a sibling store
CHAKRAS (colors) viz/index.html:3427 The zero-slot color language
Arranger panel (arrbtn, resizable, tabbed) viz/index.html:1497+, 2325-2330 The UI pattern for the OMNI panel
KEY_ACTIONS + remappable keys viz/index.html:~6060-6110 Key bindings for zero + OMNI panel
quantize option on routes viz/index.html:1398, 2415 The "assign like a synth roll" — note outputs get scale/root/octaves

Extraction discipline: before each milestone, re-verify the exact lines above (they drift) and extract any formula/shape you depend on into code comments citing viz/index.html:LINE.

2. ZERO MODE — dimension zero

Concept: a fourth entry in the mood family, but deeper: not muted — empty. No cables, no voices sounding, the field silent and almost blank. The user builds the entire instrument by hand, one cable at a time. Leaving zero (to any mood / the storm) restores the full factory experience.

Entry & exit

  • Right-click sky → 0 zero — build from nothing (top of the mood submenu), plus a key (suggest F11 or a free slot in DEFAULT_KEYS; check collisions).
  • First-visit choice card (replaces the current silent default into first light): three big options — 🌱 start from zero (build it yourself) · 🌅 first light (a gentle song) · ⛈ the full storm (everything at once). Persist choice like gs_mood. Returning users keep whatever they last used.
  • Entering zero: snapshot the current patch to a holding slot (localStorage gs_prezero) so exiting zero can offer "restore what you had" vs "keep my build". Implementation: loadPatch({routes: [], tweaks: {}, seqs: {}, …}) + a zeroMode UI flag. Do NOT touch the hub or other users — this is all client-local, like moods.

The zero UI (the whole point: calm)

  • Hide the full node field. Render instead:
    • 7 slots down the left, 7 down the right — the 14 groups from config.json in a fixed, curated order (suggest: left = the elements/feelings: light, dark, fire, water, earth, air, spirit · right = the domains: planet, cosmos, heavens, human, market, wealth, summer). Each slot: a colored ring (its chakra/vibe hue — extract or define a GROUP_HUES map; the grimoire's vibe emojis are the seed), the label, and a live "breathing" glow driven by the group's average member value (already computed for group nodes) — alive but quiet.
    • A center stage — empty at first. Nodes the user builds land here.
    • An output rack along the bottom (or right-center column): the destinations, grouped and labeled in plain language:
      • OMNI voices: lead (sings) · bass (roots) · pad (glows) · drone (hums) · perc (taps)
      • OMNI space & grit: reverb.size · delay.feedback · saturation · glitch · granular.density · crush · wavetable.morph · lfo.rate · master.space · filter.cutoff
      • 🌀 earth echo knobs (echo.*) — shown only when earth echo is on
      • MIDI (when an out port is enabled): the CC map + lead/bass note channels Each output is a small labeled socket, dark until something is patched into it.
  • Slot → picker drawer: clicking a slot slides open a drawer listing that group's member feeds: name, one-line plain-English description (extract from the grimoire source blurbs / signals labels), live value bar, chakra color. Each row has ⊕. Picking 1+ feeds creates a node on the stage (or adds to the slot's existing node). The node renders like a mini group-node: the slot's color, member count badge.
  • Routing: drag from a node to an output socket → lays a cable per member feed to that dest at a sensible default amount (0.5, curve lin) — reusing addRouteLocal. Alternatively right-click node → the existing "patch to →" submenu filtered to the rack outputs. Cables render exactly like the main UI (same renderer — don't fork it).
  • Right-click assign (the "synth roll" ask): right-click an output socket or a cable →
    • note outputs (lead.note/bass.note): quantize picker — scale (the 11 SCALES), root (note name picker), octaves (1-3); presets "minor pent @C4" etc.
    • CC/level outputs: amount slider + curve picker (lin/exp/exp3/log/scurve/inv) — the existing route controls, surfaced.
    • MIDI: choose CC number / channel (when midi enabled).
  • Guidance, not tutorial: use the existing eventTicker for 3-4 contextual nudges ("pick a source from a ring", "now drag it onto a voice", "press ♪ — that's YOUR instrument"). No modal tutorial. The calm IS the feature.
  • Graduation: a small "⛈ enter the storm" affordance; leaving zero shows the full field with everything the user built still wired (their routes persist — zero is not a sandbox, it's the same matrix).

Acceptance criteria (Z-AC)

From a fresh account in zero mode, with no instructions beyond the ticker nudges, this sequence works in under a minute and makes sound: click a ring → pick sun.speed → drag node to filter.cutoff; click another → pick crypto.price → drag to lead → right-click lead → minor pent @ 60 → press ♪ → bitcoin sings through a sun-opened filter. Verified in the preview with a screenshot; console clean; leaving zero → first light works; re-entering zero offers restore.

3. VIBES — build a vibe, save a vibe, share a vibe

A vibe = a small, additive, shareable bundle of wiring:

{ "v": 1, "name": "sun filter", "emoji": "☀️",
  "routes": [{"source": "sun.speed", "dest": "filter.cutoff", "amount": 0.85, "curve": "exp"},
             {"source": "crypto.price", "dest": "lead.note", "amount": 1.0,
              "quantize": {"scale": "minor_pent", "root": 60, "octaves": 2}}] }
  • Apply = additive (registerRoute upserts; nothing else touched). Tag applied routes with viaVibe: "<name>" so a vibe can be toggled off (remove exactly the routes it added — removeRouteLocal). Multiple vibes stack; a route present in two stays until both are off.
  • Save a vibe: in zero mode, "💾 save as vibe" on the stage selection (or the whole current zero build); in the full UI, right-click a node → "save these cables as a vibe". Named + emoji.
  • Store: account-side, in auth.py — additive migration: either a new vibes table mirroring presets, or a kind column on presets defaulting 'dimension'. Endpoints mirror the preset ones (list/save/delete). Local fallback to localStorage when signed out.
  • Share: two mechanisms, both v1:
    1. Vibe code: compact string GSV1.<base64url(json)> — "copy vibe code" puts it on the clipboard; an "import vibe" box (⚙ panel + zero mode) accepts a pasted code.
    2. URL fragment: godstrument.pro/#vibe=GSV1.... — on load (post-login), offer "apply the vibe you were sent?" Never auto-apply; show its name + route count first. (Fragment never reaches the server — clean with the invite-only model.)
  • Validation: parse defensively — unknown sources/dests in an imported vibe are listed and skipped, not errors. Cap: 32 routes per vibe.

Vibes AC

Save a 2-route vibe, toggle it off/on, copy its code, hard-reload, import the code, apply — identical wiring returns. A vibe code from another account applies cleanly.

4. DIMENSIONS — the full scene save

  • Rename the user-facing surface: ⚙ "MY PATCHES" → "DIMENSIONS" ("a dimension is the whole world you built — every cable, mix, groove, tempo, key"). Same serializePatch blob, same endpoints — this is a naming + framing change; do NOT break existing saved presets (they simply appear as dimensions).
  • Zero mode ships with the blank state as "dimension zero" (a virtual, always- available entry at the top of the list — not stored, generated).
  • Grimoire: rewrite the "Saving your instrument" section for the dimension/vibe split: a dimension is everything; a vibe is a feeling you can hand to a friend.

5. The OMNI panel

A dedicated synth panel, sibling to the tracks panel (same open/close/resize pattern — arrbtn precedent). Button: 🎹 OMNI bottom-right next to 🎛 tracks; key-bindable.

  • Layout — a synth, not a mixer: module strips:
    • VOICE: the pluck voice selector — FM tine · 🎸 string · 🎛 GODSONIQ (with a "sample 3s" button + sampling state) — wired to Synth.stringVoice() / Synth.godsoniq() / Synth.sampling(); plus 🌐 tab feed toggle.
    • LEAD (wavetable.morph knob) · BASS · PAD (pad.brightness) · DRONE (drone.voices; a small "7.83 Hz" schumann lamp when earth echo is on) · PERC (perc.density).
    • FX RACK: filter.cutoff · reverb.size · delay.feedback · saturation · glitch · granular.density · crush · lfo.rate · master.space (+ chorus shown as a fixed, labeled, non-decorative indicator — it IS always on; label it honestly).
    • 🌀 EARTH ECHO: on/off + the six echo.* knobs (only enabled when on).
    • TRANSPORT: ♪ toggle, mood selector, the current scale (from gScale, click → scale picker), BPM readout linked to godtime.
  • Knob semantics (the crucial design): every knob shows the live world-driven value as an animated arc (from the dest's current value — the world's hand). Dragging a knob adjusts the dest's tweak gain/offset (existing setParamLocal machinery) — you're riding a fader the world is also pushing. Double-click resets the tweak to neutral (the ⌘⇧-click semantic). A small dot shows when a knob has a user tweak. Right-click a knob = the node's existing context menu (mute/solo/level/ cables/patch to). No new audio paths — the panel is a view over dests + tweaks.
  • Muted dests render dim; a grooved/p-locked dest shows a tiny grid icon.
  • Works inside zero mode too (it doubles as zero's output rack detail view — if this is cleanly achievable, the rack and the panel share components; if not, keep them separate and simple).

OMNI AC

Open the panel: every knob's arc moves with live data; drag reverb.size up — audibly more reverb and the dest tweak persists in the saved dimension; voice selector swaps FM→string→GODSONIQ and plucks confirm; screenshot for the session log; console clean.

6. Milestones (each: build → verify in preview → commit)

  • Z0 — blank dimension + zero entry/exit + first-visit choice card + prezero restore.
  • Z1 — 7+7 slots + picker drawer + stage nodes (no routing yet). Colors/labels/live glow.
  • Z2 — routing to the output rack + right-click assign (quantize/curve/amount) + ticker nudges. Z-AC passes.
  • Z3 — vibes: tag/apply/toggle, save to account (additive auth.py migration), vibe code export/import + #vibe= fragment. Vibes AC passes.
  • Z4 — dimensions rename + dimension-zero entry + grimoire "Saving" rewrite.
  • Z5 — OMNI panel. OMNI AC passes.
  • Z6 — grimoire sections (zero mode gets its own tech-manual section in the style of OMNI/Earth Echo/GODSONIQ: numbered how-to + spec table), regenerate the manual, adversarial review workflow over the whole feature set, fix confirmed findings, deploy to botchat, curl-verify live, update memory.

7. Risks & taste notes

  • viz/index.html is ~7.5k lines of interlocking IIFE — add new sections as their own IIFEs near their dependencies; do not restructure existing code to "make room".
  • The 14-group order and hues are a taste call — propose them, note them as one-line consts John can retune by ear/eye (the skins pattern).
  • Zero mode must never fork the matrix/renderer: same routes, same cables, same synth. If zero starts needing its own compute path, stop — the design has gone wrong.
  • Don't gate existing users into zero — it's a door, not a wall. gs_mood returning users land where they left off.
  • Keep the first-visit card copy short and Godstrument-voiced. No onboarding-speak.

STATUS + CONTINUATION (Fable review, 2026-07-10)

Z0Z2 verified and approved (commits 2a705e3, d30cea4, 4e44533, 9a7f947): the three-door card, the calm 7+7 ring field, spinning orbs, drag-to-route, the synth-roll assign menu — all live-verified, Z-AC met (bitcoin sang through a sun-opened filter from zero), console clean, syntax clean, field-draw correctly guarded. Genuinely good work.

Continue from here, in this order:

C1. Fix: leaving zero silently DISCARDS the user's build (real bug)

exitZeroToMood always restores gs_prezero, replacing routesArr — so a user who built cables in zero and clicks "first light" loses their build with no warning. The brief's intent (§2 graduation): "leaving zero shows the full field with everything the user built still wired." Fix: on exit, if the current (zero-built) patch has ≥1 route, offer a small three-way choice (same visual language as the door card, but compact):

  • keep my build — don't restore prezero; just drop the overlay + apply the mood's mutes/gains only (the build stays as the whole patch);
  • bring the old world back — current behavior (restore prezero);
  • weave them together — restore prezero THEN re-registerRoute the zero-built routes on top (registerRoute dedups by source|dest). If the zero build has 0 routes, skip the prompt entirely (current behavior is right).

C2. Zero build must survive a reload

Routes persist via the matrix, but zeroUI.nodes (picked feeds / stage orbs) don't — a returning zero-user (gs_mood="0 zero") reloads into an empty stage even though their cables exist. Persist the picks to localStorage (gs_zeronodes: {group: [srcKeys]}) on every pick/route; restore in zeroUI.open(). Also reconstruct socket lit-state (already done via refreshSockets) — verify with a reload test.

C3. Orb interactions beyond drag (touch + depth)

HTML5 drag doesn't exist on touch, and orbs currently have no menu:

  • Tap-to-patch fallback: click/tap an orb → it "arms" (glow ring, cursor hint); click a socket → routeGroup(armed, dest); click elsewhere → disarm. Keep drag working.
  • Right-click an orb → small menu: member list with × to remove a feed, "unpatch everything from this orb", and (stub for Z3) "💾 save as vibe". Removing the last member removes the orb.
  • ⛈ graduation affordance: a small fixed "⛈ enter the storm" pill inside the zero UI (bottom-right above the rack) → runs the C1 exit flow with mood "the full storm".

C4 = Z3 (vibes), Z5 (OMNI panel), Z4 (dimensions), Z6 (ship) — per the brief above

Follow the brief sections 3, 4, 5, 6 as written, with these additions learned since:

  • The orb right-click "save as vibe" (C3) is the natural zero-mode entry to Z3 — a vibe is exactly one orb's members + the routes they carry.
  • auth.py vibes store: use the existing CREATE TABLE IF NOT EXISTS idiom (auth.py:44-46 style) so the server migrates itself on restart — additive only. Test locally against a COPY of godstrument_users.db, never the live file, and remember the deploy never ships the db.
  • Vibe import parses user-pasted data → validate hard (source/dest against known sets, numeric clamps, 32-route cap, try/catch the base64/JSON). This is a trust boundary.
  • Before the Z6 deploy, run the /ship-check skill (pre-deploy security sweep) — this feature set touches auth.py and user-pasted input, which is exactly its territory.
  • Nit (low): while in zero, the chakra-view key (F3/C) flips chakraMode with nothing drawn — make it a no-op in zero (or have it exit zero first via the C1 flow).

Keep the cadence: extraction-check → build → verify in the logged-in preview (z0test / zeropass123 account exists) → screenshot → commit per milestone. Deploy only at Z6 after the adversarial review. The grimoire is truth — Z6 writes zero/vibes/dimensions/OMNI-panel sections in the tech-manual style (numbered how-to + spec table) and regenerates the manual.