Commit graph

143 commits

Author SHA1 Message Date
w1n5t0n
0246ca58ab fix(playground): restore play drawer and preset button fixes
Re-apply fixes from ddb6b77 that were lost in the arpeggiator restore:
- play-btn: align-self: flex-start (pin to top)
- Play drawer: trigger on .play-btn:hover instead of parent :hover
  (prevents preset select from opening the drawer)
- preset-select: align-self: flex-start (pin to top)
2026-03-24 12:01:40 +02:00
w1n5t0n
d0bc34d8c9 refactor(shapeseq): restore arpeggiator, gate ShapeSeq behind ?shapeseq=1
Restore the original arpeggiator as the default sequencer. ShapeSeq
code is preserved but gated behind a URL feature flag (?shapeseq=1):

- Arpeggiator import, state, and all wiring fully restored
- ShapeSeq modules loaded via dynamic import() only when flag is on
- ensureShapeSeqInit() creates engine/viz/UI lazily on first audio start
- animate loop routes inputs to ShapeSeq only when flag + playing
- setOutputMode shows/hides ShapeSeq container only when flag is on
- HTML: ShapeSeq container added but hidden by default
- CSS: ShapeSeq styles added (inert when container is hidden)

All 13 ShapeSeq modules in playground/js/shapeseq/ are untouched.
To test ShapeSeq: add ?shapeseq=1 to the URL.
2026-03-24 11:54:13 +02:00
w1n5t0n
d40711eb77 fix(shapeseq): fix 6 bugs found in fresh-eyes review
1. Pitch pipeline: IntervalLock stores midiNote/127, sequencer now
   converts back via pitch*127. Removed pitch RangeMap from projection
   presets (was double-mapping already-quantized values).

2. Clock step duration: changed from quarter-note grid (60/bpm) to
   16th-note grid (60/bpm/4). 8 steps at 120 BPM now = 1 second.

3. Method name: drawWeights → randomiseWeights (matching WasmIML API).

4. Dirty-check: setSequenceInputs now skips re-evaluation when inputs
   haven't changed (epsilon 1e-5). Avoids 60fps chain evaluation.

5. SwingGroove: swingGrid param now functional — selects 8th note
   (period 2), 16th note (period 4), or triplet (period 3) swing grid.

6. IntervalLock base octave: starts from C3 (MIDI 48) instead of C0,
   so default output is in a playable range.
2026-03-24 11:42:25 +02:00
w1n5t0n
505ed26f21 feat(shapeseq): integrate ShapeSeq into synth mode, replace arpeggiator
- Remove Arpeggiator import and all arp references from a-app.js
- Add ShapeSeq imports (ShapeSeqEngine, StepVisualizer, ChainBuilderUI,
  getDefaultBus) and lazy initialization on first audio start
- In synth mode: show ShapeSeq container (circular viz + chain builder),
  hide flow-field particles. Visual mode unchanged.
- Route joystick or hand tracking inputs to shapeSeq.setSequenceInputs()
  each frame; render step viz at 60fps
- Add HTML: #shapeseq-container with #shapeseq-viz canvas and
  #shapeseq-chain wrapper, sequencer transport controls (play/stop,
  tempo, step count)
- Add CSS: full-screen flex layout, viz takes upper 45%, chain builder
  scrollable below, hidden by default
- Quick play button wires to ShapeSeq start/stop instead of arp

Closes meml-6ry. This completes ShapeSeq Phase 1.
2026-03-24 02:17:32 +02:00
w1n5t0n
027510a7e4 feat(shapeseq): implement Layer 3 — chain builder UI and sequencer core
chain-ui.js (1002 lines): Vertical stack chain builder (pedalboard-
style) with expandable primitive cards, category-colored borders,
drag-to-reorder via pointer events, add primitive palette modal,
NISPS live-param pulsing indicator, generator combine mode toggle.
All styles inline matching dark glass theme.

sequencer.js (304 lines): ShapeSeqEngine orchestrator wiring the
full pipeline: input routing → sequence MLP (16 outputs) → param
mapping → chain evaluation → projection → clock scheduling → C15
bridge. Default chain: Euclidean→ProbGate→PitchWalker→IntervalLock→
VelocityShaper. Tracks active notes to prevent orphaned noteOns.
2026-03-24 02:10:04 +02:00
w1n5t0n
efcf6d2c06 feat(shapeseq): implement Layer 2 — all 8 primitives and chain runner
primitives.js (505 lines): All 8 sequencing primitives extending
Primitive base class:
- EuclideanRhythm: Bjorklund-distributed trigger patterns
- ProbabilityGate: PRNG-based trigger filtering + accent assignment
- PitchWalker: stateful constrained random walk with gravity
- Ratchet: probabilistic step subdivision (1-4x)
- SwingGroove: alternating-step timing offsets (max triplet feel)
- DensityMorph: trigger placement with clustering control
- IntervalLock: 11-scale pitch quantizer (chromatic→diminished)
- VelocityShaper: 5 curve types with depth/phase control
Includes PRIMITIVE_REGISTRY for chain builder UI.

chain.js (319 lines): Sequential pipeline chain evaluator.
Buckets primitives by category (generators→processors→converters→
timing), forks deterministic PRNG per primitive, merges multiple
generators via additive (OR) or multiplicative (AND) mode
(configurable in real time). Flat param distribution by chain
position. Full state serialization for freeze support.
2026-03-24 02:06:17 +02:00
w1n5t0n
c8015da35a feat(shapeseq): implement Layer 1 modules
Four modules that build on the Layer 0 foundation:

- primitive.js: base class with category system (generator/processor/
  timing/converter), param schema validation, boundary enforcement
  (clamp/wrap/scaled), symbolic process() interface, state management
  for freeze, and applyBoundary() utility for delta control
- clock.js: AudioContext lookahead scheduling (25ms interval, 100ms
  window) replacing setTimeout arpeggiator. Reads pattern descriptions
  for per-step swing (timeOffset) and ratchet (subdivisions). Emits
  seq.noteOn/noteOff/step/loopStart via event bus.
- projection.js: composable transform chain with 5 transforms
  (VelocityCurve, GateThreshold, RangeMap, OctaveFolder, StutterMap)
  and 3 presets (expressive, percussive, fullRange). Pitch quantization
  deliberately excluded (handled by Interval Lock primitive).
- step-viz.js: Canvas2D circular step visualizer with even angular
  spacing for any step count. Pitch→radius, velocity→node size,
  accent→color. 60fps-friendly with pre-allocated coordinate buffers.
  Tap interaction for step toggling.
2026-03-24 02:02:01 +02:00
w1n5t0n
e3b94d644f feat(shapeseq): implement Layer 0 foundation modules
Five independent modules with no interdependencies:

- event-bus.js: namespaced pub/sub (seq.*/ml.*/ui.*) with wildcard
  subscriptions and automatic AudioContext/performance.now timestamps
- prng.js: seedable mulberry32 PRNG with pure-functional API, fork()
  for independent per-primitive streams, serializable state for freeze
- pattern.js: symbolic pattern description data structure with
  create/clone/merge/validate, additive and multiplicative merge modes
- param-map.js: maps fixed 16-output MLP to variable-count primitive
  params via linear interpolation, with optional per-param min/max scaling
- seq-iml.js: factory for second WasmIML instance (2 inputs, 16 outputs,
  [16,16,16] hidden layers) following existing imlJoy/imlHand pattern

All modules are ES modules with no build step. PRNG, pattern, and
param-map are port-ready (explicit state, typed arrays, no closures).
2026-03-24 01:57:59 +02:00
w1n5t0n
ddb6b77abb fix(playground): prevent play drawer from triggering on preset hover, pin controls to top
- Change drawer hover trigger from .synth-quick-controls:hover to
  .play-btn:hover ~ so hovering preset selector no longer opens it
- Add .play-drawer:hover to keep drawer open when mouse moves to it
- Set align-self: flex-start on play-btn and preset-select to prevent
  vertical layout shift when drawer appears
2026-03-24 01:54:09 +02:00
w1n5t0n
2fb00f20a9 fix(playground): move arpeggiator to Worker thread, throttle synth params
Hand tracking at 30fps was writing 126 params per frame to the C15 ring
buffer (capacity 512), flooding it and starving arpeggiator noteOn/noteOff
messages — causing stuck/dropped notes.

Three-part fix:
- Arpeggiator now runs in a dedicated Web Worker with direct
  SharedArrayBuffer access, bypassing the main thread entirely for
  note timing. Worker setInterval isn't subject to main thread jank.
- RingBufferWriter uses CAS (Atomics.compareExchange) for multi-producer
  safety — both main thread (params) and worker (notes) write safely.
- routeOutputs throttles synth parameter sends: dead-zone filter
  (0.2% change threshold) + rate cap (~20fps), reducing ring buffer
  pressure from ~3800 msg/s to ~50-100 msg/s of actual changes.
2026-03-24 01:42:37 +02:00
w1n5t0n
4fd0b39d26 docs(playground): revise ShapeSeq spec after fresh-eyes review
Major revisions addressing architectural gaps:
- Fix data flow: clock drives everything (top of diagram)
- Add fixed MLP + param mapping layer to handle dynamic chain sizing
- Introduce symbolic chain evaluation (pattern descriptions, not concrete values)
- Categorize primitives: generator, processor, timing modifier, converter
- Generator combination modes (additive/multiplicative, user-configurable)
- Swing and Ratchet are now timing modifiers on the pattern description
- Remove Scale Quantizer from projection layer (Interval Lock is the sole quantizer)
- Clarify mode integration: ShapeSeq replaces particle viz in synth mode
- Build on existing imlJoy/imlHand dual-instance pattern
- Add Open Design Questions section for deferred decisions
- Port-ready applies to primitive layer only, not orchestration
2026-03-24 00:30:43 +02:00
w1n5t0n
1a0f2c4d1c docs(playground): add ShapeSeq generative sequencing system spec
Comprehensive spec for ShapeSeq — a NISPS-driven generative sequencing
system that replaces the placeholder arpeggiator. Covers 8 composable
algorithmic primitives, configurable chain modes, delta control with
freeze/re-expose workflow, namespaced event bus, AudioContext-based
precise clock, circular step visualization, and phased implementation
plan.
2026-03-23 23:42:34 +02:00
w1n5t0n
5a4924728f feat(playground): add MediaPipe hand tracking input with dev panel
Hand tracking via webcam as alternative to joystick input:
- Right hand tracks 14 derived features (palm XY, finger curls, spread,
  roll, pitch, pinch) through a separate 14-input MLP
- Left hand gesture recognition (1 finger = thumbs up, 2 = thumbs down)
  with 400ms debounce hold
- Split-zone PIP display (no camera feed, skeleton only) with dashed
  divider and cross-zone dimming
- Dual IML architecture: independent imlJoy (2 inputs) and imlHand
  (14 inputs) with pointer swap, preserving training data per mode
- Dev panel (?devmode=true): draggable/collapsible floating panel with
  sliders for MediaPipe confidence thresholds, smoothing, gesture hold
  time, world landmarks toggle, and live feature bar monitor
- Visual presets always route to joystick IML (prevents dimension mismatch)
- Race condition guard on input mode switching
2026-03-23 23:28:58 +02:00
w1n5t0n
f8983c4806 feat(playground): replace JS ML engine with WASM nisps-core
Compile nisps-core C++ MLP to WASM (36KB) and use it as the ML engine
in the playground, replacing the JavaScript port for inference, training,
and weight manipulation.

- Add extern "C" WASM bindings with spread-aware drawWeights/moveWeights
- WasmIML class is a drop-in replacement for the JS IML
- Inference runs on main thread via WASM (fast, synchronous)
- Training runs in a Web Worker with its own WASM instance (non-blocking)
- Interactive training (thumbs-up, train button) no longer freezes UI/audio
- Preset loading and state restore still use sync training
2026-03-23 23:23:07 +02:00
w1n5t0n
8a550361ea feat(playground): add tiered synth parameter preset system
4 tiers of progressive complexity (Beginner 15 params → Expert 126),
13 presets total with per-param min/max/curve overrides that bias
distributions without clamping extremes. Preset dropdown in UI,
persisted to localStorage, supports ?preset= URL param.
2026-03-23 00:50:56 +02:00
w1n5t0n
4e32919494 fix(playground): correct SYNTH_SECTIONS alignment and bidirectional slider sync
SVF count was 7 (actual 9), Gap Filter section was missing (6 params),
FB Mix count was 10 (actual 9) — causing cascading mislabeling of all
synth sections from SVF onwards. Also sync volume/tempo changes from
bottom sheet back to quick-play controls.
2026-03-22 23:59:08 +02:00
w1n5t0n
baa5d5016d docs(playground): add synth controls and MIDI/arpeggiator info to help modal 2026-03-22 22:47:55 +02:00
w1n5t0n
33580f0ae0 feat(playground): add help/intro modal shown on first visit
- Glass-style modal explains NISPS, how learning works, and what to expect
- Documents all controls: touch, keyboard, and gamepad bindings
- Shows automatically on first visit (localStorage flag)
- Dismissible via close button, "Got it", overlay click, or Escape
- Reopenable via ? button at bottom right
2026-03-22 22:41:49 +02:00
w1n5t0n
de75a3f0e4 feat(playground): expand gamepad support with face buttons and Steam Deck fixes
- Map A=Train, B=Clear Examples, X=Randomize, LB=Thumbs Down, RB=Thumbs Up
- Add periodic gamepad polling fallback for environments where
  gamepadconnected event doesn't fire reliably (Steam Deck, some Linux browsers)
- Show "Press any gamepad button to connect" hint on page load
2026-03-22 22:40:15 +02:00
w1n5t0n
affec8f604 feat(playground): add synth parameter tooltips, group drawer with per-param curve/mute controls
- Canvas tooltip follows mouse over synth bars showing param name, value, range, curve
- Group drawer: per-param draggable curve canvas, dual-thumb min/max range slider, mute toggle
- Muted params hide from visualizer (bars redistribute), use fixed value slider instead
- Group curve drag applies relative delta preserving individual param offsets
- Pulsing orange play button when audio engine not initialized (all UI modes)
2026-03-22 21:57:21 +02:00
w1n5t0n
21832e0099 fix(playground): prevent 1/2 keys being swallowed by focused sliders
When synth controls (volume, tempo, etc.) have focus after dragging,
keyboard shortcuts 1/2 for thumbs down/up were intercepted by the
range input. Now all four UIs skip shortcut handling when an input,
select, or textarea element has focus.
2026-03-22 11:52:30 +02:00
w1n5t0n
a0adb8b14b fix(playground): move Visual/Synth toggle into bottom toolbar in immersive UI
Relocated the output mode toggle from a floating top-right position into
the bottom floating bar where it's more discoverable. Removed the
duplicate toggle from the expanded sheet. Compact pill-toggle-sm style
fits the toolbar layout.
2026-03-22 11:07:38 +02:00
w1n5t0n
295221d1b2 feat(playground): unify learning modes, add MIDI input and gamepad support
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).
2026-03-22 11:01:50 +02:00
w1n5t0n
d3797945d2 tweak(playground): simplify descriptions in card body text 2026-03-22 02:22:47 +02:00
w1n5t0n
6b6881da7d fix(playground): fix RL button position on desktop and add floating output toggle
- Fix RL buttons stuck high on desktop: media query was overriding
  bottom to 196px, now matches mobile 92px
- Add floating Visual/Synth toggle at top-right of immersive view,
  synced with the existing sheet toggle
2026-03-22 02:11:31 +02:00
w1n5t0n
3db90b035a feat(playground): add spread param for sigmoid saturation control and fix randomise sync
Add ?spread=0-1 URL param that controls weight initialization scaling,
RL noise scaling per layer, noise cap, and weight decay to prevent
sigmoid output saturation. At spread=0 (original behavior) weights are
uniform [-1,1] and outputs polarise near 0/1. At spread=1 weights use
Xavier scaling (1/sqrt(fan_in)), noise is proportionally reduced, and
10% weight decay per thumbs-down prevents unbounded magnitude drift.

Also fix randomise to re-inject current joystick position and re-run
inference before routing outputs, eliminating the jump on first
joystick move after randomise.

Defaults: tame=1, spread=0.6 across all app variants.
2026-03-22 02:10:26 +02:00
w1n5t0n
d9be808aaa style(playground): unify dark theme with orange accents and improve RL button layout
- Replace fragmented green/blue/orange accent scheme with unified hacker
  orange (#ff6a00) across all CSS, JS canvas drawing, and param palettes
- Redesign designs.html: monospace font, clickable cards, grid background,
  remove A/B/C labels; copy to index.html as landing page
- Add JetBrains Mono font across all views for consistent techy aesthetic
- Sharpen border-radius (pills 20px→6px, back buttons→6px) for angular feel
- Restructure RL buttons: key numbers inside buttons (larger, white),
  teal-green for thumbs-up, dark red-orange for thumbs-down
- Fix RL button z-index (25→45) so they render above the bottom sheet
- Add Xavier-scaled weight randomization (spread parameter)
2026-03-22 01:36:00 +02:00
w1n5t0n
b80a9e4e86 fix(playground): resize canvas when switching output modes in Design B (meml-2c3)
When switching from synth back to visual mode, the FlowFieldVisualizer
canvas had stale dimensions from being hidden. Add requestAnimationFrame
resize call after toggling canvas visibility so the visualizer picks up
correct dimensions and renders properly.
2026-03-22 01:07:52 +02:00
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
w1n5t0n
925ddc5be6 feat(playground): add volume safety limiter, devmode tame parameter, and voice buildup mitigation
- Insert DynamicsCompressorNode as always-on brick-wall limiter after
  master gain (threshold -6dB, ratio 20:1, 2ms attack)
- Add devmode tame parameter (0-1) controlling range constraining:
  - ?tame=0.7 URL param (default) or window.setTameLevel(n)
  - 0 = no mitigation, 1 = strongest constraining
- Constrain 20 volume/buildup-sensitive params with safeMin/safeMax:
  - Envelope release/decay2 times capped to prevent infinite ringing
  - Envelope gains kept in moderate range
  - All 5 drive stages (ShpA/B, FB, Out, Cabinet) capped
  - Output mixer levels given floor and ceiling
  - Echo feedback and reverb size limited to prevent wash buildup
- Fix Comb Filter group count (8, not 9) in comments and color generator
2026-03-22 00:15:11 +02:00
w1n5t0n
c3577be42c docs: update CLAUDE.md and playground README for 126-param C15 map
Document the expanded parameter map, MLP architecture, exclusion
rationale table, and synth mode in both project-level and playground docs.
2026-03-21 23:11:45 +02:00
w1n5t0n
24e3e9665e 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 22:58:48 +02:00
w1n5t0n
6f6a334529 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 22:26:37 +02:00
w1n5t0n
843f80c54a chore: add playground serve script 2026-03-04 20:47:06 +00:00
w1n5t0n
2c2c9f769d playground: add expandable visual mode and docs 2026-02-14 08:47:47 +00:00
w1n5t0n
3039221165 playground: add loss history tracking and diagnostics panel 2026-02-11 15:56:24 +00:00
w1n5t0n
96a88f4b0f playground: add follow-mode joystick and RL hotkeys 2026-02-11 15:56:19 +00:00
w1n5t0n
6187a4b4ae playground: add orbiting repulsor field controls 2026-02-11 15:50:06 +00:00
w1n5t0n
0498bbe371 playground: add inertia and drag velocity memory 2026-02-11 15:48:34 +00:00
w1n5t0n
06833cdd19 playground: add blended advection modes 2026-02-11 15:47:13 +00:00
w1n5t0n
97acfd2824 playground: add particle lifetime and respawn modes 2026-02-11 15:45:50 +00:00
w1n5t0n
0550088516 playground: add attractor and dispersion controls 2026-02-11 15:44:33 +00:00
monkey-w1n5t0n
57aae34870 feat: add web-based interactive playground for NISPS
Faithful JS port of nisps-core MLP + IML engine with a touch-friendly
UI for exploring neural parameter mapping visually. Two learning modes:
example-based (set slider targets) and RL feedback (thumbs up/down with
exploration noise). Flow field particle system controlled by 8 MLP outputs.
2026-02-11 13:17:17 +01:00