Merge Examples/RL tabs into single unified toolbar across all four UIs. Users can now freely mix supervised learning (Add Example + Train) with RL feedback (thumbs up/down) without switching modes. Param bar dragging is always enabled. Add Web MIDI input module (js/synth/midi-input.js) for external MIDI controllers — routes note on/off to C15 synth, CC 1/2 to joystick. Add gamepad module (js/ui/gamepad.js) with auto-detection, deadzone, and axis normalization across all UIs. Fix synth parameter sync: routeOutputs() now called after trainModel() in onThumbsUp and loadState to prevent stale params on first joystick move. Add separate Clear Examples button (dataset only, keeps weights).
184 lines
8.9 KiB
HTML
184 lines
8.9 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="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
|
<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 · 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>
|
|
|
|
<!-- Unified controls -->
|
|
<div class="wb-action-group" id="controls">
|
|
<button class="wb-btn wb-btn-bad" id="btn-thumbsdown"><span class="wb-btn-label">− Explore</span><span class="wb-key-hint">1</span></button>
|
|
<button class="wb-btn wb-btn-good" id="btn-thumbsup"><span class="wb-btn-label">+ Good</span><span class="wb-key-hint">2</span></button>
|
|
<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" id="btn-clear-examples">Clear Examples</button>
|
|
<button class="wb-btn wb-btn-danger" id="btn-clear">Clear All</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 class="wb-synth-row" id="midi-row" style="display:none">
|
|
<label>MIDI</label>
|
|
<button class="wb-btn" id="midi-toggle">Enable</button>
|
|
<select id="midi-select" style="flex:1;min-width:0"></select>
|
|
</div>
|
|
<div class="wb-synth-row" id="midi-status-row" style="display:none">
|
|
<span class="wb-synth-status" id="midi-status"></span>
|
|
</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">—</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 class="wb-metric"><span class="wb-metric-value" id="gamepad-status" style="color: #ff6a00; font-size: 0.7rem;"></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>Learning</h3>
|
|
<p>Both approaches can be freely mixed:</p>
|
|
<ul>
|
|
<li><strong>RL Feedback</strong>: Press <strong>+ Good</strong> to reinforce the current mapping, or <strong>− Explore</strong> to add noise and try variations.</li>
|
|
<li><strong>Supervised</strong>: Drag param bars to set desired outputs, then <strong>Add Example</strong> and <strong>Train</strong>.</li>
|
|
</ul>
|
|
<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>
|