Turn your Neural Amp Modeler captures into one new, trainable .nam model — either by blending two amps into one playable hybrid, or by sweeping several fixed-gain captures of one amp into a single Input-gain-driven model.
Ever wanted a clean Fender that opens up into a Marshall crunch the harder you
dig in, without switching presets? Or a Gain-1-to-10 amp pack collapsed into
one NAM you sweep with the ordinary Input knob instead of ten separate files?
NAM Mixer renders your own .nam captures through the same real NAMCore
inference engine used by the official plugin, combines them with one of three
Builder design modes (or, for one amp, the Continuous Gain workflow),
and trains the result into a brand-new standalone NAM model — no switching, no
source models required at inference time.
Everything runs locally — as the desktop app or a small Flask app run from source: your amp captures, DI files, and generated training material never leave your computer unless you explicitly send a training job to your own Kaggle account for free GPU time.
Status: Beta. It renders source NAMs through real NAMCore inference and generates trainable A2 bundles end to end — listen to and validate every generated model before you trust it.
No Python or renderer setup needed — the desktop app bundles the backend, the web UI and the native nam_render.
Direct downloads (always the latest release; all builds are on the Releases page):
| Platform | Download | Notes |
|---|---|---|
| macOS (Apple Silicon) | NAM-Mixer-macOS-arm64.dmg |
Not notarised: on first launch use Open from the Finder context menu (or approve it in Privacy & Security). |
| Windows (x64) | NAM-Mixer-Windows-x64-setup.exe · .msi |
Installer (.exe) or MSI package. |
| Linux (x64) | NAM-Mixer-Linux-x64.AppImage · .deb |
Portable AppImage, or a Debian/Ubuntu package. |
Older versions and release notes: all releases. See Desktop app downloads for what the desktop build does and does not include.
Clone the repo and run it as a local Flask app with python3 — see
Quick start below. This gives you the full
render/design/audition/generate experience described in this README, plus
direct access to the code. Local A2 training uses a separate environment and
needs Python 3 installed; Kaggle training remains opt-in and uses your own
Kaggle CLI/account — see Workflow.
Splitting your tone across two amp captures — a clean voice for verses, a driven one for choruses — usually means a preset switch, a MIDI patch change, or an A/B/Y box. NAM Mixer instead builds a continuous, level-driven transition between two captures, so the amp itself responds to how hard you play, the way a real tube amp's character shifts with picking dynamics. It then lets you freeze that behavior into one trained model, so the hybrid becomes something you can load anywhere a NAM runs.
| 🎚️ Three design modes | Dynamic Hybrid — level-driven crossfade between Amp A and Amp B. Parallel Blend — constant-ratio mix of both amps, independent of playing level. Character Blend — a deterministic teacher built from one drive-selected donor plus measured tone/feel corrections. |
| 🎧 Real NAM inference | Amps are rendered through the same NeuralAmpModelerCore C++ engine the official plugin uses — not a reimplementation, not an approximation. |
| 🔊 Live audition | A/B the two source amps against the blended result over a bundled library of real guitar/bass performances, with input-profile simulation, test gain, coverage analysis, and an optional cabinet IR — all without re-running inference on every tweak. |
| 🧠 Train a real model | Generate a proper training bundle from the official NAM training excitation and train a new A2 model on it — locally, or with one click on a private Kaggle GPU. |
| ✍️ Optional AI recipes | A local, Cloudflare Workers AI, or custom OpenAI-compatible model can turn a plain-English tone description into editable blend settings; built-in rules remain available with no AI service. |
| ✅ Built-in validation | Every exported model is re-rendered and compared against its frozen teacher (ESR, RMS, peak, quiet-response) so you know exactly how close the trained model landed, not just that training finished. |
| 🎛️ Continuous Gain | Turn several fixed-gain captures of one amp into one standard .nam you sweep with an ordinary player's Input gain — captures are analysed, a subset is chosen from measurements, and the model is trained locally or on Kaggle. See Continuous Gain. |
| 💾 Sessions library | Save, reload, import/export, and revisit designs and completed models without losing your place. |
| 🔒 Local-first & private | No cloud dependency, no telemetry, no account required. Kaggle training is fully opt-in and uses your own credentials and quota. |
| ⚙️ In-app Settings | Configure the renderer path, AI provider, and TONE3000 API key from a Settings tab — no shell environment variables required. See Settings. |
| ✅ Getting-started checklist | The Settings tab's "Getting started" section shows at a glance what's ready and what's still optional (renderer, training input, local A2 training env, Kaggle, AI provider). |
The app never claims that a completed training run sounds identical to its teacher. Always listen to and validate exported models.
- Get NAM Mixer
- Why this exists · Features
- What this is · This is NOT model-weight merging
- How the blend works: dry-input level, level matching, input profile vs. crossover vs. calibration
- Preview DIs vs. training material
- Workflow (incl. Kaggle GPU setup) · Sessions · Continuous Gain · Design modes & Cabinet IR
- Quick start (macOS, Linux, Windows) · Running it
- Settings — configuring the renderer, AI providers, and TONE3000 from the app itself
- Current status and limitations
- Safety: training target vs. live preview
- Project layout · NAM Tools
- License and attribution
NAM Mixer is a small local tool with two ways to build a new, trainable NAM model from captures you already have:
- Builder (the Dynamic Hybrid/Parallel Blend/Character Blend tab) combines two existing Neural Amp Modeler (NAM) amp captures — for example, moving from a Fender clean model toward a Marshall crunch model as you play harder.
- Continuous Gain takes several
fixed-gain captures of one amp and channel (Gain 1 … 10, say) and builds
one standard
.namyou sweep with an ordinary player's Input gain instead of switching between capture files.
Both generate a trainable target, then train and validate a single resulting NAM A2 model that reproduces the chosen design without requiring the source models at inference time.
We are not averaging, interpolating, or otherwise combining the weights of two NAM neural networks. That's a different (and much harder, largely unsolved) problem — two independently-trained networks don't share a latent space, so blending their weights doesn't produce anything musically meaningful.
Instead, the approach is:
- Render the same input audio through Amp A and through Amp B separately (two ordinary, unmodified NAM inference passes).
- Blend the resulting audio, sample-by-sample, using a crossfade curve driven by the level of the original dry input — quiet input leans toward Amp A's render, loud input leans toward Amp B's render, with a smooth transition in between.
- That blended audio becomes a synthetic training target.
- Eventually, train a fresh NAM A2 model against that synthetic
input/output pair, producing one model that reproduces the hybrid behavior
directly — at that point the two source
.namfiles are no longer needed at inference time.
Step 4 is available through the UI for either Kaggle GPU or local training. Every generated model still needs listening and validation; the tool does not make a claim of perceptual equivalence merely because training completes.
The crossover has to be driven by something that exists independently of which amp is currently being auditioned — otherwise there's a chicken-and-egg problem (you'd need to know the blend to compute the blend). The dry guitar signal before either amp model touches it is the one signal that's the same regardless of which amp(s) you render it through, so it's the natural, stable control signal for "how hard is the player driving this right now."
Concretely: level = envelope(dry_input), and that per-sample level (in dBFS)
is what decides the Amp A/Amp B mix weight, not the loudness of either
processed output. See hybrid/envelope.py.
Two independently-trained/captured NAM models will almost never happen to sit
at the same loudness for a given input level. If Amp B is simply louder than
Amp A at the crossover point, the transition will sound like a volume jump
rather than a change in amp character — exactly the artifact we're trying to
avoid. Rather than normalizing the two amps' overall loudness (which doesn't
guarantee anything about how they compare specifically at the crossover
point, where it actually matters), this project measures each amp's loudness
using only the portion of the render that falls near the chosen crossover level
and computes a trim from that. See hybrid/level_match.py.
It's easy to conflate these; they operate at different stages and have very different costs:
- Input profile (
hybrid/input_profiles.py,render_pair()): simulates a different instrument/pickup driving the signal chain before it reaches either NAM. This changes the actual audio both amps receive, so changing it is EXPENSIVE — it requires re-running NAM inference for both amps. Seedocs/history/INPUT_PROFILE_RESEARCH.mdfor the research behind the presets and why active pickups deliberately have no fixed preset. - Crossover / transition width / manual trim (
build_hybrid()): changes only how the already-rendered Amp A/B responses are blended together. This is CHEAP — pure numpy, no NAM inference, safe to recompute on every slider move. - NAM input calibration (
hybrid/calibration.py): when both.namcaptures report their own recording calibration (input_level_dbu), applies the official NAM plugin's per-model compensation formula so two differently-calibrated captures see the same virtual physical input level. This also happens at render time (it's folded intorender_pair()alongside the input profile), but it's a distinct concept from "how hard is the (virtual) instrument driving the amps" — one is about the instrument, the other is about reconciling two amp captures' own assumptions about their input level.
assets/di/ contains real recorded DI (direct input) guitar/bass performances,
copied from the NAMtoClo project (see
assets/di/README.md for exactly which files, where they came from, and their
measured characteristics). They exist so you can audition a hybrid
crossover on real playing dynamics — palm-muted riffing, clean chords, dynamic
picking, etc. — without needing your own guitar/interface set up, and so the
project has a consistent, versioned set of fixtures for regression testing the
blend engine itself.
genre/style DI (assets/di/*.wav)
↓
preview, auditioning, crossover analysis,
automatic level-match testing, regression testing
official NAM v3.0.0 training input (a proper calibrated reamp/DI signal, e.g.
the kind of stimulus NAMtoClo itself uses as nam_input_wav.wav — bundled at
assets/training/, seeded automatically on first run; see that folder's README)
↓
Amp A render
Amp B render
↓
dynamic blend
↓
synthetic training output
↓
A2 training
The genre DIs are musical performances — useful for judging how a hybrid
sounds and behaves, but not designed to exercise the amp's full frequency/
level response the way a proper reamping signal is. The actual synthetic
training pair used to eventually train an A2 model must come from a real NAM
training/reamping signal, not from assets/di/. This distinction is
deliberate and should not be blurred — see assets/di/README.md for more.
The interface is organised into four stages. You can move between them at any time; the same sources, preview material, cabinet, output safety, and training tools remain shared across all three design modes.
-
Sources — select Amp A and Amp B
.namfiles, a preview DI, an instrument/input profile, and (when needed) advanced NAM calibration or per-amp input trims. Render Amps runs the two expensive NAM inference passes. -
Shape — choose Dynamic Hybrid crossover/transition, Parallel Blend mix, or Character Blend tone/feel/drive controls, then set level matching. These controls recombine the cached pair and do not re-run NAM inference.
-
Listen — compare Amp A, the current result, and Amp B against a chosen genre DI, using an input profile to simulate different pickups/output levels (DESIGN/preview context only — see "Input profile vs. crossover vs. NAM calibration" above). The genre DI is a convenience audition performance, not engineered to hit every level a real player might reach — use Test gain (an additional real gain on top of the profile, applied before both amps render) to deliberately push the level up/down and stress-test the crossfade beyond whatever that clip's own dynamics happen to cover. Test gain automatically re-renders the pair after you stop dragging. Cabinet and output-gain controls are optional finishing tools; Dynamic Hybrid analysis is available on demand.
-
Create & train — the official NAM training excitation is bundled and ready by default (upload a different one only if you want to); click "Generate Training Bundle" — this freezes the current design into an immutable
HybridDesign(hybrid/design.py) and blends the official training input (not the preview DI, and not with the input-profile gain applied — the profile only shaped design/preview, never the actual training excitation) through Amp A/Amp B with that frozen design (hybrid/training_target.py). Producesinput.wav,hybrid_target.wav(+hybrid_target_raw.wavfor comparison),hybrid.hybrid.json, andtraining_manifest.jsonunderwork/a2/<design_id>/. Then train a real A2 (PackedWaveNet) model on the bundle, either:- Kaggle GPU (recommended — no local Torch install, trains on a free private Kaggle T4) — see Setting up Kaggle GPU training below, or
- Local, with
scripts/train_a2.py(needs a separate Torch/neural-amp-modelerenvironment — seerequirements-training.txt/scripts/setup_a2_env.sh(macOS/Linux) /scripts/setup_a2_env.ps1(Windows) — never the app's own Python environment).
Both paths validate the exported
.namidentically: loading and rendering its Full and Lite branches through the existing native NAMCore renderer, recording raw/gain-normalized ESR, and checking quiet response against an equivalent processed reference when one is available. Training completion and technical validation quality are reported separately.
Kaggle GPU is optional — the preview/design UI and local training work fully without it. To enable the one-click "Train A2" button on the "Create A2" card:
- Have (or create) a free Kaggle account.
- Verify your phone number on kaggle.com/settings (Account tab) — Kaggle requires this before it will grant GPU/TPU accelerator quota to any account, regardless of how you submit the job. This is the step people most often miss.
- Install and authenticate the Kaggle CLI. The Create A2 card offers an
explicit Install Kaggle CLI button when it is missing; alternatively,
install it once in your normal shell (the app never imports Kaggle's
library; it invokes the CLI/module only for cloud operations):
This opens a browser to sign in and stores a credential locally (
pip install kaggle kaggle auth login
~/.kaggle/kaggle.jsonorKAGGLE_API_TOKEN) that the Kaggle CLI manages entirely on its own — NAM Mixer never reads, stores, or logs it. - Reload the "Create A2" card in the app; it should show "Connected ✓" and enable the Train A2 (Kaggle) button.
Each training run stages a private, uniquely-named Kaggle dataset + kernel under your account, polls it without blocking the app, downloads the result, and re-validates it locally before calling the job complete — see the in-app setup checklist and Setting up Kaggle GPU training for the mechanics, troubleshooting, and how to recover a job that trained but failed to download. Kaggle's free T4 quota is weekly and account-wide; the "Create A2" card shows your remaining quota before you submit.
The Sessions tab is a project library, not a popup. Use it to save the current controls under a name, load or inspect an earlier design, export a portable NAM Mixer JSON file, import one, or delete a session. Generated training bundles are also saved as sessions automatically so their completed NAM can be downloaded again or opened in Tools. Validation reports are bound to the completed NAM's SHA-256; editing the NAM invalidates that report. A completed bundle also offers a synchronized Teacher/Full/Lite comparison on the selected musical DI at normal or quiet input level. It reconstructs the teacher from the saved design, never from current controls.
A session restores the selected settings and app-managed NAM/cabinet file
references, but it deliberately does not render automatically. After loading,
use Render Amps to rebuild the pair and verify that the referenced files
are still available. Training manifests under work/a2 are separate from
sessions and are not interchangeable with session JSON files.
The Continuous Gain tab is separate from the Builder's two-amp modes. You give it several fixed-gain captures of one amp and
channel (for example Gain 1 … 10), and it builds one standard .nam whose whole gain range you explore with an ordinary NAM
player's Input gain — no model switching, no extra runtime processing. It approximates the amp's range; it does not reproduce every
knob position exactly.
It follows the Builder's guided flow — four stages, one centred column of cards:
- Add captures — drag in the
.namfiles and confirm each one's physical gain position (file names only suggest it). Missing or duplicate positions, wrong sample rates and too-few captures are flagged before anything runs. - Analyse & select — every capture is probed, audited (VALID / CORRECTED / SUSPECT; nothing uncertain is silently fixed) and profiled; a training subset is chosen from those measurements (Automatic), or you use all captures or pick your own (Custom). You see why each capture was chosen or omitted, how well the omitted ones are reproduced, the measured response, and the Input-gain mapping that will be used. If no subset meets the coverage rule it says so and falls back to all eligible captures.
- Train — training files are created and trained with the same Kaggle / local training section as the Builder (same setup, presets, progress and logs). The default recipe is the validated one: official NAM input plus real guitar DIs at level offsets, response-distance anchors on −20…+14 dB, one peak-ceiling output scale (shown as the Output gain to set in your player). Fixed 4 dB anchors are available under Advanced as an explicit alternative.
- Test & export — automated checks (standard NAM structure, Full/Lite renders, output safety, per-position measured comparison with
your real captures on held-out DIs, direction reversals), an optional Input-gain sweep and A/B clips against the originals, and an
export package (
.nam, JSON metadata, player guide). Export is never blocked by validation or by listening.
Projects appear in Sessions labelled Continuous Gain (Sessions → Load opens this tab on that project; Delete removes its files). Analysis parallelises the native renders and caches probes per capture, so re-analysing after adding a capture is quick.
What to expect: a 60-epoch model takes roughly half an hour to train; validation figures are measurements, not a listening result —
always listen. The tab's design, the training-material experiments and the frozen configurations it reproduces are documented in
docs/continuous_gain_tab.md.
The workflow above describes Dynamic Hybrid mode, the original/default mode. Two further modes are available from the same mode selector:
- Dynamic Hybrid (
hybrid/blend.py,hybrid/design.py,hybrid/training_target.py): changes from Amp A toward Amp B according to playing level, via the crossover/transition envelope described above. - Parallel Blend (
hybrid/fixed_blend.py,hybrid/blend_training_target.py): always combines the two amp responses at one constant, user-chosen ratio (result = A * (1 - mix_b) + B * mix_b), independent of playing level — no crossover envelope at all. Its own auto level-match uses the DI's ACTIVE playing material (silence excluded) rather than a crossover band, since there's no crossover region to match around (seehybrid.fixed_blend.compute_active_trim). - Character Blend (
hybrid/character_blend.py): uses a continuous, residual-bounded nonlinear carrier plus measured EQ and compression corrections to produce a deterministic teacher design. Tone, Feel, and Drive are not a simple parallel waveform mix. Drive preserves exact Amp A/B carriers outside a 35-65% soft region and can optionally vary by input level.
All modes share Amp A/Amp B, the preview DI, input profile/calibration,
render, test gain, the Listen controls, the Cabinet IR stage, the official
training input, A2 quality, and training — switching tabs never re-runs NAM
inference; the already-rendered RenderedPair (hybrid/pipeline.py) is
reused by whichever mode you're auditioning.
A third, mode-independent stage — Cabinet IR (hybrid/cab_ir.py) — sits
AFTER the amp combination in any mode. Preview is independent of training:
all A2 training and validation uses the head-only target. When an IR is
selected, the export choices are:
- No cabinet: train/export the conventional head-only A2. A selected IR remains a reusable preview/bundle artifact, not part of the target.
- Create both (
embedded, advanced): retain the tested conventional head-only A2 and derive a second NAM containing an explicitly extracted Full WaveNet followed by canonical Linear FIR taps in a NAM Sequential model. The prepared IR WAV is also retained. The two artifacts are offered as clearly separate head-only and-with-cab.namdownloads. This option is hidden by default; enable Settings → Advanced → Enable experimental NAM architectures before selecting it.
Older saved designs that use the legacy learned mode remain readable, but
the current UI no longer applies the cabinet to the training target.
The cabinet output folds the recorded post-cab safety scalar into the Linear
weights and is downloadable only after validation using the bundled,
Sequential-capable renderer. NAM format validity and NAM A2 compatibility
are two separate claims: A2-only players may reject a Sequential model.
Use the separately provided head-only download for broad compatibility. The
setting is enforced server-side as well as in the UI, so a client cannot
request the experimental export while it is disabled.
Receptive-field policy: one hard check, two advisory ones. Amp A/Amp B (+, for Hybrid and Character, the bounded crossover envelope) are the CORE dependency — this must fit inside the destination A2's actual receptive field, or generation/training is refused exactly as before. Two further dependencies are always calculated and reported honestly, but never block training by themselves, because in both cases the A2 is being trained to approximate the rendered teacher rather than to compile its signal graph exactly:
- Character processing. Character Blend's donor transitions, smoothing,
and correction filters add their own formal temporal dependency
(
formal_character_required_samples). If it exceeds the A2's receptive field, training still proceeds as an approximation — the Full/Lite export and quiet-response checks stay authoritative for judging the result. - Baked cabinet. A baked cabinet adds
len(ir) - 1samples of serial temporal dependency on top of the core (seehybrid/receptive_field.py'scombine_required_history). A baked cab whose formal total exceeds the A2's receptive field does not block training either — the A2 learns an approximation of the post-cab response within its available capacity.
Either case prints an explicit "CABINET APPROXIMATION" or "CHARACTER
APPROXIMATION" notice from scripts/train_a2.py/the Kaggle cloud worker —
validate the result by listening and by checking the printed ESR/RMS
metrics against the baked target. The exact same full-length cabinet IR is
used for preview and for baking in either case; only the training-time
gating differs. Local and Kaggle training implement this policy
independently but are kept from silently diverging by
tests/test_receptive_field_parity.py.
Because raw WAV/FIR length is a poor proxy for how much of a captured IR is actually audible signal, the Cabinet card also reports cumulative-energy diagnostics (e.g. "99.9% energy by: 42.7 ms" for a nominally-500ms IR) — purely informational, never used to shorten the actual convolution.
The Tone Wizard works without an AI service. The provided .env enables its
optional local-AI suggestion with your local Ollama model (gemma4:e4b at
http://127.0.0.1:11434/v1). Start Ollama, then launch NAM Mixer with:
scripts/run.shThe launcher reads .env automatically. To change machines or models, edit
your untracked .env; .env.example documents the available defaults.
The base URL must be a loopback HTTP URL (localhost, 127.0.0.1, or ::1)
and is deliberately read only from the process environment. The browser cannot
choose an endpoint. Only the written tone request and a fixed recipe schema
are sent to the local service—never NAM files or audio. If the model is down,
slow, or returns invalid settings, NAM Mixer uses its built-in recipe rules.
Every platform needs Python 3.10+ plus a working nam_render — the
native NAM inference executable. You don't need a C++ compiler to get one:
CI builds nam_render for macOS, Linux, and Windows on renderer releases (see
.github/workflows/build-nam-render.yml). Version tags such as v0.2.0 also
build and attach the complete NAM Mixer desktop installers for macOS, Linux,
and Windows (see .github/workflows/build-desktop.yml). Downloading a prebuilt binary
is the default path below. Building from source is the fallback, for a
platform/architecture CI doesn't cover or if you'd rather not run a
downloaded binary.
Installers are built by .github/workflows/build-desktop.yml and published on the project's
GitHub Releases page for every version tag (v*). Each release carries the same five files under fixed names, so
these links always point at the newest release:
| Platform | Direct download | What it contains |
|---|---|---|
| macOS Apple Silicon | NAM-Mixer-macOS-arm64.dmg |
Tauri app for arm64 Macs |
| Windows x64 | NAM-Mixer-Windows-x64-setup.exe |
Windows installer |
| Windows x64 | NAM-Mixer-Windows-x64.msi |
Windows MSI package |
| Linux x64 | NAM-Mixer-Linux-x64.AppImage |
Portable app |
| Linux x64 | NAM-Mixer-Linux-x64.deb |
Debian/Ubuntu package |
To get a specific version, open its page under Releases (for example https://github.com/daverage/nam-mixer/releases/tag/v0.3.3) and download from Assets.
The desktop release bundles the Flask backend, web UI, training support files,
and the native nam_render executable; users do not need to install Python or
the renderer separately. Local A2 training still creates its own training
environment the first time it is used, and Kaggle training still needs Kaggle
authentication.
Releases are currently unsigned, so macOS may require Open from the Finder
context menu (or an approval in Privacy & Security) on first launch. If a download link returns
"not found", no release has been published yet: use the standard browser setup below or run the
desktop build locally as documented in desktop/README.md.
After first-time setup below, scripts/run.sh (macOS/Linux) or
scripts/run.ps1 (Windows) is a one-line way to relaunch later — it creates/
activates .venv, installs/updates dependencies only when
requirements.txt has changed, opens the browser on the selected local port,
and starts the app; it does not fetch nam_render for you.
macOS
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
scripts/download_nam_render.sh
scripts/run.shscripts/run.sh (and app.py directly) default to port 5001, not 5000 --
macOS reserves 5000 for AirPlay Receiver, which silently returns 403 instead
of refusing the connection. To use a different port, run
PORT=5003 scripts/run.sh.
Build from source instead
Requires Xcode's Command Line Tools (xcode-select --install) and
CMake 3.18+. The first build downloads NeuralAmpModelerCore + its
dependencies (a few hundred MB, one-time):
The normal renderer supports both conventional NAM and canonical
Sequential/Linear NAM. If you previously built the older renderer, remove
native/nam_render/build first so CMake picks up the new pinned core.
rm -rf native/nam_render/build # only needed when replacing an older build
cmake -B native/nam_render/build -S native/nam_render -DCMAKE_BUILD_TYPE=Release
cmake --build native/nam_render/build --config Release --target nam_render -j 4Linux
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
scripts/download_nam_render.sh
scripts/run.shBuild from source instead
Requires a C++ toolchain and CMake 3.18+, e.g. on Debian/Ubuntu:
sudo apt-get update && sudo apt-get install -y build-essential cmakeThen:
rm -rf native/nam_render/build # only needed when replacing an older build
cmake -B native/nam_render/build -S native/nam_render -DCMAKE_BUILD_TYPE=Release
cmake --build native/nam_render/build --config Release --target nam_render -j "$(nproc)"Windows
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
powershell -ExecutionPolicy Bypass -File scripts/download_nam_render.ps1
powershell -ExecutionPolicy Bypass -File scripts/run.ps1If Activate.ps1 is blocked, run PowerShell as: powershell -ExecutionPolicy Bypass.
Don't double-click
nam_render.exe. It's a command-line helper tool thathybrid/render.pycalls automatically with the right arguments — it's not the app. Double-clicking it in File Explorer runs it with no arguments, so it prints a usage error and the console window closes instantly, which looks like a crash but isn't one. Always launch the app itself viascripts/run.ps1(orpython app.py); that's what finds and invokesnam_render.exefor you behind the scenes.
Build from source instead
Requires CMake and the "Desktop development
with C++" workload from the
Visual Studio Build Tools
(the MSVC toolchain nam_render needs), then:
Remove-Item -Recurse -Force native/nam_render/build # only needed when replacing an older build
cmake -B native/nam_render/build -S native/nam_render -DCMAKE_BUILD_TYPE=Release
cmake --build native/nam_render/build --config Release --target nam_renderThis produces native/nam_render/build/Release/nam_render.exe, supporting
both conventional NAM and Sequential/Linear NAM models.
Then run scripts/run.sh (macOS/Linux) or scripts/run.ps1 (Windows) — the
launcher opens the browser on the selected local port, and the app discovers
the nam_render executable it just downloaded/built automatically, wherever
it landed for your platform. From here:
- Live preview/design/audition works immediately — no further setup.
- Local A2 training needs a separate, dedicated training environment
(never the app's own Python environment — see
requirements-training.txt): runscripts/setup_a2_env.sh(macOS/Linux) orscripts/setup_a2_env.ps1(Windows) to create it, then follow the Torch install command it prints for your platform/GPU. - Kaggle GPU training needs no local training environment at all — see Setting up Kaggle GPU training above.
The Settings tab (next to Sessions) covers anything that used to only be
configurable via a shell environment variable or an .env file. Its
Updates section has a Check for updates button — it only ever
contacts GitHub when you click it (never automatically), and reports either
"you're up to date" or a direct download link to the newer installer for
your OS (desktop app), or a link to the release notes plus a git pull hint
(running from source). Its Getting started section gives an at-a-glance
checklist of what's ready and what's still optional (renderer, training
input, local A2 training environment, Kaggle, AI provider) so a fresh
checkout doesn't require hunting for each setup button individually.
- NAM render executable — normally auto-detected (downloaded via
scripts/download_nam_render.*, or the in-app Download nam_render automatically button). Change it here only if you built/downloaded a customnam_render. - AI Assistant — choose Local, Cloudflare Workers AI, or Custom
OpenAI-compatible. Local uses a localhost
/v1endpoint and needs only a model name. Custom remote endpoints must use HTTPS; their hostname is resolved and private/loopback/link-local/reserved addresses are rejected. Conversation Markdown exports can optionally include a structured debug appendix containing the exact bounded model messages, research queries and results, warnings, and parsed responses. API keys and authorization headers are never included. - Cloudflare Workers AI — in the Cloudflare dashboard, open Workers AI →
Use REST API → Create a Workers AI API Token, then copy the token and the
account ID. In NAM Mixer choose Cloudflare, enter the 32-character Account
ID, the token, and a JSON-mode model such as
@cf/meta/llama-3.3-70b-instruct-fp8-fast; the app buildshttps://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1and sends the request to/chat/completionsfor you. A manually created token needs account-scoped Workers AI Read and Workers AI Edit permissions (the dashboard's “Create a Workers AI API Token” template is the easiest route). Cloudflare quota, capacity limits, and billing may apply. Test connection performs a small real JSON-mode inference request, so it may consume quota. - Automation boundary — NAM Mixer automates endpoint construction,
authentication headers, JSON-mode requests, connection testing, and safe
error handling. It deliberately does not create Cloudflare accounts/tokens,
store tokens in browser code, or silently rotate/reuse tokens when switching
providers. Use the explicit Clear API token action to remove a saved
token from
.envand the running process. - Local AI assistant — any host that speaks the OpenAI-compatible
/v1API works (Ollama, LM Studio, llama.cpp server, ...); Google's Gemma (gemma4:e4b) is a good default if you don't already run something else, and a Pull via Ollama button offers a one-click download when Ollama is installed but nothing's running yet. - TONE3000 API key — enables the TONE3000 tab's capture search. Get a key from your account at tone3000.com; saved keys are never echoed back by the app once entered.
- Advanced → Enable experimental NAM architectures — off by default.
Turning it on reveals Create both, which exports the tested head-only
NAM plus an exact Sequential Embedded cabinet derivative. Leave it off for
ordinary use unless you specifically need the advanced export.
Settings are saved to the source checkout's own
.envfile — never uploaded anywhere.
- Real NAM inference:
hybrid/render.pycalls the nativenam_renderC++ CLI innative/nam_render/, built against NeuralAmpModelerCore. This keeps inference independent of Pythontorch/neural-amp-modelerand lets NAMCore remain authoritative for.nammodel loading. Build steps are innative/nam_render/README.md. .namloading and calibration:hybrid/nam_loader.pyreads model metadata, includinginput_level_dbuandoutput_level_dbuwhen present. Older or uncalibrated files remain usable and are reported as having unavailable calibration metadata.- The complete app workflow: the Flask app and browser UI connect real
rendering, preview, coverage analysis, all three design modes, cabinet IR,
Sessions, target generation, local training, and optional private Kaggle
training. The main routes include
/api/render_pair,/api/preview,/api/blend_info,/api/blend_curve,/api/input_profiles,/api/profile_coverage, and/api/generate. - Renderer readiness: the app performs a cheap
--helplaunch check before preview inference, distinguishes missing from unusable executables, shows expandable setup help, and can retry after installation without a restart. - Mechanical safeguards: envelope following, blend math, level matching, optional alignment, receptive-field checks, NaN/Inf and silence checks, and non-limiting training-target peak control are implemented and covered by automated tests against synthetic signals.
- The Phase 4 real-render harness passed with two distinct local source models, two musical DIs, all three design modes, cabinet on/off, and input levels of -12/0/+12 dB. It produced deterministic rerenders and exact frozen-teacher equivalence for that run. Auto-calibrated cases were unavailable because those source files did not declare input-level metadata. Most automated pipeline tests still use mocked renders. Other real source-model combinations can still expose latency, calibration, or musical problems; listen to every preview and validate every exported model against its target.
- A/B alignment is deliberately off by default.
hybrid/align.pycross-correlates the two rendered signals, so a tonal or phase difference between dissimilar amps can look like latency. Enable it only when the timing behavior of the source models is known. - Character Blend is a deterministic teacher design, not a perceptual-match guarantee. No automated system judges tone, feel, or musical quality; the checks can only catch mechanical problems such as clipping, discontinuities, silence, or non-finite samples.
- Local A2 training needs the separate environment described in
requirements-training.txt. Kaggle training needs a configured Kaggle account and network access; neither is required to run the preview UI. - Continuous Gain reproduces its frozen reference configurations
bit-for-bit and has been exercised end to end (local and Kaggle GPU
training, in-browser) — see
docs/continuous_gain_tab.md. Not yet established: model quality at the full 60-epoch preset by listening (only measurements so far), and the packaged desktop build's Continuous Gain tab on Windows/Linux. - Uploaded amp/cab files and internal render-source copies are only freed
when the session(s) that used them are deleted (a startup sweep also clears
render copies that never became a saved session) — see
app.py's_sweep_orphaned_uploads/_sweep_orphaned_render_sources.
The generated hybrid training target must never be run through a limiter —
that would distort the very dynamic behavior we're trying to capture. If the
generated hybrid exceeds a target peak ceiling (default -3 dBFS), a single
fixed gain reduction is applied to the whole file instead
(hybrid.safety.apply_peak_ceiling). A limiter (hybrid.safety. preview_safety_limiter) exists only as a speaker/headphone safety net on the
live preview/playback path and must never touch a file destined to become (or
derive) a training target.
hybrid-nam-builder/
├── app.py -- Flask entry point
├── requirements.txt
├── hybrid/ -- core library (no Flask/UI dependencies)
│ ├── nam_loader.py -- parse .nam files + calibration metadata
│ ├── render.py -- NAM inference (shells out to native/nam_render)
│ ├── envelope.py -- dry-input level/envelope extraction
│ ├── level_match.py -- crossover-region auto level-match trim
│ ├── align.py -- sample-offset detection/correction (optional, off by default)
│ ├── blend.py -- the dynamic crossfade itself
│ ├── fixed_blend.py -- Parallel Blend mode (fixed-ratio combination)
│ ├── character_blend.py -- Character Blend teacher and low-level check
│ ├── cab_ir.py -- shared cabinet IR convolution (preview + baked target)
│ ├── training_target.py -- Dynamic Hybrid A2 training-target generation
│ ├── blend_training_target.py -- Parallel Blend A2 target generation
│ ├── character_training_target.py -- Character Blend A2 target generation
│ ├── receptive_field.py -- mode/cab-aware temporal-dependency accounting
│ ├── validation.py -- frozen-teacher/model rendering and shared metrics
│ ├── validation_report.py -- versioned Full/Lite/quiet quality reports
│ ├── kaggle_training.py -- private Kaggle GPU job and local validation
│ ├── cg_*.py -- Continuous Gain: probe, audit, profile, selection, anchors, bundle, project, validation, excitation
│ ├── nam_tools.py -- safe output-volume and metadata editing
│ ├── wizard.py -- guided setup flow shared by the UI modes
│ ├── safety.py -- NaN/clip checks, non-limiting peak ceiling
│ ├── settings.py -- Settings tab registry
│ ├── render_bootstrap.py -- in-app "download nam_render" for Settings
│ └── metadata.py -- hybrid provenance metadata (JSON sidecar)
├── native/nam_render/ -- C++ NAM inference tool (NeuralAmpModelerCore), see its README
├── assets/nam_models/ -- user's own .nam amp captures (gitignored)
├── assets/di/ -- genre/style DI library + its own README
├── cg_routes.py -- /api/cg/* routes for the Continuous Gain tab (static/cg.js)
├── templates/, static/ -- minimal HTML/CSS/JS UI (no build step)
├── tests/ -- unit tests for the hybrid/ modules
├── scripts/ -- run/setup/download helpers, train_a2.py, validate_a2.py, analyze_di.py (regenerates assets/di/_analysis.json)
└── work/ -- gitignored scratch output directory
See Quick start above for first-time setup on your
platform. Once dependencies are installed and nam_render is built:
scripts/run.sh
# or: PORT=5001 scripts/run.shpython3 -m pytest -q
# `pytest -q` works too; pytest.ini resolves the repository modules.The test suite exercises hybrid/envelope.py, hybrid/blend.py,
hybrid/level_match.py, hybrid/align.py, hybrid/safety.py,
hybrid/nam_loader.py, hybrid/input_profiles.py, hybrid/calibration.py,
hybrid/coverage.py, hybrid/pipeline.py, hybrid/design.py,
hybrid/training_target.py, hybrid/fixed_blend.py,
hybrid/blend_training_target.py, hybrid/character_blend.py,
hybrid/character_training_target.py, hybrid/cab_ir.py,
hybrid/receptive_field.py, hybrid/a2_training_settings.py, and
hybrid/kaggle_training.py against synthetic signals and mocked renders —
none of it requires torch, neural-amp-modeler, or the native nam_render
tool to be built. tests/test_render.py exercises real NAM inference and is
skipped automatically unless both native/nam_render has been built (see its
README) and a real .nam file is present at
assets/nam_models/FenderSuperReverb1977_Clean.nam.
tests/test_receptive_field_parity.py loads the local and Kaggle trainers
side by side and fails the suite if their receptive-field policies ever
diverge, rather than letting that drift go unnoticed.
For repeatable opt-in real-render coverage across all three design modes, use the explicit harness rather than relying on that personal-model test:
python3 scripts/real_render_regression.py \
--amp-a assets/test_cabs/Clean_NoCab_Fender_Deluxe_Reverb_Head_2.nam \
--amp-b 'assets/test_cabs/HighGain_NoCab_SLASH AFD#2 Head.nam' \
--di assets/di/high_thrash.wav --di assets/di/clean_smooth.wav \
--cab 'assets/nam_models/V30 LL 4FB 4x12 SM57 1.00in 0.0in SA73.wav' \
--out-dir work/real-render-regression --releaseIt writes listenable stems and real_render_report.json beneath ignored
work/; --release fails if prerequisites are missing or a checked case is
invalid/silent. The Phase 4 implementation was exercised with this command in
release mode: 6 cases and 36 listenable artifacts passed, with no invariant or
determinism failures. Calibration combinations remain explicitly unavailable
when both source models lack input_level_dbu metadata.
torch/neural-amp-modeler are no longer in requirements.txt — inference
is handled entirely by the native nam_render tool now. They only matter for
local A2 training, and live in requirements-training.txt
instead (install into a separate, supported-Python-version environment when
you get to that step).
This project reuses the genre/style DI WAV files bundled in the
NAMtoClo fork this repository's author
also maintains, purely as audio fixtures (see assets/di/README.md). It shares
no code with NAMtoClo and solves a completely different problem — NAMtoClo
converts a single existing NAM model to Valeton hardware's CLO format; this
project builds new hybrid NAM training material out of two existing NAM
models. Nothing here depends on NAMtoClo at runtime.
The Tools tab can open a NAM from your computer or select the latest model generated locally by this app. It always creates a new download and never overwrites the source file.
The output-volume tool applies 10 ** (dB / 20) to the recognised final audio
head_scale. head_scale is the output scale after the model has generated
its signal, so this changes output level without retraining or changing the
learned tone, distortion, dynamics, or input response. It also updates each
available metadata.loudness by the same dB amount. It deliberately does not
change metadata.gain: that describes separate metadata/calibration intent,
not the final output scale.
Modern A2 SlimmableContainer NAMs have one final audio model per submodel;
the tool edits only config.submodels[*].model.config.head_scale. Older
single-model files are supported only when their root config.head_scale is
present. Unknown layouts are refused rather than guessed. Before saving, a
recursive JSON diff must match exactly the approved output-scale and loudness
paths; weights and every other model field are therefore unchanged.
Examples: enter +3, +6, or -6 in the Output volume field. A +6 dB change
uses a multiplier of approximately 1.995262; -6 dB uses 0.501187. Boosts
above +12 dB are allowed but may clip in a host or target hardware.
The same safe operation is available from a terminal:
python3 scripts/nam_volume.py Mesa_Boogie.nam +3
python3 scripts/nam_volume.py Mesa_Boogie.nam +6 --dry-run
python3 scripts/nam_volume.py Mesa_Boogie.nam -6 --output Mesa_Boogie_quieter.namThe metadata editor loads and can safely change every official NAM A2
UserMetadata field: name, modeled-by, gear type/make/model, tone type, and
input/output dBu. Export date, trainer details, and measured loudness remain
untouched; use the Output volume slider for loudness. New NAMs identify their
creator as NAM Mixer; physical gear make/model and tone metadata are left
blank until the user can provide factual values.
NAM Mixer is copyright © 2026 Andrzej Marczewski and is released under the
MIT License. The bundled genre/style DI recordings are credited and
documented separately in assets/di/README.md; their
upstream terms continue to apply. Neural Amp Modeler, NAMCore, and other
third-party components retain their own copyrights and licenses.
