How to achieve smoother fades like setTargetAtTime() instead of linear fades?
Issue
The current fade() function creates linear volume transitions that can cause audible clicks and pops, especially when fading in/out quickly. I'm looking for a way to achieve smooth exponential fades similar to Web Audio API's setTargetAtTime().
Current Behavior
// Current implementation
sound.fade(sound.volume(), 1, 200);
Desired Behavior
// I've done this in the past to create smooth audio fades
volumeGainNode.gain.setTargetAtTime(0, audioCtx.currentTime, 0.04);
Code Example
const playSound = () => {
if (!sound?.playing()) {
sound?.fade(sound.volume(), 1, 200);
setTimeout(() => {
sound?.play();
}, 100);
}
};
const pauseSound = () => {
if (sound?.playing()) {
sound?.fade(sound.volume(), 0, 200);
setTimeout(() => {
sound?.pause();
}, 100);
}
};
Question
- Is there a way to configure Howler to use exponential fading curves instead of linear?
- Would it be possible to expose the underlying Web Audio gain node so we can use
setTargetAtTime()directly? - Are there any recommended workarounds for achieving smoother fades?
Reproducible Example
https://github.com/taariqelliott/ugubhu-v1/blob/main/src/routes/About.tsx
Reproduction Steps
Setup play/pause or mute/unmute functions as so:
const playSound = () => {
if (!sound?.playing()) {
sound?.fade(sound.volume(), 1, 200);
setTimeout(() => {
sound?.play();
}, 100);
}
};
const pauseSound = () => {
if (sound?.playing()) {
sound?.fade(sound.volume(), 0, 200);
setTimeout(() => {
sound?.pause();
}, 100);
}
};
Possible Solution
No response
Context
No response
Howler.js Version
2.2.4
Affected Browser(s)/Versiuon(s)
Electron[chromium]
I was just looking at the fading code, and I noticed something interesting:
https://github.com/goldfire/howler.js/blob/a2a47933f1ffcee659e4939a65e075fa7f25706c/src/howler.core.js#L1332-L1340
If the Web Audio api is being used, the fade is done using linearRampToValueAtTime. But then it's also done via _startFadeInterval, which seems wrong. It seems like those two code paths should be exclusive (if/else).
Maybe this is contributing to the pops/clicks?