memlnaut-nisps/vcv/README.md
monkey-w1n5t0n 9ad1f78ddd docs: the specs disposition pass (plan §8)
Roughly 20k lines were deleted from this repo in the last week and much of the
corpus still described the pre-deletion world in the present tense. Executes
the §8 table: archive the retired, reclassify the executed, prune the stale.

  aimmersive-clone-spec      -> _archive/ with a deprecated-by note
  feedback-modes-port-spec   -> plans/, kind: plan, status: executed
  manifold-parity-features   -> plans/, kind: plan, status: active
  playground-2.0-rewrite     -> status: superseded
  engine-architecture        434 -> ~120 lines; seam + spine kept, rewritten
                             present-tense against the shipped engine/
  MAIN.md                    six contradicted claims fixed; registry resynced
  vcv-module.md              pruned to the current 8->16 contract and made the
                             single .nisps format spec
  vcv/NISPS-FORMAT.md        DELETED — documented a v1 format that no longer loads
  vcv/README.md, BUILDING.md rewritten to the real contract, menu, OSC table
  inputs/backends/dock trio  grounding sections marked historical, dead cites fixed

Two rows of the §8 table were themselves wrong, corrected here: the deleted
full-state sync lives in backends-spec.md §6.3, not vcv-module.md (which has no
§6.3), and codegen/README.md was already a MAP pointer with no port-solidjs
trigger left to remove.

Beyond the table — found by sweeping every backticked path in the changed docs
against `git ls-files`, which is how these should have been caught before:

  manifold/ONBOARDING.md documented a UI that Phase 1 deleted, as if current:
  SplitStage, ReadoutStrip, InputMini, BackendAdvanced, AltitudeNav, and a
  shot.spec.ts that does not exist. The whole stage table was keyed on a `focus`
  axis that no longer exists — selection is now sandwich > particles >
  composite. This matters more than the rest: CLAUDE.md tells every agent to
  read ONBOARDING.md first for Manifold work, so it was actively teaching a
  fiction. Rewritten against ConsoleApp.tsx.

  MAP.md claimed the input layer reduces axes to the engine arity with an
  "even/odd blend". input-layer.ts says the opposite in its own header: one
  dedicated slot per axis, 1:1, into a 32-input over-provisioned head, and
  mean-blending was removed deliberately because it diluted every source.

  AGENT-REFERENCE.md still promised TS emission "returns at P5" (landed),
  per-mode dims "become schema-real at P5" (landed at P5.3), and pointed at
  nisps::FixedBuffer (deleted).

Doc-right/code-suspect, filed rather than fixed: VCV computes derivedMean/Std/
Delta and cachedNovelty behind a live context-menu toggle that nothing reads;
vcv/plugin.json points at the MusicallyEmbodiedML org rather than this repo's
origin; and the module defaults to UDP 7001+id%64 while bridge.ts defaults to
9000, so out of the box they do not meet.

Firmware-build docs are deliberately untouched — the PlatformIO migration
lands next and rewrites all of them.
2026-07-21 20:17:58 +02:00

10 KiB

MEMLNaut for VCV Rack

MEMLNaut is a CV-to-CV mapper powered by a neural network that you train in real time using reinforcement learning. Patch any CV sources into its 8 inputs, connect its 16 outputs to your synth parameters, and shape the mapping by giving thumbs-up/thumbs-down feedback. The module learns your preferences, producing complex, nonlinear modulation that evolves with your taste. It does not generate sound itself -- it generates control voltages.

Installation

MEMLNaut is built from source against the VCV Rack 2 SDK. Pre-built .vcvplugin bundles are published at https://meml.lnfinitemonkeys.org/next/vcv/ (locally-built bundles land in the git-ignored dist/; see DISTRIBUTION.md).

Requirements

  • VCV Rack 2 (Community Edition or Pro)
  • VCV Rack SDK (v2.x)
  • C++20 compiler (GCC 10+, Clang 12+, or MSVC 2019+)
  • GNU Make

Build

git clone --recursive https://github.com/monkey-w1n5t0n/MEMLNaut-NISPS.git
cd MEMLNaut-NISPS/vcv

# Point to your Rack SDK (or set in environment)
export RACK_DIR=/path/to/Rack-SDK

make
make install   # copies plugin to your VCV Rack plugins directory

The default RACK_DIR is ~/.local/share/Rack2/Rack-SDK. If your SDK lives there, you can skip the export.

The ML engine is the repository's shared nisps/ C++20 core, reached via relative includes from src/iml.hpp -- the plugin must be built from a full repo checkout. See BUILDING.md.

Quick Start

  1. Add MEMLNaut from the module browser.
  2. Patch LFOs (or any CV source) into one or more of the 8 IN jacks.
  3. Connect several of the 16 outputs to parameters on your synth voice -- filter cutoff, oscillator pitch, waveshape, VCA level, etc.
  4. Press RAND to randomize the network. You should hear your synth respond as the LFOs sweep.
  5. Flip the LEARN switch on.
  6. When you hear something you like, press + (thumbs up). When you hear something you dislike, press - (thumbs down).
  7. Keep exploring. The mapping will converge toward sounds you prefer.

Panel Controls

Knobs

Control Description
SPREAD Controls weight initialization scale, RL noise amplitude, and weight decay. Low values produce extreme, polarized mappings. High values produce balanced, subtle mappings. Default: 60%.
RATE Inference rate. Full CCW = ~170 Hz (block rate, cheapest). Noon = ~2.8 kHz (good for CV). Full CW = 44.1 kHz (audio rate, most expensive).

Buttons

Control Description
+ Thumbs up. Captures the current input/output pair as a training example and trains the network. Requires LEARN to be enabled.
- Thumbs down. Increases exploration noise and perturbs the network weights to try something different. Requires LEARN to be enabled.
LEARN Toggle switch. Enables/disables RL feedback. When off, the module still runs inference -- it just ignores +/- presses.
RAND Randomize all network weights (using current SPREAD setting).
CLEAR Long-press (~1 second) to clear all training examples and reset the network.

Inputs

Port Description
IN 1-8 CV inputs feeding the 8-input network. Default range: 0-10V (unipolar); per-input bipolar toggle in the context menu.
SPREAD CV CV modulation of the SPREAD knob (added to knob value, 0-10V).
LEARN Gate input. High = enable learning. Works alongside the LEARN toggle (either enables it).
+ TRIG Trigger input for thumbs-up. Alternative to pressing the + button.
- TRIG Trigger input for thumbs-down. Alternative to pressing the - button.

Outputs

Port Description
OUT 1-16 Raw MLP outputs. Default: 0-10V unipolar (per-output bipolar toggle in the context menu).

Each output has a trimpot attenuverter (-100% to +100%) for scaling and inverting individual outputs without external modules, and is surrounded by an LED ring whose arc fill tracks the output's current level (color-coded per output, orange-to-cyan ramp).

LEDs & Display

Indicator Meaning
LEARN (green) Lit when learning is enabled.
TRAIN (yellow) Flashes during background training.
Output LED rings Arc fill tracks each output's current level.

The built-in display shows all 16 output levels as color-coded bars, the current noise level (N:), the example count (e.g. 42/100), and "TRAIN" during active training.

RL Workflow

The reinforcement learning loop works like this:

  1. Start exploring. Patch LFOs or sequencers into the IN jacks. Connect outputs to interesting synth parameters. Press RAND a few times to hear different random mappings.

  2. Enable learning. Flip the LEARN switch on (or send a gate to the LEARN input).

  3. Thumbs up (+) when you like what you hear. This:

    • Saves the current input position and output values as a training example
    • Trains the network to reproduce this mapping
    • Slightly reduces exploration noise (the network becomes more "settled")
  4. Thumbs down (-) when you dislike what you hear. This:

    • Increases exploration noise
    • Perturbs the network weights to try a different mapping
    • Does NOT save any training example
  5. Repeat. Over time, the network learns to produce outputs you tend to like across the input space. Regions near your thumbs-up examples will be stable; distant regions remain exploratory.

  6. Disable learning when you are happy with the mapping. The module continues running inference with the trained network. You now have a complex, personalized CV source.

Tips

  • Give thumbs-up at several different input positions to teach the network about different regions of the input space.
  • The network holds up to 100 examples. Oldest examples are dropped when full (FIFO).
  • Use the SPREAD knob to control how wild the exploration is. Low spread = dramatic changes. High spread = subtle refinements.

Context Menu

Right-click the module to access these settings:

  • Output ranges -- toggle each of the 16 outputs between unipolar (0-10V, default) and bipolar (+/-5V).
  • Input ranges -- toggle each of the 8 inputs between unipolar (0-10V, default) and bipolar (+/-5V). Set to bipolar if your source produces +/-5V signals (e.g., standard LFOs).
  • Compute derived stats (Mean/Std/Delta) -- internal statistics toggle (no dedicated output jacks in the current module).
  • Output slew -- smoothing time applied when network weights change (after training or perturbation). Prevents clicks from sudden output jumps. Options: 0, 5, 10, 20, 50, 100 ms. Default: 10 ms.
  • Presets (.nisps) -- save/load the full module state (weights, training examples, knob positions, ranges) to/from a .nisps JSON file.
  • Browser bridge (WS<->OSC) -- enable the OSC server for the companion browser app; choose the listen port (7001, 7002, 7003, 9000, 9001).

Presets (.nisps)

MEMLNaut uses .nisps files for saving and sharing trained networks. The format (version 3, flat core-exact weight vector) is specified in docs/specs/vcv-module.md -- the single source of truth. Files saved by pre-2026-07 builds (version 1, nested weight arrays) no longer load.

Full module state is also saved automatically with your VCV Rack patch file; you do not need .nisps exports to preserve work between sessions.

Browser Integration (Manifold)

MEMLNaut can be driven live from the Manifold browser app (manifold/, VCV backend) over a WebSocket-to-OSC bridge (manifold/osc-bridge/bridge.ts, Deno).

  1. Right-click the module and enable OSC server (default port 7001; each module instance offsets its default port automatically).
  2. Run the bridge with --osc-port matching the module's port.
  3. In Manifold, select the VCV backend.

What flows over the wire (the complete protocol -- see docs/specs/vcv-module.md):

OSC Address Direction Content
/nisps/input Web -> VCV Input vector (browser drives the model: "bridged mode")
/nisps/output VCV -> Web Current output values (~10 times/sec)
/nisps/input VCV -> Web Current input values (~10 times/sec)
/nisps/feedback Web -> VCV Verdict ops (up / down / rand / clear)

Training is bidirectional: both the browser verdict loop and the module's panel buttons drive the same network. There is no weight/state sync channel -- persistence belongs to the Rack patch.

Technical Details

Network architecture

Inputs:  8
Hidden:  16 -> 24 -> 16  (3 hidden layers, ReLU activation)
Output:  16 (sigmoid activation, producing values in [0, 1])

The ML engine is the repository's shared nisps/ core (nisps::ml::MLPCore<DynamicStorage> behind the thin adapter src/iml.hpp) -- bit-identical training/inference semantics to the MEMLNaut firmware and the browser WASM engine.

Inference

The MLP runs in the VCV process() callback. The RATE knob controls how often inference runs, from once per 256 samples (~170 Hz) to every sample (44.1 kHz). Between inference steps, outputs are linearly interpolated to avoid staircase artifacts.

Threading

  • Audio thread: Reads CV inputs, runs MLP inference, writes CV outputs. Never blocks.
  • Worker thread: Handles training (thumbs-up) and weight perturbation (thumbs-down). When complete, signals the audio thread to crossfade to the new outputs.
  • Each module instance has its own independent worker thread and ML engine.

Building

See BUILDING.md (build steps, troubleshooting) and DISTRIBUTION.md (packaging .vcvplugin bundles, cross-platform builds).

Project structure

vcv/
  Makefile          # Build configuration (C++20; core via relative includes)
  plugin.json       # Plugin manifest (name, version, tags)
  src/
    plugin.cpp      # Plugin initialization
    plugin.hpp      # Plugin globals
    MEMLNaut.cpp    # Module logic, widget/UI, serialization, OSC wiring
    iml.hpp         # Thin adapter over the shared nisps/ ML core
    osc_server.hpp  # OSC bridge server (transport only)
    LedRing.hpp     # Per-output LED ring widget
    palette.hpp     # Color palette from the frontend design tokens
  res/
    MEMLNaut-wide.svg      # 44HP panel (the one the module loads)
    MEMLNaut.svg           # Unused 30HP variant
    MEMLNaut-expander.svg  # Unused 8HP expander panel
  dist/             # Built .vcvplugin bundles
  dep/              # Build dependencies