exponentialRampToValueAtTime Cannot Reach Zero
Short answer: An exponential ramp can't end at 0, so the browser throws an error. Ramp to a very small number, then set the gain to 0.
const g = gainNode.gain;
const now = audioCtx.currentTime;
g.setValueAtTime(g.value, now); // start from a known value
g.exponentialRampToValueAtTime(0.0001, now + 2); // tiny, not zero
g.setValueAtTime(0, now + 2); // then real silence
What you see
You write the obvious thing, a fade-out to zero, and the console shows an error where you expected silence. The wording differs between browsers. MDN's own example ramps down to 0.01 for this reason and notes that the value needs to be positive.
Why zero is impossible
An exponential fade works by multiplying. Each moment, the volume becomes a fixed fraction of what it was. Halve a number as often as you like and you never reach zero. It is like walking half the remaining distance to a wall with every step: you get very close and never touch it.
So the browser has no curve to draw from your current value down to 0, and it refuses.
The same thing from the other side
As far as I understand the spec, you also can't ramp exponentially up from 0. Nothing multiplies its way out of zero, so the value sits at 0 until the end time and then jumps. Start a fade-in from a small number such as 0.0001, or use a linear ramp for the way up.
Which fade to use
- Exponential for fade-outs. It sounds even, because we hear loudness in ratios.
- Linear for very short ramps, such as the few milliseconds at the start of a note. See Attack Time.
- setTargetAtTime when you want a smooth approach and no error. It heads toward the target and never quite arrives, which is fine for the release of a note. MDN suggests a time constant of about a third of the duration you want.
What I do
Every short sound in my project ends the same way: ramp to almost nothing, then stop the source a moment later. I came to this after the error, like everyone else.
Sources
Try it: every sound in Ocean ends with a fade like this. Listen for a click at the end of one. There shouldn't be any. It is free and starts in one click.
Open Ocean, free