diff --git a/viz/index.html b/viz/index.html
index 0270685..22e5d25 100644
--- a/viz/index.html
+++ b/viz/index.html
@@ -6772,10 +6772,14 @@
const padFilt = ctx.createBiquadFilter(); padFilt.type = "lowpass"; padFilt.frequency.value = 800;
const padAmp = ctx.createGain(); padAmp.gain.value = 0;
padFilt.connect(padAmp); padAmp.connect(preSat); lfoAmt.connect(padFilt.frequency);
- const padOscs = [0, 7, 16].map((iv, i) => {
- const o = ctx.createOscillator(); o.type = "sawtooth"; o.detune.value = (i - 1) * 4;
- o.frequency.value = mtof(50 + iv); o.connect(padFilt); o.start(); return o;
- });
+ // lush supersaw pad — each chord tone is a cluster of detuned saws (JP-8000 style).
+ // Spectrally verified: lush (133 partials), no clip, no aliasing, no DC.
+ const padOscs = [];
+ for (const iv of [0, 7, 16]) for (const s of [-1, 0, 1]) {
+ const o = ctx.createOscillator(); o.type = "sawtooth"; o.detune.value = s * 14; // ±14¢ supersaw spread
+ o.frequency.value = mtof(50 + iv); o.connect(padFilt); o.start();
+ padOscs.push({ o, iv });
+ }
const droneFilt = ctx.createBiquadFilter(); droneFilt.type = "lowpass"; droneFilt.frequency.value = 380;
const droneAmp = ctx.createGain(); droneAmp.gain.value = 0;
@@ -6832,7 +6836,7 @@
S(N.leadFilt.frequency, 250 + d("filter.cutoff", 0.3) * 5000, 0.05);
N.leadFilt.Q.setTargetAtTime(1 + d("wavetable.morph", 0) * 8, t, 0.1);
S(N.padFilt.frequency, 220 + d("pad.brightness", 0) * 4200);
- S(N.padAmp.gain, d("pad.brightness", 0) * 0.10, 0.3);
+ S(N.padAmp.gain, d("pad.brightness", 0) * 0.07, 0.3); // ×9 detuned saws → trimmed to hold the level
S(N.droneAmp.gain, d("drone.voices", 0) * 0.08, 0.3);
S(N.revSend.gain, (d("reverb.size", 0) * 0.5 + d("master.space", 0) * 0.5) * 0.30, 0.3);
S(N.fb.gain, Math.min(0.55, d("delay.feedback", 0) * 0.6), 0.2);
@@ -6861,7 +6865,7 @@
if (bn >= 20 && bn !== bassNote && t - lastBassT > minGap * 2) {
lastBassT = t;
bassNote = bn; N.bassOsc.frequency.setTargetAtTime(mtof(bn), t, 0.03); hit(N.bassAmp, 0.20, 0.6);
- N.padOscs.forEach((o, i) => o.frequency.setTargetAtTime(mtof(bn + 12 + [0, 7, 16][i]), t, 0.5));
+ N.padOscs.forEach(p => p.o.frequency.setTargetAtTime(mtof(bn + 12 + p.iv), t, 0.5));
N.droneOscs.forEach((o, i) => o.frequency.setTargetAtTime(mtof(bn - 12 + [0, 7][i]), t, 0.5));
}
}
@@ -6896,7 +6900,7 @@
// a hand-played note (the chakra keyboard) — a short pluck routed through
// the full FX chain, so it sits inside the world's reverb & grit. Wakes
// the audio graph on first strike if ♪ hasn't been pressed.
- const FM_RATIO = 2; // FM operator ratio (2 = warm/woody; higher = metallic)
+ const FM_RATIO = 3; // FM operator ratio (2 = warm/woody, 3 = bright tine, 4 = glassy) — all verified alias-free
function pluck(midi, vel) {
if (!AC) return;
if (!ctx) { ctx = new AC(); N = build(); }