memlnaut-nisps/playground/b-workbench.html
w1n5t0n a669a54543 feat(playground): add three alternative UI design explorations
Three redesigns of the playground UI, each exploring a different
interaction philosophy for the NISPS interactive ML engine:

A) Immersive — fullscreen canvas with floating glassmorphism overlays
B) Workbench — dashboard with 2D mapping heatmap as centerpiece
C) Journey — phased UI that dissolves (Explore → Teach → Perform)

All share the same 126-output MLP ([32,48,64] hidden layers), C15
synth bridge, and flow field visualizer. Each adds:
- SynthVisualizer (126 grouped vertical bars) replacing particles in synth mode
- RL as default learning mode with keyboard shortcuts (1/2)
- Follow mode for trackpad exploration
- ?tame= URL param for safe-range constraining
- localStorage persistence (random on first boot)
- Live iframe previews on designs.html homepage

See devlog/2026-03-21-ui-redesign-explorations.md and meml-2c3 for
remaining refinements identified during browser testing.
2026-03-22 00:57:58 +02:00

191 lines
8.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>NISPS Workbench</title>
<link rel="stylesheet" href="css/b-workbench.css">
</head>
<body>
<div class="workbench">
<!-- Header -->
<header class="wb-header">
<div class="wb-header-left">
<a href="designs.html" class="wb-back-btn" title="Back">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M10 2L4 8l6 6"/></svg>
</a>
<h1 class="wb-title">NISPS <span class="wb-title-accent">Workbench</span></h1>
</div>
<div class="wb-header-right">
<span class="wb-mode-badge" id="mode-badge">Visual</span>
<button class="wb-help-btn" id="help-btn" title="Help">?</button>
</div>
</header>
<!-- Main grid -->
<div class="wb-grid">
<!-- Left column: Canvas -->
<section class="wb-panel wb-canvas-panel">
<div class="wb-panel-header">
<span class="wb-panel-title">Flow Field</span>
<div class="presets" id="presets-visual">
<button class="preset-pill" data-preset="calm-to-chaotic">Calm/Chaos</button>
<button class="preset-pill" data-preset="rainbow-sweep">Rainbow</button>
<button class="preset-pill" data-preset="vortex">Vortex</button>
</div>
</div>
<div class="wb-canvas-wrap">
<canvas id="visual-canvas"></canvas>
<canvas id="synth-vis-canvas" class="hidden"></canvas>
</div>
</section>
<!-- Right column: Input Space (merged joystick + mapping) -->
<section class="wb-panel wb-mapping-panel">
<div class="wb-panel-header">
<span class="wb-panel-title">Input Space</span>
<button class="wb-follow-toggle-btn" id="follow-toggle-btn">Follow</button>
</div>
<div class="wb-mapping-area">
<div class="wb-heatmap-wrap">
<canvas id="mapping-canvas" width="280" height="280"></canvas>
<div class="wb-follow-badge hidden" id="follow-badge">FOLLOW</div>
<div class="wb-heatmap-label">Drag to explore &middot; double-click or <kbd>f</kbd> to toggle follow</div>
</div>
</div>
<!-- Constellation -->
<div class="wb-constellation-wrap">
<canvas id="constellation-canvas" width="120" height="120"></canvas>
<span class="wb-constellation-label" id="constellation-label">20 parameters</span>
</div>
</section>
<!-- Bottom left: Param Groups -->
<section class="wb-panel wb-params-panel">
<div class="wb-panel-header">
<span class="wb-panel-title">Parameters</span>
<div class="wb-output-toggle">
<button class="wb-toggle-btn active" id="toggle-visual" data-mode="visual">Visual</button>
<button class="wb-toggle-btn" id="toggle-synth" data-mode="synth">Synth</button>
</div>
</div>
<div class="wb-param-groups" id="param-groups"></div>
</section>
<!-- Bottom right: Controls + Metrics -->
<section class="wb-panel wb-controls-panel">
<div class="wb-panel-header">
<span class="wb-panel-title">Controls</span>
</div>
<!-- Learning mode toggle -->
<div class="wb-mode-toggle">
<button class="wb-mode-btn" id="mode-examples">Examples</button>
<button class="wb-mode-btn active" id="mode-rl">RL</button>
</div>
<!-- Examples mode controls -->
<div class="wb-action-group hidden" id="examples-controls">
<button class="wb-btn wb-btn-primary" id="btn-add">Add Example</button>
<button class="wb-btn wb-btn-accent" id="btn-train">Train</button>
<button class="wb-btn" id="btn-randomize">Randomize</button>
<button class="wb-btn wb-btn-danger" id="btn-clear">Clear</button>
</div>
<!-- RL mode controls -->
<div class="wb-action-group" id="rl-controls">
<button class="wb-btn wb-btn-bad" id="btn-thumbsdown">&minus; Explore</button>
<span class="wb-key-hint">1</span>
<button class="wb-btn wb-btn-good" id="btn-thumbsup">+ Good</button>
<span class="wb-key-hint">2</span>
<button class="wb-btn" id="btn-randomize-rl">Randomize</button>
</div>
<!-- Synth controls (shown in synth mode) -->
<div class="wb-synth-controls hidden" id="synth-controls">
<div class="wb-synth-row">
<button class="wb-btn wb-btn-primary" id="synth-start">Start Audio</button>
<span class="wb-synth-status" id="synth-status">Stopped</span>
</div>
<div class="wb-synth-row">
<label>Volume</label>
<input type="range" id="synth-volume" min="0" max="1" step="0.01" value="0.5">
</div>
<div class="wb-synth-row">
<button class="wb-btn" id="arp-toggle">Arp: Play</button>
<select id="arp-progression">
<option value="I-vi-IV-V">I-vi-IV-V</option>
<option value="I-IV-vi-V">I-IV-vi-V</option>
<option value="i-VI-III-VII">i-VI-III-VII</option>
<option value="I-V-vi-IV">I-V-vi-IV</option>
</select>
</div>
<div class="wb-synth-row">
<label>Tempo <span id="tempo-val">120</span></label>
<input type="range" id="arp-tempo" min="40" max="240" step="1" value="120">
</div>
<div class="wb-synth-row">
<label>Octaves <span id="octaves-val">2</span></label>
<input type="range" id="arp-octaves" min="1" max="5" step="1" value="2">
</div>
<div class="wb-synth-row">
<label>Offset <span id="offset-val">0</span></label>
<input type="range" id="arp-offset" min="-2" max="3" step="1" value="0">
</div>
</div>
<!-- Loss plot -->
<div class="wb-loss-wrap">
<div class="wb-loss-label">Loss</div>
<canvas id="loss-canvas" width="280" height="60"></canvas>
</div>
<!-- Status metrics -->
<div class="wb-metrics" id="metrics">
<div class="wb-metric"><span class="wb-metric-label">Examples</span><span class="wb-metric-value" id="metric-examples">0</span></div>
<div class="wb-metric"><span class="wb-metric-label">Loss</span><span class="wb-metric-value" id="metric-loss">&mdash;</span></div>
<div class="wb-metric"><span class="wb-metric-label">Noise</span><span class="wb-metric-value" id="metric-noise">0.050</span></div>
<div class="wb-metric"><span class="wb-metric-label">Joy X,Y</span><span class="wb-metric-value" id="metric-joy">0.50, 0.50</span></div>
</div>
</section>
</div><!-- /wb-grid -->
<!-- Toast notification -->
<div class="wb-toast hidden" id="toast"></div>
<!-- Help overlay -->
<div class="wb-help-overlay hidden" id="help-overlay">
<div class="wb-help-content" onclick="event.stopPropagation()">
<h2>NISPS Workbench</h2>
<p>Train a neural network to map 2D joystick positions to output parameters using interactive machine learning.</p>
<h3>Input Space</h3>
<p>The heatmap shows what the network has learned across the input space. Bright dots show training examples. Drag anywhere on the map to move the input position. Double-click or press Follow to toggle follow mode.</p>
<h3>Examples Mode</h3>
<ol>
<li>Drag on the input space to a position</li>
<li>Expand a parameter group and drag bars to set desired outputs</li>
<li>Press <strong>Add Example</strong></li>
<li>Repeat, then <strong>Train</strong></li>
</ol>
<h3>RL Mode</h3>
<ol>
<li>Move around the input space &mdash; the network produces outputs</li>
<li>Like it? Press <strong>+ Good</strong></li>
<li>Not quite? Press <strong>&minus; Explore</strong> for variation</li>
</ol>
<h3>Output Modes</h3>
<p><strong>Visual</strong>: 20 params control the flow-field particle system. <strong>Synth</strong>: 126 params control a C15 synthesizer engine.</p>
<p style="margin-top:16px;text-align:center;color:#555;">Tap outside to close</p>
</div>
</div>
</div>
<script type="module" src="js/b-app.js"></script>
</body>
</html>