Skip to main content

Ducking

Added in 6.5.0

Ducking turns one sound down while another plays. Music drops when someone speaks and comes back when they stop. Games with dialogue need it, and so does any app with a voice-over. Without it you end up wiring fades to the start and end callbacks of every line.

In soundhub it is one call:

soundHub.duck('music', { when: 'voice', amount: 0.3 });

soundHub.play('music', { loop: true });
soundHub.play('voice'); // the music drops to 30%

Try it​

Try it

Music that steps back for a voice

Idle

Start the music, then the voice. Let go of a slider to hear the new setting.

Duck level1.00
isDuckedfalse
Voicestopped
0.25
silentno change
0.10 s
0.80 s

The duck has a gain node of its own, so the volume of the music stays 0.7 the whole time. Only the bar above moves.

Code
Your clicks show up here as soundhub calls

How it works​

  1. duck names a target and one or more triggers. Both can be a sound, a group, a stream or a variation name.
  2. The hub puts a gain node for that target between the target's sounds and the master bus.
  3. Every time a sound starts, stops, pauses, resumes, ends, mutes, unmutes or unloads, the hub checks whether a trigger can still be heard. The duck node ramps down to amount over attack seconds when one starts, and back up to 1 over release seconds after the last one stops.
target sound → pan → gain → [duck gain] → master gain → ... → output

Because the duck has its own node, it never writes to the target's volume. A fade on the music or a volume slider keeps working during a duck, and whatever you set is still there when the duck lets go.

Triggers and targets​

A name matches in several ways, so you rarely need to list sounds one by one:

NameMatches
'music'the sound or stream with that id
'laser'every overlapping instance, 'laser:1', 'laser:2' and so on
'ui'every sprite cut from the sound 'ui', such as 'ui_click'
'dialogue'every sound in the group 'dialogue', including ones that join later
'footstep'every take behind the variation name 'footstep'

A trigger only counts while you can hear it: a paused or muted trigger does not duck. Three overlapping barks from a dog keep the music down until the third one ends.

Events​

Two events tell you when a duck moves. Both carry the target as soundId and the level as volume.

EventFires whenvolume
duck_startedthe first trigger startsthe amount
duck_endedthe last trigger stops, or unduck removes a duck that was down1
import { SoundEventsEnum } from 'soundhub';

soundHub.addEventListener(SoundEventsEnum.DUCK_STARTED, (event) => {
subtitles.show();
}, { soundId: 'music' });

soundHub.addEventListener(SoundEventsEnum.DUCK_ENDED, () => {
subtitles.hide();
}, { soundId: 'music' });

The methods​

MethodWhat it does
duckSet up a duck, or replace the options of an existing one. Returns a function that removes it.
unduckRemove a duck.
isDuckedWhether a target is down right now.
getDuckLevelThe level it is at right now, also during a ramp.

The options are described on the DuckOptions page.

Good to know​

  • One sound goes through one duck. When a sound matches two targets, say it is 'music' and also in the group 'background', only the duck set up first applies to it.
  • destroy removes every duck.
  • The Howler layer shares the hub, so a duck works on Howls too. See Migrating from Howler.js.