🎭 performance mode goes pure — nothing on stage but the world

P (or F7) now removes everything that is not the world: the wordmark,
the header and its counts, the godtime readout, the ticker, every
button and chip. Only the visuals and the rotating dispatch line
remain for the audience. Z stays zen for the fully wordless sky.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-06 23:30:26 +10:00
parent aa81535379
commit 56a775fcf8

View File

@ -398,6 +398,8 @@
#grimoire code { font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; color: #b8e0c8; background: rgba(120,255,178,0.08); padding: 0 4px; border-radius: 4px; }
#grimoire .hr { height: 1px; background: linear-gradient(90deg, transparent, rgba(140,170,230,0.25), transparent); margin: 30px 0; }
#grimoire .foot { text-align: center; color: rgba(150,170,205,0.55); font-size: 12px; margin-top: 40px; font-style: italic; }
body.zen > :not(#stage):not(#ctxmenu) { display: none !important; } /* zen — nothing but the sky */
body.perform > :not(#stage):not(#ctxmenu) { display: none !important; } /* performance — pure visuals, no chrome */
</style>
<canvas id="stage"></canvas>
<div id="gear" title="controls"></div>
@ -921,7 +923,9 @@
<li><b>⌘/Ctrl-click several sources, then group them — mint your own vibe.</b> Multi-select spheres by feeling, not category — a <i>shadow</i> of debt and inflation and quakes and the death rate, a <i>bright</i> of births and moonlight and fertility — and group them into a new named macro you can then mute, freeze, or gain as one hand-sized thing. The factory ships fourteen such vibes (<i>planet, cosmos, human, market, heavens, dark, light, and the seven elemental + seasonal blends</i>); yours join them.</li>
<li><b>♪ (top-right) — wake the sound.</b> Browsers hold audio until a human asks. This is the ask. One click and the in-browser synth spins up, and the planet — already flowing as data — becomes audible.</li>
<li><b>The godtime dial (top) — set the pulse.</b> Drag it to choose a tempo from <b>40 to 180 BPM</b>, with the real planets of tonight's sky wheeling around it as you turn. Click the dial to <b>lock</b> the tempo where you left it — otherwise the data itself keeps nudging it (bitcoin's velocity, the dealgod market, inflation, each with a small hand on the clock).</li>
<li><b>Press <kbd>P</kbd> — performance mode.</b> The editing chrome vanishes, the readouts declutter, and a rotating line of dispatches from the world (<i>bitcoin at $62,902 · the moon is 81% lit · mercury is retrograde · M4.2 earthquake</i>) rises for the audience. Put the console on the projector, hit <kbd>P</kbd>, and conduct.</li>
<li><b>Press <kbd>P</kbd> — performance mode.</b> Everything that is not the world vanishes: the wordmark, the header, the counts, the godtime readout, the ticker, every button and chip — <i>pure visuals</i>, with only a rotating line of dispatches from the world (<i>bitcoin at $62,902 · the moon is 81% lit · mercury is retrograde · M4.2 earthquake</i>) rising for the audience. Put the console on the projector, hit <kbd>P</kbd>, and conduct. (Want even the labels and dispatches gone? <kbd>Z</kbd> is zen — nothing but the sky.)</li>
<li><b>Press <kbd>W</kbd> — the sky wheel.</b> The zodiac rises behind the whole instrument, huge and faint: the twelve signs in their element colours, the <b>living planets</b> on the rim exactly where they stand tonight (the Sun in Cancer in early July, wherever the Moon has wandered to), and the sky's <b>aspects strung as chords</b> across the wheel — red for the hard angles, green for the soft, gold for the meetings. Cast a chart (✨ your stars) and the layer deepens: your <b>whole-sign houses</b> divide the wheel, the ASC and MC stand in gold, your natal planets sit as gold ghosts on the inner ring, and transit chords strike them as the sky moves. The same wheel the zodiac skin draws, now living under the main view. <kbd>W</kbd> again to put it away; it remembers.</li>
<li><b>Press <kbd>Z</kbd> — zen.</b> Every word vanishes: labels, readouts, panels, the header, the ticker, the dial — all of it — and only the visuals remain, full screen. The spheres, the cables, the shockwaves, the sky wheel if it's up, still moving to the world; nothing left to read, everything left to watch. <kbd>Z</kbd> or <kbd>esc</kbd> brings the words back.</li>
<li><b>The keyboard is yours — <kbd>F1</kbd><kbd>F10</kbd> and beyond.</b> A default set of function keys is ready to fly: <kbd>F1</kbd> the grimoire, <kbd>F2</kbd> play/stop the synth, <kbd>F3</kbd> the skin view, <kbd>F4</kbd> next skin, <kbd>F5</kbd> cycle mood, <kbd>F6</kbd> tracks, <kbd>F7</kbd> performance mode, <kbd>F8</kbd> lock the godtime, <kbd>F9</kbd> the earth echo, <kbd>F10</kbd> the stage. But every key is remappable: right-click the sky → <b>⌨ keys</b> to bind any key to any action, or <b>map a key</b> from a voice's own right-click menu. Want <kbd>a</kbd> and <kbd>s</kbd> to raise and lower a drone's level like a fader? Right-click that voice → <i>map a key → raise its level</i>, press <kbd>a</kbd>; again for <i>lower</i>, press <kbd>s</kbd> — now they nudge it live, hold to sweep. Your whole flow, under your fingers.</li>
<li><b>Press <kbd>C</kbd> — chakra view.</b> The sources leave their column and take up their stations on the subtle body: seven wheels on a spine of light, crown to root, each feed orbiting the chakra it belongs to — the sky's machinery at the crown, the omens at the third eye, the species talking at the throat, weather and air at the heart, fire and money at the solar plexus, birth and water at the sacral, quakes and debt and the mortal weight at the root. Each wheel glows with the live average of its members and spins its traditional petals, counter-rotating wheel to wheel. <b>Drag empty space to spin the whole body 360°</b>, drag up/down to tilt, flick and let momentum carry it. Everything still works in there: click a node to mute it, drag it onto a voice to patch, shift-click for its tab. <kbd>C</kbd> again and the sources glide home.</li>
<li><b>Right-click anything — the everything menu.</b> Every node carries its full settings under the right button: mute, <b>solo</b>, a <b>level</b> fader (02×), a <b>trim</b> offset, every cable it carries with its amount slider and an unpatch ×, and a nested <i>patch to →</i> list for laying new cables without dragging. Right-click the empty sky for the view menu. And the modifiers, for the fast hands: <b>click</b> mutes · <b>⇧-click</b> opens the node's tab · <b>⇧-drag</b> rides the level up and down · <b>⌥-click</b> solos a source · <b>⌘-click</b> multi-selects for grouping · <b>⌘⇧-click</b> resets a node to neutral. People play differently; the instrument shouldn't care which hand you reach with.</li>
@ -2140,7 +2144,7 @@
try { loupe.on = localStorage.getItem("gs_loupe") !== "0"; } catch (e) {}
function updateLoupe(mx, my) {
if (!loupe.on || chakraMode || performMode || !columnInfo
if (!loupe.on || chakraMode || performMode || zenMode || !columnInfo
|| inspectEl.classList.contains("open") || arr.open) { loupe.active = false; return; }
if (loupe.frozen) return; // held still through a press
// once open, keep it open while the cursor is over the magnified panel, so
@ -2571,6 +2575,10 @@
if (chakraMode) updateChakra(t, dt); // positions sources on the subtle body
drawBackground(t);
if (skyWheel && !chakraMode && connected) { // the sky wheel, huge and faint behind everything (press W)
const r = Math.min(W, H) * 0.47;
drawWheelOverlay(t, null, { x0: W / 2 - r, x1: W / 2 + r, y0: H / 2 - r, y1: H / 2 + r });
}
drawCables(t);
drawShockwaves(t, dt);
drawParticles(dt);
@ -2580,10 +2588,10 @@
for (const n of dests.values()) drawDestNode(n, t);
if (!chakraMode) drawLoupe(t); // the hover zoom over a dense column
drawEditing(t);
if (performMode) drawPerform(t, dt);
drawHeader(t);
if (connected) drawGodtime(t);
drawFooter(t);
if (performMode && !zenMode) drawPerform(t, dt);
if (!zenMode && !performMode) drawHeader(t); // performance: no wordmark, no counts
if (connected && !zenMode && !performMode) drawGodtime(t);
if (!zenMode && !performMode) drawFooter(t); // performance: no ticker
if (!connected) drawConnecting(t);
requestAnimationFrame(frame);
@ -3310,8 +3318,8 @@
if (n && typeof n.raw === "number") n.station = SIGNS[Math.floor((((n.raw % 360) + 360) % 360) / 30)].key;
}
}
function drawWheelOverlay(t, g) {
const box = mapBox(g);
function drawWheelOverlay(t, g, layerBox) { // layerBox: drawn as a main-screen layer (press W)
const box = layerBox || mapBox(g);
const cx = (box.x0 + box.x1) / 2, cy = (box.y0 + box.y1) / 2;
const R = Math.min(box.x1 - box.x0, box.y1 - box.y0) * 0.5 - 4;
const lonXY = (lon, r) => {
@ -3319,11 +3327,24 @@
return [cx + r * Math.cos(a2), cy - r * Math.sin(a2)];
};
ctx.save();
glyphPass = true; // the wheel's glyphs survive zen
ctx.textAlign = "center"; ctx.textBaseline = "middle";
ctx.strokeStyle = "rgba(200,212,240,0.20)"; ctx.lineWidth = 1;
ctx.beginPath(); ctx.arc(cx, cy, R, 0, Math.PI * 2); ctx.stroke();
ctx.beginPath(); ctx.arc(cx, cy, R * 0.55, 0, Math.PI * 2); ctx.stroke();
// as a layer the skin's station nodes aren't there, so the wheel carries
// its own signs: boundary ticks and the twelve glyphs, in element colour
if (layerBox) for (let i2 = 0; i2 < 12; i2++) {
const t1 = lonXY(i2 * 30, R - 6), t2 = lonXY(i2 * 30, R + 5);
ctx.beginPath(); ctx.moveTo(t1[0], t1[1]); ctx.lineTo(t2[0], t2[1]);
ctx.strokeStyle = "rgba(200,212,240,0.14)"; ctx.lineWidth = 1; ctx.stroke();
const gp = lonXY(i2 * 30 + 15, R + 17);
ctx.fillStyle = rgba(SIGNS[i2].col, 0.55);
ctx.font = "13px 'Helvetica Neue', Arial, sans-serif";
ctx.fillText(SIGNS[i2].label.split(" ")[0] + "", gp[0], gp[1]); // : text glyph, not emoji
}
// whole-sign houses + the ascendant, when a chart with a birthplace is cast
if (natalActive && natalActive.asc != null) {
const ascSign = Math.floor(natalActive.asc / 30);
@ -3417,6 +3438,7 @@
ctx.fillStyle = "rgba(224,234,255,0.95)";
ctx.fillText(lv.glyph, pp[0], pp[1]);
}
glyphPass = false;
ctx.restore();
}
@ -4547,6 +4569,18 @@
let learnMode = false, learnTarget = null;
let patchList = [], templateSelect = null;
let performMode = false, dispatchIdx = 0, dispatchT = 0;
// the sky wheel as a main-screen layer (W) and zen — nothing but the sky (Z)
let zenMode = false, glyphPass = false;
let skyWheel = false;
try { skyWheel = localStorage.getItem("gs_wheel") === "1"; } catch (e) {}
function toggleWheel() {
skyWheel = !skyWheel;
try { localStorage.setItem("gs_wheel", skyWheel ? "1" : "0"); } catch (e) {}
}
function setZen(on) { zenMode = on; document.body.classList.toggle("zen", on); }
const _fillText = ctx.fillText.bind(ctx); // zen silences every canvas label…
ctx.fillText = (s, x, y, m) => { if (!zenMode || glyphPass) _fillText(s, x, y, m); }; // …except the wheel's glyphs
const midiBindings = loadBindings(); // "cc:17" / "note:36" -> {target,param,mode}
const lastCCSent = {}, lastNoteFor = {};
let outSel = null, inSel = null, midiStatusEl = null, learnBtn = null, bindCountEl = null;
@ -4708,19 +4742,17 @@
}
}
if (ev.key === "p" || ev.key === "P") {
performMode = !performMode;
if (performMode) {
gearEl.style.display = "none"; panelEl.style.display = "none";
inspectEl.style.display = "none";
} else {
gearEl.style.display = ""; panelEl.style.display = ""; inspectEl.style.display = "";
}
}
performMode = !performMode; // pure visuals: the CSS hides every
document.body.classList.toggle("perform", performMode); // DOM chrome, the frame loop
} // skips header/godtime/footer
if (ev.key === "c" || ev.key === "C") {
chakraMode = !chakraMode;
if (!chakraMode) { layoutDirty = true; exitMind(); } // sources glide home
else clearTrails();
}
if (ev.key === "w" || ev.key === "W") toggleWheel(); // the sky wheel layer
if (ev.key === "z" || ev.key === "Z") setZen(!zenMode); // zen — nothing but the sky
if (ev.key === "Escape" && zenMode) setZen(false);
if (ev.key === "t" || ev.key === "T") {
arr.open ? closeArranger() : openArranger(); // tracks, for the casuals
}
@ -4913,6 +4945,8 @@
}
ctxItem(performMode ? "leave performance mode" : "performance mode",
() => window.dispatchEvent(new KeyboardEvent("keydown", { key: "p" })), "P");
ctxItem((skyWheel ? "✓ " : "") + "☉ sky wheel — signs, houses & planets", () => toggleWheel(), "W");
ctxItem(zenMode ? "leave zen" : "☾ zen — only the visuals", () => setZen(!zenMode), "Z");
ctxItem(arr.open ? "close tracks" : "🎛 tracks — arrange clips",
() => { arr.open ? closeArranger() : openArranger(); }, "T");
const moodSub = ctxSub("🌗 mood — how much world");
@ -5485,6 +5519,8 @@
moodCycle: { label: "cycle mood (first light / drift / storm)", run: () => cycleMood() },
tracks: { label: "tracks / arranger", run: () => { arr.open ? closeArranger() : openArranger(); } },
perform: { label: "performance mode", run: () => window.dispatchEvent(new KeyboardEvent("keydown", { key: "p" })) },
skyWheel: { label: "the sky wheel (zodiac layer)", run: () => toggleWheel() },
zen: { label: "zen — only the visuals", run: () => setZen(!zenMode) },
godLock: { label: "lock / unlock godtime", run: () => { godtime.locked = !godtime.locked; } },
stage: { label: "the stage (project the view)", run: () => { stageWin ? closeStage() : openStage(null); } },
earthEcho: { label: "toggle the earth echo", run: () => earthEchoSet(!earthEchoOn) },