Sound manager for the web · TypeScript · Web Audio API

A JavaScript audio library for web apps and games.

soundhub loads each sound once and keeps track of it for you. Play, fade, group, sprite, stream and place sounds in 3D, and follow every change on one typed event bus.

Console idle
−∞ Rain
−∞ Birds
−∞ Brook
−∞ Crickets
80%
C
waiting for the first eventbus

Four nature recordings from the demos, loaded and played by the real soundhub. Drag a strip to set its volume with setSoundVolume(). The waves behind this panel read the hub's master output.

Bundle23 KB
Typed events40
Dependencies0
Downloads since 20248,000+

Works with

No adapter needed. Each guide comes with an example you can open in StackBlitz.

Hear the code

A few lines each. Press Run.

Pick a recipe and run it. The code on the right is the code that plays, through a real hub on this page. The line under it is the master output, and the log prints the events the hub sends back.

    duck.ts
    Events

      Press Run to hear it.

      What it does

      The basics you expect, plus the bookkeeping you would write yourself.

      One event bus

      40 typed events on one bus, filtered per sound or per overlapping instance.

      hub.addEventListener(ENDED, next, { soundId: 'music' })

      Overlap with a voice limit

      Every shot gets its own instance. A full group stops its oldest voice instead of piling up.

      hub.createSoundGroup('lasers', { maxInstances: 8 })

      Sprites

      One file, many sounds, each cut into its own buffer so it starts on the exact sample.

      hub.playSprite('ui', 'click')

      3D sound and a moving listener

      Place sounds around the ear, or move the ear through the scene for a first-person camera.

      hub.setListenerPosition(player.x, 0, player.z)

      Streams for long files

      An hour of audio plays without decoding it first, and still runs through the panner and the limiter.

      await hub.loadStream('episode', url)

      Lock screen and limiter

      Artwork and media keys on the lock screen, and a limiter so many sounds at once cannot clip.

      hub.setMediaSession('episode', { title })

      Quick start

      Install it, load a sound, play it.

      Install soundhub from npm, or load it from a CDN without a build step. The same code runs in TypeScript and in plain JavaScript.

      // npm install soundhub
      import { SoundHub, SoundEventsEnum } from 'soundhub';
      
      const hub = new SoundHub({ masterLimiter: true });
      
      await hub.loadSounds([
        { id: 'music', url: '/audio/theme.mp3' },
        { id: 'laser', url: '/audio/laser.wav' },
      ]);
      
      hub.play('music', { loop: true, fadeInDuration: 2 });
      hub.play('laser', { overlap: true });
      
      // One bus, one place to react
      hub.addEventListener(SoundEventsEnum.PROGRESS, (e) => {
        seekBar.value = e.state!.progress;
      }, { soundId: 'music' });

      Compared with Howler.js

      Coming from Howler.js?

      Change one import and your Howl code runs on soundhub. From there you can use what Howler does not have: music that ducks under a voice, footsteps that vary, interface sounds without files, and a hub that knows the state of every sound.

      howler → soundhub
      // was: import { Howl, Howler } from 'howler';
      import { Howl, Howler } from 'soundhub/howler';
      
      // The Howler code you have keeps running
      const music = new Howl({ src: ['theme.mp3'], loop: true });
      const voice = new Howl({ src: ['line.mp3'] });
      music.play();
      
      // What Howler cannot do is on Howler.hub
      Howler.hub.duck(music.soundhubId, {
        when: voice.soundhubId,
        amount: 0.3,
      });
      voice.play();   // the music steps back
      soundhub compared with Howler.js 2.2.4
      FeaturesoundhubHowler.js 2.2.4
      EventsOne typed bus, 40 types, a filter per listenerCallbacks per Howl
      ProgressFull state on the eventPoll seek() yourself
      Long filesloadStream, still in the Web Audio graphhtml5: true, outside it
      3D on long filesYesNo, HTML5 mode skips the panner
      Voice limitsGroups with maxInstancesNo cap on concurrent instances
      LimitermasterLimiter: trueBuild it on Howler.ctx
      Duckingduck('music', { when: 'voice' })Fade by hand in onplay and onend
      VariationscreateVariations, with a pitch and volume spreadPick a random sprite yourself
      Sounds without filessoundhub/ui, and addBuffer for your ownEvery sound needs a url

      Where it comes from

      Built by someone who has been doing this since Flash.

      My first sound engine was in ActionScript 3.0. This one went on npm in December 2024 as sound-manager-ts, and became soundhub in August 2026 with the same code under a new name.

      1. Flash eraA sound engine in ActionScript 3.0
      2. 19 Dec 2024sound-manager-ts 1.0.0 on npm
      3. 2025 to 2026More than fifty releases, over 8,000 downloads
      4. 29 Aug 2026Renamed to soundhub, version 6.0.0

      Questions about using a sound manager

      What is a sound manager for the web?

      A sound manager is the layer between your code and the browser audio system. It loads audio files once, keeps track of every sound that is playing, and gives you one place to start, stop, fade, pan or position them. Without one you end up writing that bookkeeping yourself in every project.

      Can I use soundhub.js as a sound engine for a browser game?

      Yes. Each play call can create its own instance, so overlapping shots and explosions do not cut each other off, and sound groups let you cap how many instances a category may hold at once. Sound sprites keep dozens of short effects in a single file, which is the usual approach for web games.

      How is it different from using the HTML audio element?

      The audio element plays one file at a time and gives you no control over panning, spatial position or grouping. soundhub.js runs on the Web Audio API, so the same sound can play many times at once, each with its own volume, pan and 3D position.

      Does the sound player work on mobile browsers?

      Yes. Mobile browsers keep audio suspended until the user interacts with the page, and the library resumes the audio context on that first gesture. It also mutes when the tab goes to the background and resumes on focus, which is what mobile users expect.

      Is soundhub.js an alternative to Howler.js?

      Yes. Change the import to soundhub/howler and existing Howl code runs on soundhub. Beyond what Howler does, soundhub ducks music under a voice, gives one name to several takes of a sound, ships interface sounds that need no files, keeps the state of every sound on one typed event bus, caps voices with sound groups, and keeps long streamed files inside the Web Audio graph, so 3D panning still works on them.

      How large is the library and does it have dependencies?

      It is around 23 KB gzipped and has zero runtime dependencies. It ships as an ES module, a CommonJS build and a UMD build, with types for every TypeScript setup.