2026-02-11 13:17:17 +01:00
|
|
|
/* NISPS Playground - Dark theme, mobile-first */
|
|
|
|
|
|
|
|
|
|
:root {
|
|
|
|
|
--bg: #0d0d0d;
|
|
|
|
|
--bg-surface: #1a1a1a;
|
|
|
|
|
--bg-elevated: #252525;
|
|
|
|
|
--text: #ccc;
|
|
|
|
|
--text-dim: #666;
|
|
|
|
|
--accent: #00ff88;
|
|
|
|
|
--accent-dim: #00cc6a;
|
|
|
|
|
--danger: #ff3366;
|
|
|
|
|
--good: #00ff88;
|
|
|
|
|
--bad: #ff6644;
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
--synth-accent: #ff6b35;
|
2026-02-11 13:17:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
|
|
|
|
|
|
html, body {
|
|
|
|
|
background: var(--bg);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro', 'Segoe UI', system-ui, sans-serif;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
touch-action: manipulation;
|
|
|
|
|
-webkit-user-select: none;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Layout --- */
|
|
|
|
|
.app {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-rows: auto 1fr auto auto auto;
|
|
|
|
|
height: 100vh;
|
|
|
|
|
height: 100dvh;
|
|
|
|
|
max-width: 600px;
|
|
|
|
|
margin: 0 auto;
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
position: relative;
|
2026-02-11 13:17:17 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 09:47:47 +01:00
|
|
|
.header,
|
|
|
|
|
.visual-container,
|
|
|
|
|
.param-container,
|
|
|
|
|
.controls-area,
|
|
|
|
|
.presets,
|
|
|
|
|
.expand-visual-btn {
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
/* Header */
|
|
|
|
|
.header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
border-bottom: 1px solid #222;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header h1 {
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
.header-right {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
.header button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
.mode-badge {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
padding: 3px 8px;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: rgba(0, 255, 136, 0.12);
|
|
|
|
|
border: 1px solid var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mode-badge.synth {
|
|
|
|
|
background: rgba(255, 107, 53, 0.12);
|
|
|
|
|
border-color: var(--synth-accent);
|
|
|
|
|
color: var(--synth-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ==================== Side Panel ==================== */
|
|
|
|
|
.side-panel-toggle {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
z-index: 50;
|
|
|
|
|
background: var(--bg-surface);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
border-left: none;
|
|
|
|
|
border-radius: 0 8px 8px 0;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
padding: 12px 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
transition: background 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-toggle:hover {
|
|
|
|
|
background: var(--bg-elevated);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel {
|
|
|
|
|
position: fixed;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 260px;
|
|
|
|
|
background: var(--bg-surface);
|
|
|
|
|
border-right: 1px solid #333;
|
|
|
|
|
z-index: 60;
|
|
|
|
|
transform: translateX(-100%);
|
|
|
|
|
transition: transform 0.25s ease;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel.open {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-backdrop {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
z-index: 55;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 14px 16px 10px;
|
|
|
|
|
border-bottom: 1px solid #222;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-title {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-close {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.side-panel-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
border-bottom: 1px solid #222;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-tab {
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
background: var(--bg-elevated);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-tab:first-child {
|
|
|
|
|
border-radius: 6px 0 0 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-tab:last-child {
|
|
|
|
|
border-radius: 0 6px 6px 0;
|
|
|
|
|
border-left: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-tab.active[data-mode="visual"] {
|
|
|
|
|
background: rgba(0, 255, 136, 0.12);
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-tab.active[data-mode="synth"] {
|
|
|
|
|
background: rgba(255, 107, 53, 0.12);
|
|
|
|
|
border-color: var(--synth-accent);
|
|
|
|
|
color: var(--synth-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sp-hint {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Synth controls */
|
|
|
|
|
.synth-controls {
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-label {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-label span {
|
|
|
|
|
color: var(--synth-accent);
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-slider {
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
appearance: none;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 6px;
|
|
|
|
|
background: #333;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-slider::-webkit-slider-thumb {
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
appearance: none;
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
background: var(--synth-accent);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-slider::-moz-range-thumb {
|
|
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
background: var(--synth-accent);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-select {
|
|
|
|
|
background: var(--bg-elevated);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-start-btn {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-arp-btn {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-arp-btn.playing {
|
|
|
|
|
background: rgba(255, 107, 53, 0.15);
|
|
|
|
|
border-color: var(--synth-accent);
|
|
|
|
|
color: var(--synth-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.synth-status {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
/* Visual canvas */
|
|
|
|
|
.visual-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 09:47:47 +01:00
|
|
|
.expand-visual-btn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
right: 8px;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
background: rgba(26, 26, 26, 0.82);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: #9f9f9f;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
backdrop-filter: blur(4px);
|
|
|
|
|
-webkit-backdrop-filter: blur(4px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.expand-visual-btn:active,
|
|
|
|
|
.expand-visual-btn[aria-pressed="true"] {
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
background: rgba(0, 255, 136, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
.visual-container canvas {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Preset pills */
|
|
|
|
|
.presets {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
left: 8px;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preset-pill {
|
|
|
|
|
background: rgba(26, 26, 26, 0.8);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
backdrop-filter: blur(4px);
|
|
|
|
|
-webkit-backdrop-filter: blur(4px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.preset-pill:active {
|
|
|
|
|
background: rgba(0, 255, 136, 0.15);
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parameter display */
|
|
|
|
|
.param-container {
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
background: var(--bg-surface);
|
|
|
|
|
border-top: 1px solid #222;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
height: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-label {
|
|
|
|
|
width: 42px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): expand C15 param map from 20 to 126 synth parameters
Control all sonically meaningful C15 parameters through NISPS ML outputs.
Excludes hardware routing, macros, scale/tuning, key tracking, velocity,
envelope mod depths, discrete switches, and dangerous volume/pitch params.
- Expand param-map.js with 126 curated params grouped by synthesis section
- Widen MLP architecture from [3,10,10,14,20] to [3,32,48,64,126]
- Add ParamDisplay.rebuild() for mode-dependent bar count (20 visual, 126 synth)
- Add scrollable compact layout for synth mode param bars
- Pad visual presets and old saves to 126 outputs for backward compatibility
2026-03-21 21:58:48 +01:00
|
|
|
.param-container.synth-mode {
|
|
|
|
|
max-height: 50vh;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
scrollbar-width: thin;
|
|
|
|
|
scrollbar-color: #444 #1a1a1a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-container.synth-mode .param-row {
|
|
|
|
|
height: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
.param-container.synth-mode .param-label {
|
feat(playground): expand C15 param map from 20 to 126 synth parameters
Control all sonically meaningful C15 parameters through NISPS ML outputs.
Excludes hardware routing, macros, scale/tuning, key tracking, velocity,
envelope mod depths, discrete switches, and dangerous volume/pitch params.
- Expand param-map.js with 126 curated params grouped by synthesis section
- Widen MLP architecture from [3,10,10,14,20] to [3,32,48,64,126]
- Add ParamDisplay.rebuild() for mode-dependent bar count (20 visual, 126 synth)
- Add scrollable compact layout for synth mode param bars
- Pad visual presets and old saves to 126 outputs for backward compatibility
2026-03-21 21:58:48 +01:00
|
|
|
width: 82px;
|
|
|
|
|
font-size: 9px;
|
feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator
Adds a secondary Synth mode (toggled via collapsible left side panel)
where the NISPS ML engine's 20 outputs control curated C15 synthesizer
parameters (oscillator PM, shapers, filters, reverb, echo, flanger,
output mixer) through a WASM AudioWorklet bridge.
Includes a chord progression arpeggiator with controls for tempo (BPM),
octave range, octave offset, and 4 selectable progressions. The C15
engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring
buffer communication.
New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map),
serve-coop.py (COOP/COEP headers for SharedArrayBuffer support).
2026-03-21 21:26:37 +01:00
|
|
|
color: #886644;
|
|
|
|
|
}
|
|
|
|
|
|
feat(playground): expand C15 param map from 20 to 126 synth parameters
Control all sonically meaningful C15 parameters through NISPS ML outputs.
Excludes hardware routing, macros, scale/tuning, key tracking, velocity,
envelope mod depths, discrete switches, and dangerous volume/pitch params.
- Expand param-map.js with 126 curated params grouped by synthesis section
- Widen MLP architecture from [3,10,10,14,20] to [3,32,48,64,126]
- Add ParamDisplay.rebuild() for mode-dependent bar count (20 visual, 126 synth)
- Add scrollable compact layout for synth mode param bars
- Pad visual presets and old saves to 126 outputs for backward compatibility
2026-03-21 21:58:48 +01:00
|
|
|
.param-container.synth-mode .param-track {
|
|
|
|
|
height: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-container.synth-mode .param-value {
|
|
|
|
|
font-size: 8px;
|
|
|
|
|
min-width: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-container.synth-mode.draggable .param-track {
|
|
|
|
|
height: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
.param-track {
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 8px;
|
|
|
|
|
background: #222;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.draggable .param-track {
|
|
|
|
|
cursor: ew-resize;
|
|
|
|
|
height: 14px;
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.draggable .param-track:active {
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-fill {
|
|
|
|
|
height: 100%;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
transition: width 0.05s ease-out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-value {
|
|
|
|
|
width: 32px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
text-align: right;
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Controls area */
|
|
|
|
|
.controls-area {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
background: var(--bg-surface);
|
|
|
|
|
border-top: 1px solid #222;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 09:47:47 +01:00
|
|
|
.app.expanded {
|
|
|
|
|
grid-template-rows: auto minmax(0, 1fr) 58px 102px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .header {
|
|
|
|
|
padding-top: 6px;
|
|
|
|
|
padding-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .header h1 {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: #9a9a9a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .presets {
|
|
|
|
|
top: 6px;
|
|
|
|
|
left: 6px;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .preset-pill {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
padding: 3px 7px;
|
|
|
|
|
color: #868686;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .param-container {
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
opacity: 0.78;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .param-row {
|
|
|
|
|
height: 17px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .param-track {
|
|
|
|
|
height: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .draggable .param-track {
|
|
|
|
|
height: 9px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .param-label,
|
|
|
|
|
.app.expanded .param-value {
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .controls-area {
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
opacity: 0.82;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded #joystick-container {
|
|
|
|
|
width: 94px;
|
|
|
|
|
height: 94px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded #joystick-container canvas {
|
|
|
|
|
transform: scale(0.58);
|
|
|
|
|
transform-origin: top left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .controls-mode-toggle,
|
|
|
|
|
.app.expanded .controls-actions {
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .btn,
|
|
|
|
|
.app.expanded .mode-btn {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .btn-good,
|
|
|
|
|
.app.expanded .btn-bad {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
padding: 5px 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app.expanded .controls-status,
|
|
|
|
|
.app.expanded .metrics-grid,
|
|
|
|
|
.app.expanded .loss-plot-wrap {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 13:17:17 +01:00
|
|
|
#joystick-container {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#controls-container {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.controls-mode-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mode-btn {
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: 6px 0;
|
|
|
|
|
background: var(--bg-elevated);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mode-btn.active {
|
|
|
|
|
background: rgba(0, 255, 136, 0.1);
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.controls-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
padding: 8px 14px;
|
|
|
|
|
background: var(--bg-elevated);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.1s;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn:active {
|
|
|
|
|
transform: scale(0.96);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
background: rgba(0, 255, 136, 0.12);
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-primary:active {
|
|
|
|
|
background: rgba(0, 255, 136, 0.25);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-danger {
|
|
|
|
|
color: var(--danger);
|
|
|
|
|
border-color: #552233;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-danger:active {
|
|
|
|
|
background: rgba(255, 51, 102, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-good {
|
|
|
|
|
background: rgba(0, 255, 136, 0.12);
|
|
|
|
|
border-color: var(--accent-dim);
|
|
|
|
|
color: var(--good);
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
padding: 8px 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-good:active {
|
|
|
|
|
background: rgba(0, 255, 136, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-bad {
|
|
|
|
|
background: rgba(255, 102, 68, 0.1);
|
|
|
|
|
border-color: #553322;
|
|
|
|
|
color: var(--bad);
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
padding: 8px 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-bad:active {
|
|
|
|
|
background: rgba(255, 102, 68, 0.25);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flash {
|
|
|
|
|
animation: flash-anim 0.2s ease-out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes flash-anim {
|
|
|
|
|
0% { box-shadow: 0 0 12px var(--accent); }
|
|
|
|
|
100% { box-shadow: none; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.controls-status {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
font-family: monospace;
|
2026-02-11 16:56:24 +01:00
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.metrics-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: auto 1fr;
|
|
|
|
|
gap: 3px 10px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.metric-label {
|
|
|
|
|
color: #707070;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.metric-value {
|
|
|
|
|
color: #b5b5b5;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.loss-plot-wrap {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border: 1px solid #2a2a2a;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#loss-plot {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 86px;
|
|
|
|
|
display: block;
|
2026-02-11 13:17:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hidden { display: none !important; }
|
|
|
|
|
|
|
|
|
|
/* Help overlay */
|
|
|
|
|
.help-overlay {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(0, 0, 0, 0.85);
|
|
|
|
|
z-index: 100;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
backdrop-filter: blur(8px);
|
|
|
|
|
-webkit-backdrop-filter: blur(8px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.help-content {
|
|
|
|
|
background: var(--bg-surface);
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
max-width: 360px;
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.help-content h2 {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.help-content h3 {
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
margin-top: 14px;
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.help-content p, .help-content li {
|
|
|
|
|
color: var(--text-dim);
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.help-content ol {
|
|
|
|
|
padding-left: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Responsive: wider screens (foldable inner, tablet) */
|
|
|
|
|
@media (min-width: 500px) {
|
|
|
|
|
.app {
|
|
|
|
|
max-width: 800px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.controls-area {
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.param-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
}
|
|
|
|
|
}
|