moods (song-shaped first load) + audit fixes
- Moods: "first light" (bass line, hats, small melody, soft space, tempo locked 92 — auto-applied on very first visit with a welcoming ticker), "ambient drift" (no drums, all atmosphere), "the full storm" (the factory instrument). Sky menu -> mood submenu; mood grooves use fresh arrays so arranger banks are never overwritten. Grimoire gains "Zero — the moods". - Audit fixes (self-audit; the review fleet hit the daily session cap): commune peer grooves now REPLACE the steps reference instead of writing in-place — a peer can no longer silently overwrite one of your arranger bank patterns (verified by isolated repro). export .mid with no note clips now explains instead of writing an empty file. wheelyearOf ordering fixed so sky-harmony reaches Ostara instead of Samhain's astro catch-all. Commune docs note the room word is the key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0a4c28b4c2
commit
7cad65125b
@ -898,7 +898,7 @@
|
||||
<li><b>The groove grids — sequence the voices.</b> Open any voice's tab (shift-click it) and there's a <b>16-step grid on the godtime clock</b>. The note voices (<i>lead, bass</i>) get a <b>mini piano roll</b> — eight rows of minor pentatonic, click to place a note, click again to clear — and while your groove is on, <i>your melody replaces the data's</i>: bitcoin lets go of the lead and you take it. Every other voice gets an <b>808-style gate row</b> that rhythmically chops the world's signal — the reverb, the grit, the percussion breathing in your pattern while the planet still decides how hard. Grooves ride the godtime tempo (lock the dial, or let your heart drive it), and they <b>save with your patch</b>. And the <b>sources</b> get the grid too, with a different power: <b>sample & hold</b> — while a source's groove is on, the world's number is only <i>sampled</i> on your lit steps and held between them, so a smooth drifting feed becomes stepped, rhythmic, quantized in time. Bitcoin arpeggiating in your pattern. The wind as a sequence. If an out port is chosen in ⚙, godtime also sends <b>MIDI clock</b> (24 ppqn), so a DAW can slave its tempo to the planet — or to your pulse. And the grids play <i>outward</i> too: with an out port chosen, <b>piano-roll notes go out as real MIDI</b> on the lead and bass channels (each note one sixteenth long), and the <b>mind-mode console plays on its own third channel</b> — so your grooves and your chakra-scale jams drive Ableton, Strudel, or hardware exactly as they drive the built-in synth. The three mouths drink at once: ♪ for the browser synth, the out port for MIDI, both or neither — there is no switch, only where you point the sound.</li>
|
||||
<li><b>Press <kbd>T</kbd> — tracks, for making actual songs.</b> A conventional tracks-and-clips view for anyone who finds the cathedral overwhelming: every voice is a <b>track</b>, eight <b>bars</b> run across, and each cell cycles <b>● live</b> (the world plays that lane) → <b>A</b> → <b>B</b> → <b>C</b> (clip patterns on the groove grids — A and B come pre-seeded so it makes music immediately) → <b>silent</b>. Double-click any clip to edit its groove in the piano roll. Press <b>▶</b> and the arrangement loops on the godtime clock — the planet still plays <i>through</i> your song, because live lanes stay wired to the world. Arrangements <b>save with your patch</b>, play out over MIDI like everything else, and <b>export .mid</b> hands you a Standard MIDI File of the note lanes to drag into Ableton, Logic, anything. Not a DAW — a sketchpad where the world sits in on your session. The panel earns its keep like one, too: a <b>🎛 tracks button</b> sits bottom-right always; <b>drag the panel's top edge to resize</b> (remembered); the top bar <b>tabs between clips and grooves</b> — the grooves tab is a full-size pattern editor (pick a track, pick a letter, big piano roll or 16 steps; double-clicking any clip jumps you straight there); and <b>right-click any track name for that voice's full routing menu</b> — every cable, every level, patch and unpatch without leaving the panel.</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.</b> Right-click the sky → <b>the commune</b>, agree on a room word with your friends (any word — "coven" will do), and join. No pairing, no Bluetooth, no setup: everyone on the site is already connected to the same hub, and the hub simply carries your hands to each other — same living room or opposite sides of the earth. Then <b>claim your sections</b>: right-click any voice → <i>claim this voice</i>, and from that moment your levels, mutes, grooves and arranger lanes for it follow your hands on everyone's screen. One of you takes the bass and the fires, another the pads and the moon, and someone <b>claims the godtime itself</b> and conducts. Claims are visible to all, take-overs are announced, and the world — the Source — stays identical for everyone, because it always was. Three people, one planet, one instrument.</li></ul>
|
||||
<li><b>🕸 The commune — play together.</b> Right-click the sky → <b>the commune</b>, agree on a room word with your friends (any word — though the word <i>is</i> the key, so pick an uncommon one), and join. No pairing, no Bluetooth, no setup: everyone on the site is already connected to the same hub, and the hub simply carries your hands to each other — same living room or opposite sides of the earth. Then <b>claim your sections</b>: right-click any voice → <i>claim this voice</i>, and from that moment your levels, mutes, grooves and arranger lanes for it follow your hands on everyone's screen. One of you takes the bass and the fires, another the pads and the moon, and someone <b>claims the godtime itself</b> and conducts. Claims are visible to all, take-overs are announced, and the world — the Source — stays identical for everyone, because it always was. Three people, one planet, one instrument.</li></ul>
|
||||
|
||||
<div class="hr"></div>
|
||||
|
||||
@ -916,6 +916,8 @@
|
||||
|
||||
<p class="lead">The matrix pours out clean control values whether or not anyone is listening. Choosing <i>how</i> to hear it is choosing which instrument the planet plays.</p>
|
||||
|
||||
<p><b>Zero — the moods, the gentle front door.</b> On your very first visit the instrument greets you with <b>🌅 first light</b> instead of the full flood: a bass line, soft hats, a small melody, modest space — a thing shaped like a song, with the world still playing underneath it. Right-click the sky → <b>🌗 mood</b> to move between <i>first light</i>, <i>ambient drift</i> (no drums, all atmosphere, the planet as weather), and <b>the full storm</b> — the factory instrument with every one of the world's hands on it at once. Ease in, then open the door.</p>
|
||||
|
||||
<p><b>One — the built-in browser synth.</b> Click <b>♪</b> and a full voice rack wakes inside the page: <b>lead</b> (bitcoin's price and your market's average price both quantize to it — minor pentatonic and G dorian), <b>bass</b> (quake depth chooses its notes in a low minor), <b>pad</b> (brightened by Tokyo's warmth, daylight, harmony, the birth rate, longer lives), <b>drone</b> (thickened by aircraft aloft, barometric pressure, Saturn's slow wheel, the weight of the national debt), and <b>perc</b> (BTC volatility and market activity drive the hats). Over them sit the effects the world modulates: <b>reverb</b> (earthquakes open the void, room light sizes it), <b>delay</b> (your hand's Y feeds it, Mercury retrograde smears it), <b>saturation</b> (Delhi's air becomes grit, hard aspects add dissonance, wildfires burn it, hunger grinds beneath), and <b>glitch</b> (every Wikipedia edit on Earth, every light flash, every new wildfire fires a burst). Nothing to install; the planet plays a synth in your tab.</p>
|
||||
|
||||
<p><b>Two — Web MIDI, out into a DAW or hardware.</b> In Chrome or Edge, open the ⚙ panel's WEB MIDI section, <i>enable MIDI</i>, and pick an <b>out</b> port. Every destination now streams as CC or notes straight to your rig — no Python, no IAC bus. The map is fixed and MIDI-learnable at the far end:</p>
|
||||
@ -1303,6 +1305,10 @@
|
||||
bytes.push(0, 0xff, 0x2f, 0x00);
|
||||
trks.push([bytes]);
|
||||
}
|
||||
if (trks.length === 1) { // only the tempo track — nothing to hear yet
|
||||
eventTicker.unshift({ text: "no note clips in the arrangement yet — place some A/B/C cells on lead or bass first", tAdded: now(), src: "sys" });
|
||||
return;
|
||||
}
|
||||
const flat = (t2) => t2.length === 1 ? t2[0] : t2.reduce((a, e) => a.concat(e), []);
|
||||
const chunks = [];
|
||||
const push32 = (a, v) => a.push((v >> 24) & 255, (v >> 16) & 255, (v >> 8) & 255, v & 255);
|
||||
@ -1581,8 +1587,8 @@
|
||||
if (st.tw) { const t2 = tw(c.voice); t2.mute = st.tw.m; t2.gain = st.tw.g; t2.offset = st.tw.o; }
|
||||
if (st.steps && st.steps.length === 16) {
|
||||
const sq = seqFor(c.voice, n.isNote);
|
||||
sq.steps.length = 0; Array.prototype.push.apply(sq.steps, st.steps); // in place: banks stay wired
|
||||
sq.on = !!st.on;
|
||||
sq.steps = st.steps.slice(); // replace the reference — never let a peer
|
||||
sq.on = !!st.on; // overwrite one of YOUR arranger banks
|
||||
}
|
||||
if (st.lane && st.lane.length === arr.bars) arr.grid[c.voice] = st.lane.slice();
|
||||
if (arrPaintBar) arrPaintBar();
|
||||
@ -1722,6 +1728,56 @@
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ---- moods — song-shaped presets, so first load greets instead of floods --
|
||||
// "first light" is the front door: a bass line, hats, a small melody, soft
|
||||
// space — the world still plays underneath, just fewer of its hands at once.
|
||||
const MOODS = {
|
||||
"first light": {
|
||||
bpm: 92, lock: true,
|
||||
mutes: { "glitch": 1, "granular.density": 1, "saturation": 1, "tempo.nudge": 1 },
|
||||
gains: { "reverb.size": 0.7, "delay.feedback": 0.5, "drone.voices": 0.5,
|
||||
"pad.brightness": 0.7, "perc.density": 1.3, "master.space": 0.7 },
|
||||
grooves: {
|
||||
"bass.note": [0,-1,-1,-1, 0,-1,-1,-1, 1,-1,-1,-1, 2,-1, 1,-1],
|
||||
"lead.note": [0,-1, 2,-1, 3,-1, 2,-1, 4,-1, 3, 2, 1,-1, 2,-1],
|
||||
"perc.density": [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1],
|
||||
},
|
||||
},
|
||||
"ambient drift": {
|
||||
bpm: 70, lock: true, clearGrooves: true,
|
||||
mutes: { "glitch": 1, "granular.density": 1, "saturation": 1, "tempo.nudge": 1,
|
||||
"perc.density": 1, "lead.note": 1, "bass.note": 1 },
|
||||
gains: { "reverb.size": 1.2, "master.space": 1.2, "pad.brightness": 1.0,
|
||||
"drone.voices": 0.9, "delay.feedback": 0.8 },
|
||||
},
|
||||
"the full storm": { storm: true }, // the factory instrument, every hand at once
|
||||
};
|
||||
const MOOD_KEYS = ["glitch", "granular.density", "saturation", "tempo.nudge", "wavetable.morph",
|
||||
"lfo.rate", "reverb.size", "delay.feedback", "drone.voices", "pad.brightness",
|
||||
"perc.density", "master.space", "lead.note", "bass.note"];
|
||||
function applyMood(name) {
|
||||
const m = MOODS[name];
|
||||
if (!m) return;
|
||||
for (const k of MOOD_KEYS) { const t2 = tw(k); t2.mute = 0; t2.gain = 1; t2.offset = 0; }
|
||||
if (m.storm) {
|
||||
for (const k in seqs) seqs[k].on = false;
|
||||
godtime.locked = false;
|
||||
} else {
|
||||
for (const k in (m.mutes || {})) tw(k).mute = m.mutes[k];
|
||||
for (const k in (m.gains || {})) tw(k).gain = m.gains[k];
|
||||
if (m.clearGrooves) for (const k in seqs) seqs[k].on = false;
|
||||
for (const k in (m.grooves || {})) {
|
||||
const n = dests.get(k);
|
||||
const sq = seqFor(k, n ? n.isNote : /note/.test(k));
|
||||
sq.steps = m.grooves[k].slice(); // a fresh array — never overwrite an arranger bank
|
||||
sq.on = true;
|
||||
}
|
||||
if (m.bpm) godtime.bpm = m.bpm;
|
||||
godtime.locked = !!m.lock;
|
||||
}
|
||||
eventTicker.unshift({ text: "🌗 mood: " + name, tAdded: now(), src: "sys" });
|
||||
}
|
||||
|
||||
// the launcher — one visible button, always there for the casuals
|
||||
const arrBtn = document.createElement("div");
|
||||
arrBtn.id = "arrbtn";
|
||||
@ -2040,6 +2096,15 @@
|
||||
if (Array.isArray(msg.routes)) {
|
||||
preLayoutRoutes(msg.routes);
|
||||
clientMatrix = true; // from here YOUR patch is computed locally
|
||||
// first visit: greet gently — a song-shaped mood, not the full flood
|
||||
let seen = false; try { seen = !!localStorage.getItem("gs_seen"); } catch (e) {}
|
||||
if (!seen) {
|
||||
try { localStorage.setItem("gs_seen", "1"); } catch (e) {}
|
||||
setTimeout(() => {
|
||||
applyMood("first light");
|
||||
eventTicker.unshift({ text: "🌅 first light — a gentle arrangement to start you off. right-click the sky → mood → the full storm when you're ready", tAdded: now(), src: "sys" });
|
||||
}, 900);
|
||||
}
|
||||
}
|
||||
if (Array.isArray(msg.patches)) patchList = msg.patches;
|
||||
if (msg.groups) { groupsInfo = msg.groups; buildPanel(); }
|
||||
@ -3244,10 +3309,10 @@
|
||||
};
|
||||
function wheelyearOf(key) {
|
||||
const k = (key || "").toLowerCase(), p = k.split(".")[0];
|
||||
if (p === "weather" || /season|harmony/.test(k)) return "ostara"; // the turning, the balance
|
||||
if (/deaths|quake|debt|poverty/.test(k) || p === "astro") return "samhain"; // the ancestors, the deep, the seer's night
|
||||
if (/light|ambient/.test(k)) return "yule"; // the returning light
|
||||
if (/births|fertile/.test(k) || p === "natal") return "imbolc"; // first stirrings; your birth
|
||||
if (p === "weather" || /season|harmony/.test(k)) return "ostara"; // the turning, the balance
|
||||
if (p === "fire" || ["hand", "tof", "touch", "body", "syn"].includes(p)) return "beltane"; // the fires, the dance, the union
|
||||
if (p === "sun" || /^sky\./.test(k)) return "litha"; // the sun at its height
|
||||
if (["wiki", "audio", "market", "me", "crypto", "planes", "iss"].includes(p)) return "lughnasadh"; // the great fair
|
||||
@ -4338,6 +4403,12 @@
|
||||
() => window.dispatchEvent(new KeyboardEvent("keydown", { key: "p" })), "P");
|
||||
ctxItem(arr.open ? "close tracks" : "🎛 tracks — arrange clips",
|
||||
() => { arr.open ? closeArranger() : openArranger(); }, "T");
|
||||
const moodSub = ctxSub("🌗 mood — how much world");
|
||||
for (const mn of Object.keys(MOODS)) {
|
||||
const it2 = document.createElement("div"); it2.className = "it"; it2.textContent = mn;
|
||||
it2.onclick = () => { applyMood(mn); closeCtx(); };
|
||||
moodSub.appendChild(it2);
|
||||
}
|
||||
if (mindChakra) ctxItem("step out of " + mindChakra.label, () => exitMind(), "esc");
|
||||
const fxLabel = fxMode === "auto" ? "auto (" + (fxLevel > 1 ? "full" : "lite") + ")"
|
||||
: fxMode === "ultra" ? "ultra ✦ trails" : fxMode;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user