feat(sim): collapse Dark/Light into one bipolar Mood dial; drop calibration UI

The engine already models Dark/Light as the two poles of one signed axis
(tone = (light - dark)/4, equal = neutral), so the simulator now exposes a single
bipolar Mood dial (-4 dark .. 0 neutral .. +4 light) that maps onto dark/light in
controls(). Removes the Calibration section entirely: mood_gain and overlay_gain
were always-locked 1.0 dev knobs (full dial = full effect), so they bake in as the
DEFAULT_CALIBRATION constants and the sim sends no calibration. Experience panel is
now just Left, Right, Mood.

Engine/tests unchanged (DEFAULT_CALIBRATION already carries the 1.0 constants).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-23 09:58:43 -07:00
parent cbb7c9f53d
commit f5d8a4f96b
2 changed files with 8 additions and 16 deletions
+7 -8
View File
@@ -294,25 +294,24 @@ function renderScaleReadout() {
}
function controls() {
// One bipolar Mood dial (-4 dark .. 0 neutral .. +4 light) maps onto the engine's
// two poles. The calibration gains are pinned to their locked 1.0 (full dial = full
// effect), so the simulator sends no calibration — the server uses DEFAULT_CALIBRATION.
const mood = +$("mood").value;
return {
content: $("content").value,
left: +$("left").value, right: +$("right").value,
dark: +$("dark").value, light: +$("light").value,
dark: mood < 0 ? -mood : 0, light: mood > 0 ? mood : 0,
volume: 2, brightness: 2,
};
}
function calibration() {
return { mood_gain: +$("mood_gain").value, overlay_gain: +$("overlay_gain").value,
dream_gain: 1.0 };
}
let timer = null;
async function update() {
if (busy) return;
const resp = await fetch("/api/alteration", {
method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ controls: controls(), calibration: calibration() }),
body: JSON.stringify({ controls: controls() }),
});
if (!resp.ok) { readout.textContent = "invalid: " + resp.status; return; }
const data = await resp.json();
@@ -422,7 +421,7 @@ async function main() {
try { initPaint(); } catch (e) { paintOK = false; paint.style.display = "none"; showError("WebGL init: " + e.message); }
await loadData();
renderScaleReadout();
for (const id of ["content", "left", "right", "dark", "light", "mood_gain", "overlay_gain"]) {
for (const id of ["content", "left", "right", "mood"]) {
$(id).addEventListener("input", debounced);
}
$("zoom-in").addEventListener("click", () => advance(1));
+1 -8
View File
@@ -48,16 +48,9 @@
<legend>Experience knobs (04)</legend>
<label>Left (analytical) <input type="range" id="left" min="0" max="4" value="0" /></label>
<label>Right (dreamlike) <input type="range" id="right" min="0" max="4" value="0" /></label>
<label>Dark <input type="range" id="dark" min="0" max="4" value="0" /></label>
<label>Light <input type="range" id="light" min="0" max="4" value="0" /></label>
<label>Mood — dark ◀ 0 ▶ light <input type="range" id="mood" min="-4" max="4" value="0" /></label>
</fieldset>
<fieldset>
<legend>Calibration</legend>
<label>mood gain <input type="range" id="mood_gain" min="0" max="2" step="0.05" value="1" /></label>
<label>overlay gain <input type="range" id="overlay_gain" min="0" max="2" step="0.05" value="1" /></label>
<p class="hint">Gains, not effects: <b>mood gain</b> scales the Dark/Light grade, <b>overlay gain</b> the Left HUD &amp; affect words. Turn those knobs up first or these do nothing.</p>
</fieldset>
<fieldset>
<legend>RenderPlan readout</legend>