PlayOptions
Try it
Edit the options below and press play. Every field of PlayOptions is editable, so you can
hear directly what each one does.
PlayOptions Configuration
Edit the JSON below to customize playback options
playbackRate or volume. Adding "seamlessLoop": true loops without a gap, and then maxLoops no longer applies: the sound runs until you stop it.The interface
export interface PlayOptions {
createNewInstance?: boolean; // Create a new instance of the sound when playing it. By default this is false. This is useful when you want to play the same sound multiple times simultaneously.
duration?:number; // in seconds
fadeInDuration?: number; // in seconds
fadeInStartVolume?: number; // 0 to 1
fadeOutDuration?: number; // in seconds, when you play a sound it will immidiately start fading out
fadeOutEndVolume?: number; // 0 to 1
fadeOutBeforeEndDuration?: number; // in seconds, fade out before the sound ends
groupId?: string; // Group ID for the sounds that will be in this group.
isSeeking?: boolean; // used internally for the seek method
loop?: boolean; // default: false
maxLoops?: number; // -1 for infinte, number > 0 for specific number of loops
pan?: number; // -1 (left) to 1 (right)
panSpatialPosition?: { x: number; y: number; z: number }; // If you want to use 3D panning you must also set panType to SoundPanType.Spatial
panType?: SoundPanType; // 'stereo' or 'spatial' (default is 'stereo')
pauseAtDurationReached?: boolean; // This will only work if you set the duration and if that duration is reached it will pause. Note: Loop must be false.
playbackRate?: number; // 0.5 to 4 (normal speed is 1)
seamlessLoop?: boolean; // Loop inside the audio graph instead of restarting the source, so there is no gap between iterations. Requires loop: true. The loop then never ends by itself, so maxLoops is ignored and no 'loop-completed' event is dispatched.
startTime?: number; // in seconds
trackProgress?: boolean; // Track progress of the sound playback. This will keep track of the process and will dispatch the 'progress' event. This is useful when you want to show the progress of the sound playback.
volume?: number; // 0 to 1
}
You can use the PlayOptions to pass to the play() method.
Basic PlayOptions Example
The most common use case is setting basic playback options like volume, pan, and playback rate:
import { SoundHub, type PlayOptions } from 'soundhub';
const soundHub = new SoundHub();
await soundHub.loadSound('music', '/audio/music.mp3');
soundHub.play('music', {
volume: 0.8,
pan: 0.3,
playbackRate: 1.0,
startTime: 0,
});
// The sound plays at 80% volume, slightly panned to the right,
// at normal speed, starting from the beginning.
Fade In / Fade Out
You can smoothly fade a sound in when it starts playing and/or fade it out:
const soundHub = new SoundHub();
await soundHub.loadSound('voice', '/audio/voice.mp3');
// Fade in over 2 seconds, fade out over 3 seconds
soundHub.play('voice', {
volume: 1.0,
// Fade in settings
fadeInDuration: 2,
fadeInStartVolume: 0, // Start from silence
// Fade out settings
fadeOutDuration: 3,
fadeOutEndVolume: 0, // Fade to silence
});
// The sound fades in from 0 to full volume over 2 seconds,
// then immediately starts fading out over 3 seconds until silent.
Fade Out Before Sound Ends
If you want a natural fade-out just before a sound finishes playing (not immediately after start):
const soundHub = new SoundHub();
await soundHub.loadSound('jingle', '/audio/jingle.mp3');
soundHub.play('jingle', {
fadeOutBeforeEndDuration: 4, // Fade out the last 4 seconds
});
// The sound plays at full volume, then fades out smoothly
// during the final 4 seconds of playback.
Looping with maxLoops
Play a sound repeatedly for a specific number of times, or infinitely:
const soundHub = new SoundHub();
await soundHub.loadSound('bgm', '/audio/background.mp3');
// Loop 3 times then stop
soundHub.play('bgm', {
loop: true,
maxLoops: 3,
});
// The sound plays 3 times in total and then automatically stops.
// Infinite looping (plays until manually stopped)
soundHub.play('bgm', {
loop: true,
maxLoops: -1, // -1 means infinite
});
// Later, stop it manually:
soundHub.stop('bgm');
// The sound loops forever until stop() is called.
Seamless looping
Added in 5.9.0
A normal loop restarts the source node when the buffer ends, and that restart always leaves a
small gap. Noise hides it, a sustained tone does not: on a drone, a hum or a musical bed you
hear the sound stop and start again on every lap. With seamlessLoop the source node loops the
buffer itself, so there is no gap at all.
const soundHub = new SoundHub();
await soundHub.loadSound('drone', '/audio/drone.wav');
soundHub.play('drone', {
loop: true,
seamlessLoop: true,
volume: 0.4,
});
// The buffer repeats inside the audio graph until you stop it.
soundHub.stop('drone');
Hear the difference
The same twelve second pad, looped both ways:
The trade-off is that such a loop never ends by itself:
maxLoopsis ignored, because there is no iteration boundary for the library to count- no
loop-completedevent is dispatched, for the same reason loop: trueis required;seamlessLoopon its own does nothing
So reach for it on beds, drones and anything tonal, and keep the normal loop when you need the loop count or the event.
Two things decide whether a loop really is inaudible. The audio has to line up at its own seam, which is a property of the file, not of the player. And the file should not be an MP3: MP3 carries encoder padding at both ends, and playing that padding puts a hole in the loop no matter how the loop is driven. Use WAV, OGG or FLAC for material that has to loop cleanly.
Duration and pauseAtDurationReached
Play a sound for a specific duration and automatically pause when that duration is reached:
const soundHub = new SoundHub();
await soundHub.loadSound('alert', '/audio/alert.mp3');
// Play only the first 5 seconds, then pause
soundHub.play('alert', {
duration: 5,
pauseAtDurationReached: true,
loop: false, // Must be false for pauseAtDurationReached to work
});
// After 5 seconds, the sound pauses automatically.
// Resume it later if needed:
soundHub.resume('alert');
Progress Tracking
Track the playback progress of a sound and listen for progress events:
const soundHub = new SoundHub();
await soundHub.loadSound('podcast', '/audio/podcast.mp3');
soundHub.play('podcast', {
trackProgress: true, // Enable progress tracking for this sound
});
// Listen for progress updates
soundHub.addEventListener('progress', (event) => {
console.log(`Progress: ${event.progressPercentage}%`);
console.log(`Current time: ${event.currentTime}s`);
console.log(`Duration: ${event.duration}s`);
});
// The progress event fires at regular intervals (default: every 250ms).
// You can configure the interval with setProgressUpdateInterval(ms).
Spatial Audio (3D Panning)
Use 3D spatial positioning for immersive audio experiences:
import { SoundHub, SoundPanType, type PlayOptions } from 'soundhub';
const soundHub = new SoundHub();
await soundHub.loadSound('explosion', '/audio/explosion.mp3');
soundHub.play('explosion', {
panType: SoundPanType.Spatial, // Enable 3D spatial audio
panSpatialPosition: { // Position in 3D space
x: 5,
y: 0,
z: -10,
},
volume: 0.9,
});
// The explosion sound is positioned 5 units to the right,
// at ground level, and 10 units behind the listener.
For comparison, stereo panning only requires a single pan value:
soundHub.play('explosion', {
panType: SoundPanType.Stereo, // This is the default if not specified
pan: -0.8, // -1 (full left) to 1 (full right)
});
// The explosion sound is panned hard to the left speaker.
Playing Multiple Instances (createNewInstance)
Play the same sound multiple times simultaneously, creating independent instances:
const soundHub = new SoundHub();
await soundHub.loadSound('coin', '/audio/coin.mp3');
// First instance
soundHub.play('coin', {
createNewInstance: false, // Default: uses the existing instance (re-plays it)
playbackRate: 1.0,
volume: 0.8,
pan: -0.5,
});
// Wait a moment, then play another instance while the first is still playing
setTimeout(() => {
soundHub.play('coin', {
createNewInstance: true, // Creates a new independent instance
playbackRate: 1.5, // Higher pitch
volume: 0.6,
pan: 0.5, // Different pan position
});
}, 500);
// Now two instances of 'coin' play simultaneously,
// each with their own playback rate, volume, and pan.
Sound Groups
Assign a sound to a group when playing it:
import { SoundHub, type PlayOptions } from 'soundhub';
const soundHub = new SoundHub();
await soundHub.loadSounds([
{ id: 'sfx1', url: '/audio/sfx1.mp3' },
{ id: 'sfx2', url: '/audio/sfx2.mp3' },
{ id: 'bgm', url: '/audio/music.mp3' },
]);
// Play sounds and assign them to a group
soundHub.play('sfx1', {
groupId: 'sfx',
volume: 0.7,
});
soundHub.play('sfx2', {
groupId: 'sfx',
volume: 0.5,
});
soundHub.play('bgm', {
groupId: 'music',
volume: 0.8,
loop: true,
});
// Stop all sounds in the 'sfx' group at once
const sfxGroup = soundHub.getGroup('sfx');
if (sfxGroup) {
sfxGroup.sounds.forEach(soundId => {
soundHub.stop(soundId);
});
}
// Or pause the entire music group
const musicGroup = soundHub.getGroup('music');
soundHub.pauseAllSounds(); // Pauses all sounds regardless of group
Complete Practical Example
A full example combining multiple PlayOptions features together:
import { SoundHub, SoundPanType, SoundEventsEnum, type PlayOptions } from 'soundhub';
const soundHub = new SoundHub();
async function playCinematicSound() {
await soundHub.loadSound('cinematic', '/audio/cinematic-intro.mp3');
// Play with advanced options
soundHub.play('cinematic', {
volume: 0.85,
pan: 0,
playbackRate: 1.0,
loop: false,
trackProgress: true,
// Fade in over 3 seconds starting from silence
fadeInDuration: 3,
fadeInStartVolume: 0,
// Fade out during the last 5 seconds
fadeOutBeforeEndDuration: 5,
});
// Set up duration display using progress tracking
soundHub.addEventListener(SoundEventsEnum.PROGRESS, (event) => {
if (event.soundId === 'cinematic') {
const progressBar = '█'.repeat(Math.round(event.progressPercentage / 10));
const emptyBar = '░'.repeat(10 - Math.round(event.progressPercentage / 10));
console.log(`[${progressBar}${emptyBar}] ${event.progressPercentage.toFixed(1)}%`);
}
});
// Listen for when the sound finishes
soundHub.addEventListener(SoundEventsEnum.FINISHED, (event) => {
if (event.soundId === 'cinematic') {
console.log('Cinematic playback finished.');
}
});
}
playCinematicSound();
Related Methods
play- Play a sound with PlayOptionsgetSoundState- Get the current state of a soundSoundHubConfig- Configure default values for the sound manager