loadStream
Load a long audio file as a stream instead of decoding it into memory. Reach for it for podcasts, audiobooks, radio and hour-long background music.
loadStream(id: string, url: string, options?: StreamOptions): Promise<void>;
loadSound fetches the whole file and decodes it into an AudioBuffer. Sprites, overlapping instances and sample-accurate scheduling need that, but it is the wrong shape for a podcast. An hour of stereo audio at 44.1 kHz takes about 1.3 GB once decoded, and nothing plays until the download and the decode have both finished.
loadStream takes the other route. An HTMLAudioElement fetches the file as it plays, and a MediaElementAudioSourceNode puts the result into the same audio graph your buffered sounds use. Master volume, master pan and the master limiter still apply. With the default preload: 'metadata', only the duration and headers are fetched before the promise resolves.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
id | string | required | The id you will address the stream by, like any other sound. |
url | string | required | Url of the audio file. |
options | StreamOptions | {} | Initial volume, pan, loop, playback rate, start time, progress tracking and preload strategy. |
StreamOptions
| Property | Type | Default | Description |
|---|---|---|---|
volume | number | config defaultVolume | 0 to 1. |
pan | number | config defaultPan | -1 (left) to 1 (right). Stereo panning only. |
loop | boolean | config loopSounds | Start over at the end. |
playbackRate | number | config defaultPlaybackRate | 0.25 to 4. |
startTime | number | config defaultStartTime | Second to start from when play() starts the stream from stopped. |
trackProgress | boolean | config trackProgress | Dispatch progress events while playing. |
preload | 'none' | 'metadata' | 'auto' | 'metadata' | How much the browser fetches before playback. Leave it on metadata for long files. |
Returns
Promise<void>: resolves once the browser has the file's metadata. It rejects when the element reports a load error.
Example
import { SoundHub, SoundEventsEnum } from 'soundhub';
const soundHub = new SoundHub();
await soundHub.loadStream('episode-42', '/audio/episode-42.mp3', {
volume: 0.8,
trackProgress: true,
});
soundHub.play('episode-42');
// The usual methods work on a stream
soundHub.setPlaybackRate('episode-42', 1.5);
soundHub.seek('episode-42', 1800); // half an hour in
soundHub.setSoundVolume('episode-42', 0.4);
soundHub.addEventListener(SoundEventsEnum.PROGRESS, (event) => {
progressBar.value = event.progressInfo!.progress;
}, { soundId: 'episode-42' });
Drawing a loading bar
getStreamElement hands you the media element, which knows how much of the file has arrived:
const element = soundHub.getStreamElement('episode-42');
if (element?.buffered.length) {
const loadedUpTo = element.buffered.end(element.buffered.length - 1);
bufferBar.style.width = `${(loadedUpTo / soundHub.getDuration('episode-42')) * 100}%`;
}
Try it
The demo streams a short track so the page stays quick to load, but the code path is the same for a two-hour recording. The darker bar behind the playhead shows how much of the file has been downloaded so far.
Stream a long file
IdleThe lighter bar is what the browser has downloaded so far. A stream starts playing before the file is complete.
What a stream cannot do
Anything that needs random access to the samples, because they are never all in memory at once:
| Feature | On a stream |
|---|---|
setSoundSprite and playSprite | Throws an error. Use loadSound for sprite sheets. |
overlap | Not available. One id is one playing stream. |
seamlessLoop | Not available. The browser handles looping. |
loop_completed event | Never fires, and maxLoops has nothing to count. |
setSpatialPosition | Does nothing. A stream has a stereo panner only. |
Playback, seeking, volume, mute, fades, stereo panning, playback rate, looping, getSoundState and progress events behave as they do for a buffered sound.
Good to know
- An id that already exists, as a sound or a stream, is skipped and the promise resolves without loading anything.
- A
loadedevent with thedurationis dispatched when the stream is ready. There is noloadingevent, andgetLoadStateonly reportsloadedonce the promise has resolved. - The media element gets
crossOriginfrom yourSoundHubConfig, and'anonymous'when that is not set. A file from another domain has to sendAccess-Control-Allow-Origin. Without it the browser will not route the audio through Web Audio, and you hear silence rather than get an error. - A live stream or a file without a known length reports a duration of
0, so a progress bar does not divide byInfinity. play()on a stream returnsundefinedrather than aSound. Errors that happen during playback are stored forgetLastErrorand dispatched aserrorevents.
See also
isStream: check whether an id is a stream.getStreamElement: the media element behind a stream.setMediaSession: put a stream on the lock screen.unloadSound: stop a stream and cancel its download.