Skip to main content

SoundState

The SoundStateInfo interface provides complete state information about a sound at a given moment. Use getSoundState() to retrieve the current state of any loaded sound.

Try it

Play, pause and stop the sound and watch every field of SoundStateInfo update live.

Sound State
stopped
isPlaying: falseisPaused: falseisStopped: true
Try it: Use the buttons to change the helicopter sound's state. Watch the status update in real time — isPlaying, isPaused, and isStopped are mutually exclusive.
export interface SoundStateInfo {
progress: number; // Playback position as a ratio from 0 to 1
startTime: number; // Context time the sound started, in seconds
currentTime: number; // Current position in seconds, adjusted for playback rate
elapsedTime: number; // Time played in seconds, adjusted for playback rate
adjustedElapsedTime: number; // Raw elapsed time, not adjusted for playback rate
duration: number; // Duration in seconds, adjusted for playback rate
rawDuration: number | null; // Duration of the buffer itself, in seconds
playbackRate: number | null; // Current playback rate
state: SoundState; // 'playing' | 'paused' | 'stopped'
volume: number; // Current volume, 0 to 1
pan: number; // Current stereo pan, -1 (left) to 1 (right)
panSpatialPosition: { x: number; y: number; z: number }; // Current 3D position
}

The state field uses the SoundState enum:

export enum SoundState {
Playing = "playing",
Paused = "paused",
Stopped = "stopped",
}
Adjusted versus raw time

Several fields come in both forms because changing the playback rate changes how long a sound takes to play. duration and currentTime are adjusted for the current rate, which is what you want for a progress bar. rawDuration and adjustedElapsedTime are the untouched buffer values. At the default rate of 1 they are identical.

Getting Sound State

const mySoundHub = new SoundHub();

// Load and play a sound
await mySoundHub.loadSound('piano', '/audio/piano.mp3');
mySoundHub.play('piano', { volume: 0.7, pan: -0.5 });

// Get the current state
const state = mySoundHub.getSoundState('piano');

console.log('State:', state.state); // 'playing'
console.log('Volume:', state.volume); // 0.7
console.log('Pan:', state.pan); // -0.5
console.log('Current time:', state.currentTime);
console.log('Duration:', state.duration);
console.log('Progress:', state.progress); // 0 to 1

Asking for a sound that is not loaded returns a neutral, stopped state rather than throwing, so it is safe to call from a render loop:

const state = mySoundHub.getSoundState('does-not-exist');
console.log(state.state); // 'stopped'
console.log(state.duration); // 0

Checking State with Helper Methods

SoundStateInfo carries a single state field rather than separate booleans. For simple checks the dedicated methods read more clearly:

const mySoundHub = new SoundHub();
await mySoundHub.loadSound('sfx', '/audio/sfx.mp3');

if (mySoundHub.isPlaying('sfx')) {
console.log('Sound is currently playing');
}

if (mySoundHub.isPaused('sfx')) {
console.log('Sound is paused');
}

if (mySoundHub.isStopped('sfx')) {
console.log('Sound is stopped');
}

if (mySoundHub.isSoundLoaded('sfx')) {
console.log('Sound buffer is loaded');
}

Or compare against the enum directly:

import { SoundState } from 'soundhub';

const state = mySoundHub.getSoundState('sfx');

if (state.state === SoundState.Playing) {
console.log('Playing');
}

Building a Progress Bar

progress is already a 0 to 1 ratio, so it maps straight onto a bar:

const mySoundHub = new SoundHub();
await mySoundHub.loadSound('song', '/audio/song.mp3');
mySoundHub.play('song', { trackProgress: true });

function render() {
const state = mySoundHub.getSoundState('song');

bar.style.width = `${state.progress * 100}%`;
label.textContent = `${state.currentTime.toFixed(1)}s / ${state.duration.toFixed(1)}s`;

if (state.state === SoundState.Playing) {
requestAnimationFrame(render);
}
}

render();

Using SoundState with Multiple Instances

When using createNewInstance: true, each instance is registered under its own id and has its own state:

const mySoundHub = new SoundHub();
await mySoundHub.loadSound('laser', '/audio/laser.mp3');

// Play multiple instances
mySoundHub.play('laser', { createNewInstance: true, volume: 0.5 });
mySoundHub.play('laser', { createNewInstance: true, volume: 1.0, pan: 0.8 });

// Instance ids look like 'laser:1', 'laser:2', ...
mySoundHub.getSoundIds().forEach(id => {
const state = mySoundHub.getSoundState(id);
console.log(`${id}: ${state.state}, volume=${state.volume}`);
});

Reacting to State Changes with Events

const mySoundHub = new SoundHub();
await mySoundHub.loadSound('ambient', '/audio/ambient.mp3');

mySoundHub.addEventListener(SoundEventsEnum.PLAYBACK_RATE_CHANGED, (event) => {
const state = mySoundHub.getSoundState(event.soundId!);
console.log('New playback rate:', state.playbackRate);
});

mySoundHub.addEventListener(SoundEventsEnum.VOLUME_CHANGED, (event) => {
const state = mySoundHub.getSoundState(event.soundId!);
console.log('New volume:', state.volume);
});