Un synth monofonico suona una nota per volta. Per suonare accordi serve la polifonia: ogni nota è una "voce" indipendente, con il suo oscillatore e il suo inviluppo.

const voci = new Map();

function noteOn(midi) {
  const freq = 440 * Math.pow(2, (midi - 69) / 12); // MIDI -> Hz
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  osc.frequency.value = freq;
  gain.gain.setValueAtTime(0, ctx.currentTime);
  gain.gain.linearRampToValueAtTime(0.3, ctx.currentTime + 0.02);
  osc.connect(gain).connect(ctx.destination);
  osc.start();
  voci.set(midi, { osc, gain }); // ricorda la voce per spegnerla
}

function noteOff(midi) {
  const v = voci.get(midi);
  if (!v) return;
  v.gain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.1);
  v.osc.stop(ctx.currentTime + 0.1);
  voci.delete(midi);
}

Il cuore è la gestione delle voci: una Map che associa ogni nota premuta alla sua coppia oscillatore/gain, così a noteOff sai quale spegnere. La formula 440 * 2^((midi-69)/12) converte il numero di nota MIDI in frequenza.

Collega tutto alla Web MIDI API e hai un synth polifonico suonabile con una tastiera vera. Da qui aggiungi filtri, detune, più oscillatori per voce: la strada verso un vero strumento software.