diff --git a/CLAUDE.md b/CLAUDE.md
index 09f27fb..b641d4c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -44,6 +44,7 @@ Key files: `js/nisps/` (ML core port), `js/ui/` (visualizer, joystick, controls)
|-------|-------|---------|--------|
| `tame` | 0–1 | 1 | Constrains synth output ranges toward safe limits |
| `spread` | 0–1 | 0.6 | Controls weight initialization, RL noise scaling, and weight decay (see below) |
+| `preset` | preset id | _(none)_ | Auto-loads a synth parameter preset on first visit (e.g. `?preset=beginner-1`) |
#### `spread` — sigmoid saturation control
@@ -76,6 +77,21 @@ The 126 synth parameters in `js/synth/param-map.js` were curated from the C15's
| PM shaper blend | 4 | Secondary routing params |
| FB Mix source selects | 4 | Discrete A/B selectors |
+### Synth Presets
+
+Presets (`js/synth/presets.js`) control which parameters the ML engine can modify, with unselected params muted at safe defaults. Each preset defines per-param `{ muted, fixedValue, min, max, curve }` — no training examples or model weights.
+
+4 tiers of progressive complexity:
+
+| Tier | Presets | Active params | What's exposed |
+|------|---------|---------------|----------------|
+| 1 (Beginner) | 1.1–1.4 | 15 | Basic ADSR, SVF cutoff/res, Shaper A drive/fold, output levels, reverb mix |
+| 2 (Intermediate) | 2.1–2.4 | 40 | + Env B/C, filter FM, effects (reverb/echo/flanger), cabinet, stereo panning |
+| 3 (Advanced) | 3.1–3.3 | ~95 | + Cross-oscillator PM, feedback mixer, dual shapers, comb/gap filters, ring mod |
+| 4 (Expert) | 4.1–4.2 | 126 | Full engine |
+
+Presets use `curve` values to bias parameter distributions (< 0.5 = spend more time low, > 0.5 = bias high) without clamping extremes. Users can tweak any preset via the group drawer after loading.
+
## Build System
This is an Arduino project targeting Raspberry Pi Pico. Build and upload using Arduino IDE or arduino-cli with the earlephilhower/pico board package.
diff --git a/playground/a-immersive.html b/playground/a-immersive.html
index b8231f0..c0da7e6 100644
--- a/playground/a-immersive.html
+++ b/playground/a-immersive.html
@@ -28,6 +28,30 @@
Vol
BPM 120
+
+ Manual
+
+ 1.1
+ 1.2
+ 1.3
+ 1.4
+
+
+ 2.1
+ 2.2
+ 2.3
+ 2.4
+
+
+ 3.1
+ 3.2
+ 3.3
+
+
+ 4.1
+ 4.2
+
+
@@ -226,6 +250,7 @@
Synth Controls
+ Presets — use the dropdown (top left, next to play) to choose a parameter preset. Lower tiers (1.x) expose fewer parameters for simpler exploration; higher tiers (3.x, 4.x) unlock cross-modulation, feedback, and the full engine. You can tweak any preset afterwards using the controls below.
Hover any bar in the synth visualizer to see parameter name, current value, range, and curve
Hover a group name (e.g. "Env A", "Osc B") at the top to open its control drawer:
diff --git a/playground/css/a-immersive.css b/playground/css/a-immersive.css
index ef59f6b..51d48ac 100644
--- a/playground/css/a-immersive.css
+++ b/playground/css/a-immersive.css
@@ -822,6 +822,45 @@ html, body {
display: flex;
}
+/* Synth preset dropdown */
+.preset-select {
+ margin-left: 8px;
+ height: 36px;
+ padding: 0 10px;
+ border-radius: 18px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ background: rgba(13, 13, 13, 0.65);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ color: var(--text);
+ font-family: inherit;
+ font-size: 11px;
+ cursor: pointer;
+ transition: background 0.15s, border-color 0.15s;
+ appearance: none;
+ -webkit-appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 8px center;
+ padding-right: 24px;
+ min-width: 0;
+ max-width: 160px;
+ text-overflow: ellipsis;
+}
+
+.preset-select:hover,
+.preset-select:focus {
+ border-color: rgba(255, 255, 255, 0.15);
+ background-color: rgba(255, 255, 255, 0.06);
+ outline: none;
+}
+
+.preset-select option,
+.preset-select optgroup {
+ background: #1a1a1a;
+ color: var(--text);
+}
+
/* ---- Utility ---- */
.hidden {
display: none !important;
diff --git a/playground/js/a-app.js b/playground/js/a-app.js
index c2216f2..7a547f5 100644
--- a/playground/js/a-app.js
+++ b/playground/js/a-app.js
@@ -8,6 +8,7 @@ import { Arpeggiator } from './synth/arpeggiator.js';
import { MIDIInput } from './synth/midi-input.js';
import { SYNTH_PARAM_MAP, SYNTH_PARAM_NAMES, SYNTH_PARAM_COLORS, applyCurve, applyGroupOverride } from './synth/param-map.js';
import { GamepadInput } from './ui/gamepad.js';
+import { SYNTH_PRESETS, PRESET_TIERS } from './synth/presets.js';
// ---- Constants ----
const N_INPUTS = 2;
@@ -128,9 +129,13 @@ const SYNTH_SECTIONS = [
// Defaults are seeded from safeMin/safeMax in SYNTH_PARAM_MAP, scaled by the
// ?tame URL parameter (0 = unconstrained, 1 = full safe limits).
// localStorage overrides on load.
+
+// Module-level tame so applyPreset() can use it
+const _urlTame = parseFloat(new URLSearchParams(window.location.search).get('tame') ?? '1');
+const tameLevel = isNaN(_urlTame) ? 1 : Math.max(0, Math.min(1, _urlTame));
+
const groupOverrides = (() => {
- const urlTame = parseFloat(new URLSearchParams(window.location.search).get('tame') ?? '1');
- const t = isNaN(urlTame) ? 1 : Math.max(0, Math.min(1, urlTame));
+ const t = tameLevel;
let flatIdx = 0;
return SYNTH_SECTIONS.map(sec => ({
curve: 0.5,
@@ -150,6 +155,13 @@ const groupOverrides = (() => {
}));
})();
+// Lookup: param name -> flat index (for preset application)
+const paramNameToIndex = new Map();
+SYNTH_PARAM_MAP.forEach((p, i) => paramNameToIndex.set(p.name, i));
+
+// Currently active synth preset id (null = no preset / manual)
+let activeSynthPresetId = null;
+
// Build a flat lookup: paramIndex -> { sectionIndex, localIndex }
const paramToSection = [];
{
@@ -188,6 +200,93 @@ function isParamMuted(paramIndex) {
return groupOverrides[mapping.si].params[mapping.li].muted;
}
+// ---- Synth Preset Application ----
+
+/**
+ * Apply a synth preset by id.
+ * Sets groupOverrides (muted/active, min/max/curve/fixedValue) for all 126 params,
+ * re-routes outputs, saves state, and updates the UI dropdown.
+ */
+function applyPreset(presetId) {
+ const preset = SYNTH_PRESETS.find(p => p.id === presetId);
+ if (!preset) {
+ console.warn(`[NISPS] Unknown preset: ${presetId}`);
+ return;
+ }
+
+ const t = tameLevel;
+ const allActive = preset.active === null; // null = all params active (Tier 4)
+ const activeSet = allActive ? null : new Set(preset.active);
+ const overrides = preset.overrides || {};
+ const mutedOv = preset.mutedOverrides || {};
+
+ for (let i = 0; i < N_SYNTH_OUTPUTS; i++) {
+ const param = SYNTH_PARAM_MAP[i];
+ const mapping = paramToSection[i];
+ if (!mapping) continue;
+ const gp = groupOverrides[mapping.si].params[mapping.li];
+ const paramName = param.name;
+
+ // Tame-derived defaults for this param
+ const safeMin = param.safeMin ?? 0;
+ const safeMax = param.safeMax ?? 1;
+ const tameMin = safeMin * t;
+ const tameMax = 1 - (1 - safeMax) * t;
+
+ const isActive = allActive || activeSet.has(paramName);
+
+ if (isActive) {
+ gp.muted = false;
+ const ov = overrides[paramName];
+ if (ov) {
+ gp.min = ov.min !== undefined ? ov.min : tameMin;
+ gp.max = ov.max !== undefined ? ov.max : tameMax;
+ gp.curve = ov.curve !== undefined ? ov.curve : 0.5;
+ gp.fixedValue = ov.fixedValue !== undefined ? ov.fixedValue : param.defaultValue;
+ } else {
+ gp.min = tameMin;
+ gp.max = tameMax;
+ gp.curve = 0.5;
+ gp.fixedValue = param.defaultValue;
+ }
+ } else {
+ gp.muted = true;
+ const mov = mutedOv[paramName];
+ gp.fixedValue = (mov && mov.fixedValue !== undefined) ? mov.fixedValue : param.defaultValue;
+ // Keep tame-derived ranges for manual unmuting
+ gp.min = tameMin;
+ gp.max = tameMax;
+ gp.curve = 0.5;
+ }
+ }
+
+ // Apply group curves
+ for (let si = 0; si < SYNTH_SECTIONS.length; si++) {
+ const gc = preset.groupCurves || {};
+ const secName = SYNTH_SECTIONS[si].name;
+ groupOverrides[si].curve = (gc[secName] !== undefined) ? gc[secName] : 0.5;
+ }
+
+ activeSynthPresetId = presetId;
+
+ // Close the group drawer if open
+ hideGroupDrawer();
+
+ // Re-route outputs through updated overrides
+ if (iml) {
+ routeOutputs(iml.getOutputs());
+ }
+
+ // Update the preset dropdown
+ const $presetSelect = document.getElementById('synth-preset-select');
+ if ($presetSelect) $presetSelect.value = presetId;
+
+ // Save to storage
+ saveState();
+
+ console.log(`[NISPS] Applied synth preset: ${preset.name}`);
+}
+
// ---- SynthVisualizer class ----
class SynthVisualizer {
constructor(canvas) {
@@ -648,6 +747,19 @@ function init() {
// Restore saved state (if any)
loadState();
+ // Wire synth preset selector
+ wireSynthPresets();
+
+ // Check URL ?preset param (overrides localStorage)
+ const urlPreset = urlParams.get('preset');
+ if (urlPreset && SYNTH_PRESETS.some(p => p.id === urlPreset)) {
+ applyPreset(urlPreset);
+ } else if (activeSynthPresetId) {
+ // Restored from localStorage — sync the dropdown
+ const $ps = document.getElementById('synth-preset-select');
+ if ($ps) $ps.value = activeSynthPresetId;
+ }
+
// Initial inference
iml.setInput(0, joyX);
iml.setInput(1, joyY);
@@ -1438,6 +1550,22 @@ function wireKeyboard() {
}
// ---- Quick play controls ----
+// ---- Synth Preset Selector ----
+function wireSynthPresets() {
+ const $select = document.getElementById('synth-preset-select');
+ if (!$select) return;
+ $select.addEventListener('change', () => {
+ const val = $select.value;
+ if (val === '') {
+ // "Manual" selected — clear preset tracking but don't change overrides
+ activeSynthPresetId = null;
+ saveState();
+ } else {
+ applyPreset(val);
+ }
+ });
+}
+
function wireQuickPlayControls() {
const quickPlay = document.getElementById('quick-play');
const quickPlayIcon = document.getElementById('quick-play-icon');
@@ -1879,6 +2007,7 @@ function saveState() {
joyX,
joyY,
groupOverrides,
+ synthPresetId: activeSynthPresetId,
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
} catch (e) {
@@ -1924,6 +2053,11 @@ function loadState() {
}
}
+ // Restore synth preset id (just track it, don't re-apply — groupOverrides already restored above)
+ if (typeof state.synthPresetId === 'string') {
+ activeSynthPresetId = state.synthPresetId;
+ }
+
// Restore output mode
if (state.outputMode && state.outputMode !== outputMode) {
setOutputMode(state.outputMode);
diff --git a/playground/js/synth/presets.js b/playground/js/synth/presets.js
new file mode 100644
index 0000000..8c5cc15
--- /dev/null
+++ b/playground/js/synth/presets.js
@@ -0,0 +1,628 @@
+// Synth presets for NISPS playground
+// Each preset defines which of the 126 C15 params are active (ML-controlled)
+// and which are muted at safe defaults. Organized in 4 tiers of complexity.
+
+export const SYNTH_PRESETS = [
+ // ======================================================================
+ // TIER 1 — Beginner (15 active params)
+ // ======================================================================
+ {
+ id: 'beginner-1',
+ name: '1.1',
+ tier: 1,
+ description: 'Mellow filtered tones with slow attack and long reverb tail',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.2, max: 0.7, curve: 0.6 },
+ 'Env_A_Dec_1': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Env_A_Sus': { min: 0.3, max: 0.8, curve: 0.6 },
+ 'Env_A_Rel': { min: 0.3, max: 0.65, curve: 0.55 },
+ 'Osc_A_Fluct': { min: 0.0, max: 0.3, curve: 0.35 },
+ 'Osc_B_Fluct': { min: 0.0, max: 0.3, curve: 0.35 },
+ 'Shp_A_Drive': { min: 0.05, max: 0.35, curve: 0.35 },
+ 'Shp_A_Fold': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'SV_Flt_Cut': { min: 0.15, max: 0.55, curve: 0.35 },
+ 'SV_Flt_Res': { min: 0.2, max: 0.6, curve: 0.4 },
+ 'SV_Flt_LBH': { min: 0.0, max: 0.4, curve: 0.35 },
+ 'Out_Mix_A_Lvl': { min: 0.4, max: 0.85, curve: 0.55 },
+ 'Out_Mix_SVF_Lvl': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Out_Mix_Lvl': { min: 0.25, max: 0.6, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.15, max: 0.55, curve: 0.4 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'beginner-2',
+ name: '1.2',
+ tier: 1,
+ description: 'Snappy attack, crisp shaper, filter biased high',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.1, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.15, max: 0.5, curve: 0.35 },
+ 'Env_A_Sus': { min: 0.0, max: 0.4, curve: 0.4 },
+ 'Env_A_Rel': { min: 0.15, max: 0.45, curve: 0.4 },
+ 'Osc_A_Fluct': { min: 0.0, max: 0.2, curve: 0.35 },
+ 'Osc_B_Fluct': { min: 0.0, max: 0.2, curve: 0.35 },
+ 'Shp_A_Drive': { min: 0.15, max: 0.55, curve: 0.6 },
+ 'Shp_A_Fold': { min: 0.2, max: 0.8, curve: 0.55 },
+ 'SV_Flt_Cut': { min: 0.5, max: 0.95, curve: 0.65 },
+ 'SV_Flt_Res': { min: 0.3, max: 0.7, curve: 0.55 },
+ 'SV_Flt_LBH': { min: 0.4, max: 1.0, curve: 0.6 },
+ 'Out_Mix_A_Lvl': { min: 0.4, max: 0.85, curve: 0.55 },
+ 'Out_Mix_SVF_Lvl': { min: 0.35, max: 0.8, curve: 0.55 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.25, curve: 0.35 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'beginner-3',
+ name: '1.3',
+ tier: 1,
+ description: 'Slow attack, high sustain, wide filter sweep, deep reverb',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.3, max: 0.85, curve: 0.6 },
+ 'Env_A_Dec_1': { min: 0.4, max: 0.9, curve: 0.6 },
+ 'Env_A_Sus': { min: 0.5, max: 1.0, curve: 0.6 },
+ 'Env_A_Rel': { min: 0.35, max: 0.7, curve: 0.55 },
+ 'Osc_A_Fluct': { min: 0.0, max: 0.4, curve: 0.35 },
+ 'Osc_B_Fluct': { min: 0.0, max: 0.4, curve: 0.35 },
+ 'Shp_A_Drive': { min: 0.0, max: 0.25, curve: 0.3 },
+ 'Shp_A_Fold': { min: 0.4, max: 0.65, curve: 0.45 },
+ 'SV_Flt_Cut': { min: 0.1, max: 0.85, curve: 0.45 },
+ 'SV_Flt_Res': { min: 0.15, max: 0.55, curve: 0.4 },
+ 'SV_Flt_LBH': { min: 0.0, max: 0.6, curve: 0.4 },
+ 'Out_Mix_A_Lvl': { min: 0.45, max: 0.85, curve: 0.55 },
+ 'Out_Mix_SVF_Lvl': { min: 0.4, max: 0.85, curve: 0.55 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.2, max: 0.65, curve: 0.45 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'beginner-4',
+ name: '1.4',
+ tier: 1,
+ description: 'Zero attack, fast decay, low sustain, bright filter with steep curve',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.02, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.1, max: 0.4, curve: 0.35 },
+ 'Env_A_Sus': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Env_A_Rel': { min: 0.1, max: 0.35, curve: 0.4 },
+ 'Osc_A_Fluct': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Osc_B_Fluct': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Shp_A_Drive': { min: 0.1, max: 0.45, curve: 0.55 },
+ 'Shp_A_Fold': { min: 0.3, max: 0.75, curve: 0.55 },
+ 'SV_Flt_Cut': { min: 0.55, max: 1.0, curve: 0.65 },
+ 'SV_Flt_Res': { min: 0.25, max: 0.65, curve: 0.6 },
+ 'SV_Flt_LBH': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Out_Mix_A_Lvl': { min: 0.4, max: 0.85, curve: 0.55 },
+ 'Out_Mix_SVF_Lvl': { min: 0.35, max: 0.8, curve: 0.55 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.3, curve: 0.3 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ // ======================================================================
+ // TIER 2 — Intermediate (adds 25 params → 40 total)
+ // ======================================================================
+ {
+ id: 'intermediate-1',
+ name: '2.1',
+ tier: 2,
+ description: 'Lush reverb and echo, wide stereo spread, gentle cabinet warmth',
+ active: [
+ // Tier 1 base
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ // Tier 2 additions
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.15, max: 0.6, curve: 0.55 },
+ 'Env_A_Dec_1': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Env_A_Sus': { min: 0.3, max: 0.8, curve: 0.6 },
+ 'Env_A_Rel': { min: 0.3, max: 0.65, curve: 0.55 },
+ 'SV_Flt_Cut': { min: 0.2, max: 0.75, curve: 0.45 },
+ 'SV_Flt_Spread': { min: 0.2, max: 0.8, curve: 0.6 },
+ 'Out_Mix_A_Pan': { min: 0.2, max: 0.8, curve: 0.5 },
+ 'Out_Mix_To_FX': { min: 0.2, max: 0.7, curve: 0.55 },
+ 'Reverb_Size': { min: 0.25, max: 0.65, curve: 0.6 },
+ 'Reverb_Color': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.15, max: 0.6, curve: 0.45 },
+ 'Echo_Mix': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Echo_Time': { min: 0.2, max: 0.7, curve: 0.55 },
+ 'Flanger_Mix': { min: 0.0, max: 0.3, curve: 0.35 },
+ 'Flanger_Time': { min: 0.2, max: 0.6, curve: 0.45 },
+ 'Flanger_Rate': { min: 0.1, max: 0.4, curve: 0.4 },
+ 'Cabinet_Drive': { min: 0.1, max: 0.35, curve: 0.35 },
+ 'Cabinet_Tilt': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'Cabinet_Mix': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'intermediate-2',
+ name: '2.2',
+ tier: 2,
+ description: 'Short envelopes, echo with rhythmic timing, flanger movement',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.08, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.1, max: 0.4, curve: 0.35 },
+ 'Env_A_Sus': { min: 0.0, max: 0.25, curve: 0.3 },
+ 'Env_A_Rel': { min: 0.1, max: 0.35, curve: 0.35 },
+ 'Env_B_Att': { min: 0.0, max: 0.05, curve: 0.3 },
+ 'Env_B_Dec_1': { min: 0.1, max: 0.35, curve: 0.35 },
+ 'SV_Flt_Cut': { min: 0.3, max: 0.9, curve: 0.55 },
+ 'SV_Flt_FM': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'Echo_Mix': { min: 0.15, max: 0.55, curve: 0.45 },
+ 'Echo_Time': { min: 0.15, max: 0.6, curve: 0.35 },
+ 'Flanger_Mix': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Flanger_Time': { min: 0.1, max: 0.5, curve: 0.45 },
+ 'Flanger_Rate': { min: 0.2, max: 0.7, curve: 0.45 },
+ 'Cabinet_Drive': { min: 0.15, max: 0.45, curve: 0.55 },
+ 'Out_Mix_To_FX': { min: 0.25, max: 0.7, curve: 0.55 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.2, curve: 0.3 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'intermediate-3',
+ name: '2.3',
+ tier: 2,
+ description: 'Long envelope times, filter FM, slow flanger, lush reverb',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.3, max: 0.85, curve: 0.6 },
+ 'Env_A_Dec_1': { min: 0.5, max: 0.9, curve: 0.6 },
+ 'Env_A_Dec_2': { min: 0.5, max: 0.85, curve: 0.6 },
+ 'Env_A_Sus': { min: 0.4, max: 0.9, curve: 0.6 },
+ 'Env_A_Rel': { min: 0.35, max: 0.65, curve: 0.55 },
+ 'Env_B_Att': { min: 0.2, max: 0.7, curve: 0.6 },
+ 'Env_B_Dec_1': { min: 0.4, max: 0.85, curve: 0.6 },
+ 'Env_B_Sus': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Env_C_Att': { min: 0.2, max: 0.6, curve: 0.55 },
+ 'SV_Flt_Cut': { min: 0.15, max: 0.8, curve: 0.4 },
+ 'SV_Flt_FM': { min: 0.25, max: 0.75, curve: 0.45 },
+ 'SV_Flt_Spread': { min: 0.2, max: 0.7, curve: 0.55 },
+ 'Reverb_Size': { min: 0.3, max: 0.65, curve: 0.55 },
+ 'Reverb_Color': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.2, max: 0.6, curve: 0.45 },
+ 'Echo_Mix': { min: 0.05, max: 0.35, curve: 0.35 },
+ 'Flanger_Mix': { min: 0.05, max: 0.35, curve: 0.35 },
+ 'Flanger_Rate': { min: 0.05, max: 0.25, curve: 0.35 },
+ 'Flanger_Time': { min: 0.2, max: 0.6, curve: 0.45 },
+ 'Out_Mix_To_FX': { min: 0.2, max: 0.6, curve: 0.5 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Cabinet_Mix': { min: 0.1, max: 0.4, curve: 0.35 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'intermediate-4',
+ name: '2.4',
+ tier: 2,
+ description: 'Cabinet drive pushed, asymmetric shaper, compressed envelope, dry',
+ active: [
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.1, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.15, max: 0.5, curve: 0.4 },
+ 'Env_A_Sus': { min: 0.1, max: 0.5, curve: 0.45 },
+ 'Env_A_Rel': { min: 0.15, max: 0.4, curve: 0.4 },
+ 'Shp_A_Drive': { min: 0.2, max: 0.55, curve: 0.6 },
+ 'Shp_A_Fold': { min: 0.2, max: 0.8, curve: 0.55 },
+ 'Shp_A_Asym': { min: 0.1, max: 0.7, curve: 0.6 },
+ 'Shp_A_Mix': { min: 0.2, max: 0.8, curve: 0.55 },
+ 'SV_Flt_Cut': { min: 0.25, max: 0.8, curve: 0.55 },
+ 'SV_Flt_Res': { min: 0.3, max: 0.75, curve: 0.55 },
+ 'Cabinet_Drive': { min: 0.3, max: 0.58, curve: 0.6 },
+ 'Cabinet_Tilt': { min: 0.2, max: 0.65, curve: 0.55 },
+ 'Cabinet_Mix': { min: 0.3, max: 0.8, curve: 0.6 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.5, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Echo_Mix': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Flanger_Mix': { min: 0.0, max: 0.1, curve: 0.3 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ // ======================================================================
+ // TIER 3 — Advanced (adds 55 params → 95 total)
+ // ======================================================================
+ {
+ id: 'advanced-1',
+ name: '3.1',
+ tier: 3,
+ description: 'Cross-oscillator PM, ring modulation, comb filter resonance, clean effects',
+ active: [
+ // Tier 1
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ // Tier 2
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ // Tier 3 additions
+ 'Env_B_BP', 'Env_B_Dec_2', 'Env_B_Gain',
+ 'Env_C_BP', 'Env_C_Dec_2', 'Env_C_Rel', 'Env_C_Sus',
+ 'Osc_A_PM_Self', 'Osc_A_PM_B', 'Osc_A_PM_FB', 'Osc_A_Phase',
+ 'Osc_B_PM_Self', 'Osc_B_PM_A', 'Osc_B_PM_FB', 'Osc_B_Phase',
+ 'Shp_A_FB_Mix', 'Shp_A_Ring_Mod',
+ 'Shp_B_Drive', 'Shp_B_Fold', 'Shp_B_Asym', 'Shp_B_Mix', 'Shp_B_FB_Mix', 'Shp_B_Ring_Mod',
+ 'Comb_Flt_Pitch', 'Comb_Flt_Decay', 'Comb_Flt_In_A_B', 'Comb_Flt_AP_Res', 'Comb_Flt_PM',
+ 'SV_Flt_In_A_B', 'SV_Flt_Comb_Mix', 'SV_Flt_Par', 'SV_Flt_FM_A_B',
+ 'Gap_Flt_Center', 'Gap_Flt_Gap', 'Gap_Flt_Res', 'Gap_Flt_Mix',
+ 'FB_Mix_Comb', 'FB_Mix_SVF', 'FB_Mix_FX', 'FB_Mix_Osc', 'FB_Mix_Lvl', 'FB_Mix_Drive',
+ 'Out_Mix_B_Lvl', 'Out_Mix_B_Pan', 'Out_Mix_Comb_Lvl',
+ 'Flanger_Feedback', 'Flanger_Stereo', 'Flanger_Phase',
+ 'Echo_Feedback', 'Echo_Stereo',
+ 'Reverb_Pre_Dly', 'Reverb_Chorus',
+ 'Cabinet_Fold', 'Cabinet_Asym', 'Cabinet_Hi_Cut', 'Cabinet_Lo_Cut', 'Cabinet_Cab_Lvl',
+ 'Unison_Detune',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.05, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.2, max: 0.6, curve: 0.35 },
+ 'Env_A_Sus': { min: 0.0, max: 0.2, curve: 0.3 },
+ 'Env_A_Rel': { min: 0.2, max: 0.55, curve: 0.4 },
+ 'Osc_A_PM_Self': { min: 0.3, max: 0.7, curve: 0.4 },
+ 'Osc_A_PM_B': { min: 0.25, max: 0.7, curve: 0.4 },
+ 'Osc_A_PM_FB': { min: 0.3, max: 0.65, curve: 0.4 },
+ 'Osc_B_PM_Self': { min: 0.3, max: 0.7, curve: 0.4 },
+ 'Osc_B_PM_A': { min: 0.25, max: 0.7, curve: 0.4 },
+ 'Osc_B_PM_FB': { min: 0.3, max: 0.65, curve: 0.4 },
+ 'Shp_A_Ring_Mod': { min: 0.0, max: 0.5, curve: 0.35 },
+ 'Shp_B_Ring_Mod': { min: 0.0, max: 0.5, curve: 0.35 },
+ 'Comb_Flt_Pitch': { min: 0.3, max: 0.7, curve: 0.45 },
+ 'Comb_Flt_Decay': { min: 0.3, max: 0.7, curve: 0.4 },
+ 'Comb_Flt_AP_Res': { min: 0.2, max: 0.65, curve: 0.4 },
+ 'SV_Flt_Cut': { min: 0.3, max: 0.85, curve: 0.55 },
+ 'Reverb_Mix': { min: 0.15, max: 0.5, curve: 0.4 },
+ 'Reverb_Size': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Reverb_Chorus': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Echo_Mix': { min: 0.0, max: 0.25, curve: 0.35 },
+ 'Flanger_Mix': { min: 0.0, max: 0.2, curve: 0.3 },
+ 'FB_Mix_Lvl': { min: 0.1, max: 0.4, curve: 0.4 },
+ 'FB_Mix_Drive': { min: 0.1, max: 0.3, curve: 0.35 },
+ 'Out_Mix_Lvl': { min: 0.2, max: 0.55, curve: 0.45 },
+ 'Out_Mix_B_Lvl': { min: 0.15, max: 0.6, curve: 0.45 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'advanced-2',
+ name: '3.2',
+ tier: 3,
+ description: 'Feedback mixer active, dual shapers, gap filter, gritty cabinet',
+ active: [
+ // Tier 1
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ // Tier 2
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ // Tier 3 additions
+ 'Env_B_BP', 'Env_B_Dec_2', 'Env_B_Gain',
+ 'Env_C_BP', 'Env_C_Dec_2', 'Env_C_Rel', 'Env_C_Sus',
+ 'Osc_A_PM_Self', 'Osc_A_PM_B', 'Osc_A_PM_FB', 'Osc_A_Phase',
+ 'Osc_B_PM_Self', 'Osc_B_PM_A', 'Osc_B_PM_FB', 'Osc_B_Phase',
+ 'Shp_A_FB_Mix', 'Shp_A_Ring_Mod',
+ 'Shp_B_Drive', 'Shp_B_Fold', 'Shp_B_Asym', 'Shp_B_Mix', 'Shp_B_FB_Mix', 'Shp_B_Ring_Mod',
+ 'Comb_Flt_Pitch', 'Comb_Flt_Decay', 'Comb_Flt_In_A_B', 'Comb_Flt_AP_Res', 'Comb_Flt_PM',
+ 'SV_Flt_In_A_B', 'SV_Flt_Comb_Mix', 'SV_Flt_Par', 'SV_Flt_FM_A_B',
+ 'Gap_Flt_Center', 'Gap_Flt_Gap', 'Gap_Flt_Res', 'Gap_Flt_Mix',
+ 'FB_Mix_Comb', 'FB_Mix_SVF', 'FB_Mix_FX', 'FB_Mix_Osc', 'FB_Mix_Lvl', 'FB_Mix_Drive',
+ 'Out_Mix_B_Lvl', 'Out_Mix_B_Pan', 'Out_Mix_Comb_Lvl',
+ 'Flanger_Feedback', 'Flanger_Stereo', 'Flanger_Phase',
+ 'Echo_Feedback', 'Echo_Stereo',
+ 'Reverb_Pre_Dly', 'Reverb_Chorus',
+ 'Cabinet_Fold', 'Cabinet_Asym', 'Cabinet_Hi_Cut', 'Cabinet_Lo_Cut', 'Cabinet_Cab_Lvl',
+ 'Unison_Detune',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.0, max: 0.15, curve: 0.3 },
+ 'Env_A_Dec_1': { min: 0.2, max: 0.6, curve: 0.4 },
+ 'Env_A_Sus': { min: 0.1, max: 0.5, curve: 0.45 },
+ 'Shp_A_Drive': { min: 0.2, max: 0.55, curve: 0.6 },
+ 'Shp_A_Asym': { min: 0.1, max: 0.65, curve: 0.6 },
+ 'Shp_B_Drive': { min: 0.15, max: 0.55, curve: 0.6 },
+ 'Shp_B_Asym': { min: 0.1, max: 0.6, curve: 0.55 },
+ 'Shp_B_FB_Mix': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Gap_Flt_Center': { min: 0.2, max: 0.8, curve: 0.45 },
+ 'Gap_Flt_Gap': { min: 0.05, max: 0.4, curve: 0.35 },
+ 'Gap_Flt_Res': { min: 0.2, max: 0.7, curve: 0.45 },
+ 'Gap_Flt_Mix': { min: 0.2, max: 0.7, curve: 0.45 },
+ 'FB_Mix_Comb': { min: 0.3, max: 0.65, curve: 0.45 },
+ 'FB_Mix_SVF': { min: 0.3, max: 0.65, curve: 0.45 },
+ 'FB_Mix_FX': { min: 0.3, max: 0.6, curve: 0.4 },
+ 'FB_Mix_Osc': { min: 0.3, max: 0.6, curve: 0.4 },
+ 'FB_Mix_Lvl': { min: 0.2, max: 0.5, curve: 0.4 },
+ 'FB_Mix_Drive': { min: 0.15, max: 0.38, curve: 0.4 },
+ 'Cabinet_Drive': { min: 0.3, max: 0.58, curve: 0.6 },
+ 'Cabinet_Fold': { min: 0.15, max: 0.5, curve: 0.55 },
+ 'Cabinet_Asym': { min: 0.1, max: 0.5, curve: 0.55 },
+ 'Cabinet_Mix': { min: 0.3, max: 0.75, curve: 0.6 },
+ 'Out_Mix_Lvl': { min: 0.18, max: 0.5, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.25, curve: 0.3 },
+ 'Echo_Mix': { min: 0.0, max: 0.2, curve: 0.3 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'advanced-3',
+ name: '3.3',
+ tier: 3,
+ description: 'Both oscillators through both filters, stereo panning, full effects',
+ active: [
+ // Tier 1
+ 'Env_A_Att', 'Env_A_Dec_1', 'Env_A_Sus', 'Env_A_Rel',
+ 'Osc_A_Fluct', 'Osc_B_Fluct',
+ 'Shp_A_Drive', 'Shp_A_Fold',
+ 'SV_Flt_Cut', 'SV_Flt_Res', 'SV_Flt_LBH',
+ 'Out_Mix_A_Lvl', 'Out_Mix_SVF_Lvl', 'Out_Mix_Lvl',
+ 'Reverb_Mix',
+ // Tier 2
+ 'Env_A_BP', 'Env_A_Dec_2', 'Env_A_Gain',
+ 'Env_B_Att', 'Env_B_Dec_1', 'Env_B_Sus', 'Env_B_Rel',
+ 'Env_C_Att', 'Env_C_Dec_1',
+ 'Shp_A_Asym', 'Shp_A_Mix',
+ 'SV_Flt_FM', 'SV_Flt_Spread',
+ 'Out_Mix_A_Pan', 'Out_Mix_To_FX',
+ 'Reverb_Size', 'Reverb_Color',
+ 'Echo_Mix', 'Echo_Time',
+ 'Flanger_Mix', 'Flanger_Time', 'Flanger_Rate',
+ 'Cabinet_Drive', 'Cabinet_Tilt', 'Cabinet_Mix',
+ // Tier 3 additions
+ 'Env_B_BP', 'Env_B_Dec_2', 'Env_B_Gain',
+ 'Env_C_BP', 'Env_C_Dec_2', 'Env_C_Rel', 'Env_C_Sus',
+ 'Osc_A_PM_Self', 'Osc_A_PM_B', 'Osc_A_PM_FB', 'Osc_A_Phase',
+ 'Osc_B_PM_Self', 'Osc_B_PM_A', 'Osc_B_PM_FB', 'Osc_B_Phase',
+ 'Shp_A_FB_Mix', 'Shp_A_Ring_Mod',
+ 'Shp_B_Drive', 'Shp_B_Fold', 'Shp_B_Asym', 'Shp_B_Mix', 'Shp_B_FB_Mix', 'Shp_B_Ring_Mod',
+ 'Comb_Flt_Pitch', 'Comb_Flt_Decay', 'Comb_Flt_In_A_B', 'Comb_Flt_AP_Res', 'Comb_Flt_PM',
+ 'SV_Flt_In_A_B', 'SV_Flt_Comb_Mix', 'SV_Flt_Par', 'SV_Flt_FM_A_B',
+ 'Gap_Flt_Center', 'Gap_Flt_Gap', 'Gap_Flt_Res', 'Gap_Flt_Mix',
+ 'FB_Mix_Comb', 'FB_Mix_SVF', 'FB_Mix_FX', 'FB_Mix_Osc', 'FB_Mix_Lvl', 'FB_Mix_Drive',
+ 'Out_Mix_B_Lvl', 'Out_Mix_B_Pan', 'Out_Mix_Comb_Lvl',
+ 'Flanger_Feedback', 'Flanger_Stereo', 'Flanger_Phase',
+ 'Echo_Feedback', 'Echo_Stereo',
+ 'Reverb_Pre_Dly', 'Reverb_Chorus',
+ 'Cabinet_Fold', 'Cabinet_Asym', 'Cabinet_Hi_Cut', 'Cabinet_Lo_Cut', 'Cabinet_Cab_Lvl',
+ 'Unison_Detune',
+ ],
+ overrides: {
+ 'Env_A_Att': { min: 0.05, max: 0.5, curve: 0.45 },
+ 'Env_B_Att': { min: 0.05, max: 0.5, curve: 0.45 },
+ 'Osc_A_PM_B': { min: 0.2, max: 0.65, curve: 0.45 },
+ 'Osc_B_PM_A': { min: 0.2, max: 0.65, curve: 0.45 },
+ 'SV_Flt_In_A_B': { min: 0.2, max: 0.8, curve: 0.5 },
+ 'Comb_Flt_In_A_B': { min: 0.2, max: 0.8, curve: 0.5 },
+ 'Out_Mix_A_Lvl': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Out_Mix_B_Lvl': { min: 0.3, max: 0.8, curve: 0.55 },
+ 'Out_Mix_A_Pan': { min: 0.15, max: 0.45, curve: 0.4 },
+ 'Out_Mix_B_Pan': { min: 0.55, max: 0.85, curve: 0.6 },
+ 'Out_Mix_Comb_Lvl': { min: 0.15, max: 0.6, curve: 0.45 },
+ 'Out_Mix_Lvl': { min: 0.18, max: 0.5, curve: 0.45 },
+ 'SV_Flt_Cut': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'SV_Flt_Spread': { min: 0.15, max: 0.75, curve: 0.55 },
+ 'Flanger_Stereo': { min: 0.2, max: 0.8, curve: 0.6 },
+ 'Echo_Stereo': { min: 0.2, max: 0.8, curve: 0.6 },
+ 'Reverb_Mix': { min: 0.1, max: 0.5, curve: 0.4 },
+ 'Echo_Mix': { min: 0.05, max: 0.4, curve: 0.4 },
+ 'Flanger_Mix': { min: 0.05, max: 0.35, curve: 0.35 },
+ 'Unison_Detune': { min: 0.0, max: 0.3, curve: 0.3 },
+ 'FB_Mix_Lvl': { min: 0.15, max: 0.45, curve: 0.4 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ // ======================================================================
+ // TIER 4 — Expert (all 126 params active)
+ // ======================================================================
+ {
+ id: 'expert-1',
+ name: '4.1',
+ tier: 4,
+ description: 'All parameters active with conservative tame-derived ranges',
+ active: null, // null means ALL params active
+ overrides: {
+ // Use tame-derived defaults for everything (no per-param overrides)
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+
+ {
+ id: 'expert-2',
+ name: '4.2',
+ tier: 4,
+ description: 'All parameters active with wider ranges for extreme exploration',
+ active: null, // all active
+ overrides: {
+ // Push ranges wider than tame defaults — halve the tame constraint
+ 'Osc_A_PM_Self': { min: 0.1, max: 0.9, curve: 0.45 },
+ 'Osc_A_PM_B': { min: 0.1, max: 0.9, curve: 0.45 },
+ 'Osc_A_PM_FB': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'Osc_B_PM_Self': { min: 0.1, max: 0.9, curve: 0.45 },
+ 'Osc_B_PM_A': { min: 0.1, max: 0.9, curve: 0.45 },
+ 'Osc_B_PM_FB': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'Shp_A_Drive': { min: 0.0, max: 0.8, curve: 0.55 },
+ 'Shp_B_Drive': { min: 0.0, max: 0.8, curve: 0.55 },
+ 'Shp_A_FB_Mix': { min: 0.0, max: 0.85, curve: 0.45 },
+ 'Shp_B_FB_Mix': { min: 0.0, max: 0.85, curve: 0.45 },
+ 'Comb_Flt_Decay': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'Comb_Flt_AP_Res': { min: 0.0, max: 0.9, curve: 0.4 },
+ 'Comb_Flt_PM': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'SV_Flt_Res': { min: 0.0, max: 0.9, curve: 0.4 },
+ 'SV_Flt_FM': { min: 0.1, max: 0.9, curve: 0.45 },
+ 'Gap_Flt_Res': { min: 0.0, max: 0.9, curve: 0.4 },
+ 'FB_Mix_Comb': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'FB_Mix_SVF': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'FB_Mix_FX': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'FB_Mix_Osc': { min: 0.15, max: 0.85, curve: 0.45 },
+ 'FB_Mix_Lvl': { min: 0.0, max: 0.65, curve: 0.4 },
+ 'FB_Mix_Drive': { min: 0.0, max: 0.5, curve: 0.4 },
+ 'Out_Mix_Lvl': { min: 0.1, max: 0.8, curve: 0.45 },
+ 'Out_Mix_Drive': { min: 0.0, max: 0.65, curve: 0.55 },
+ 'Cabinet_Drive': { min: 0.0, max: 0.75, curve: 0.55 },
+ 'Flanger_Feedback':{ min: 0.1, max: 0.9, curve: 0.45 },
+ 'Flanger_Cross_FB':{ min: 0.15, max: 0.85, curve: 0.45 },
+ 'Echo_Feedback': { min: 0.0, max: 0.85, curve: 0.4 },
+ 'Echo_Mix': { min: 0.0, max: 0.8, curve: 0.4 },
+ 'Reverb_Size': { min: 0.0, max: 0.8, curve: 0.45 },
+ 'Reverb_Mix': { min: 0.0, max: 0.8, curve: 0.4 },
+ 'Reverb_Send': { min: 0.0, max: 1.0, curve: 0.5 },
+ },
+ mutedOverrides: {},
+ groupCurves: {},
+ },
+];
+
+// Tier metadata for UI grouping
+export const PRESET_TIERS = [
+ { tier: 1, label: 'Beginner' },
+ { tier: 2, label: 'Intermediate' },
+ { tier: 3, label: 'Advanced' },
+ { tier: 4, label: 'Expert' },
+];