Se colleghi un oscillatore direttamente alle casse, senti un "click" fastidioso all'accensione e allo spegnimento. Serve un GainNode per controllare il volume nel tempo: l'inviluppo.

const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);

const now = ctx.currentTime;
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(1, now + 0.02); // attacco
gain.gain.linearRampToValueAtTime(0, now + 0.5);  // rilascio
osc.start(now);

Invece di accendere e spegnere di colpo, fai salire e scendere il volume con delle rampe: è l'inviluppo di ampiezza (il classico ADSR: Attack, Decay, Sustain, Release).

È la differenza tra un beep da sveglia e una nota di pianoforte. Il timbro non sta solo nell'onda: sta anche in come il suono nasce e muore.