“Just add sound to your web app” sounds like a one-liner, until you realize the three libraries that dominate the search results solve three completely different problems. Howler.js (25,336 stars) plays audio reliably in every browser. Tone.js (14,705 stars) builds synthesizers and interactive music. Wavesurfer.js (10,384 stars) renders waveforms and builds audio players with timelines. Teams keep picking the wrong one because they search for “JavaScript audio library” when they actually need one specific thing — and then fight the API for weeks.
TL;DR — Quick Verdict: If you need to play sound effects, music, or streams reliably across browsers, mobile included, choose Howler.js — it is a battle-tested playback engine with Web Audio + HTML5 fallback, sprites, and spatial audio in 7kb gzipped. If you are building interactive music, instruments, or generative audio — think synths, sequencers, or a game soundtrack that reacts to gameplay — choose Tone.js; it is a DAW-grade framework with a global transport and prebuilt synths. If you need waveform visualization, audio editing UI, or a podcast/player front-end with a timeline, choose Wavesurfer.js. These are not competing products; they are three layers of the same stack, and teams that realize that can combine them instead of replacing one with the other.
Feature Comparison: Howler.js vs Tone.js vs Wavesurfer.js
| Feature | Howler.js | Tone.js | Wavesurfer.js |
|---|---|---|---|
| GitHub stars | 25,336 | 14,705 | 10,384 |
| Last push | 2025-11-23 (stable/maintenance) | 2026-08-21 | 2026-08-21 |
| License | MIT | MIT | BSD-3-Clause |
| Core purpose | Audio playback engine | Music synthesis framework | Waveform renderer + player |
| Bundle size | ~7kb gzipped (core) | ~130kb+ (framework) | ~90kb (with plugins) |
| Web Audio API | Default, with HTML5 Audio fallback | Native Web Audio only | Native Web Audio only |
| Browser autoplay/mobile handling | Excellent (handles edge cases) | Requires user gesture to start context | Requires user gesture |
| Sound sprites | Yes, first-class | Manual scheduling | Via Regions plugin |
| Spatial/3D audio | Yes (howler.spatial plugin) | Yes (Panner3D) | No |
| Synths/effects | No | Yes (Synth, PolySynth, reverb, delay…) | No |
| Transport/sequencer | No | Yes (DAW-style global transport) | No |
| Waveform visualization | No | No | Yes, core feature |
| Plugins/regions/timeline | No | No | Yes (Regions, Timeline, Spectrogram) |
| TypeScript types | Included | Included (TS-first) | Included |
Use-Case Decision Matrix
| Use Case | Recommendation | Why |
|---|---|---|
| Game sound effects and background music | Howler.js | Sprites, volume control per group, reliable mobile playback, tiny bundle |
| Podcast/web player with seek bar and timeline | Wavesurfer.js | Waveform + Regions + Timeline plugins give you a player UI in minutes |
| Interactive synthesizer or music tool | Tone.js | Prebuilt synths, effects, and a global transport for sequencing |
| Streaming radio / long audio files | Howler.js | html5: true streams without buffering the whole file into memory |
| Audio editing or transcription front-end | Wavesurfer.js | Regions plugin enables clip selection, cutting, and annotations |
| Generative or adaptive game soundtrack | Tone.js | Schedule events on the transport, trigger notes from gameplay state |
| WebRTC/meeting recording playback | Howler.js | Battle-tested across browsers and mobile Safari |
Howler.js — Playback You Can Stop Worrying About
Howler.js has one job — play audio — and it does it better than anything else in the ecosystem because it treats browser quirks as its primary problem. It defaults to the Web Audio API and transparently falls back to HTML5 Audio where Web Audio is missing or flaky, which matters far more than most developers realize: mobile Safari, older Android WebViews, and enterprise browsers all have their own audio pathologies. The most basic usage from the official README:
| |
For live streams or very large files, set html5: true so the browser streams instead of buffering the entire asset:
| |
The full option set covers the real-world cases — autoplay, looping, volume, and completion callbacks:
| |
Howler’s headline features: sound sprites (define a single audio file with multiple labeled segments — perfect for packing dozens of tiny game effects into one HTTP request), 3D spatial audio via the howler.spatial plugin, per-sound and global volume/fade/rate control, and a modular build system (howler.core + howler.spatial). It has zero dependencies and weighs about 7kb gzipped. The flip side: howler is intentionally not a music framework — no synths, no effects chains, no sequencing. Its last push was 2025-11, which reads as “done and stable” rather than abandoned; the project remains the default answer in 2026.
Tone.js — A DAW in Your Browser
Tone.js is not an audio player; it is a Web Audio framework for creating audio — synthesizers, effects, and sequences, with an architecture that speaks to musicians as much as programmers. It offers DAW features you will not find anywhere else in the browser ecosystem: a global transport for synchronizing and scheduling events, prebuilt instruments, and a large library of effects. The canonical “Hello Tone” from the README is almost absurdly short:
| |
The note-based API is what separates Tone from every playback library — you work in musical time, not milliseconds:
| |
triggerAttackRelease accepts frequencies in hertz (440) or pitch-octave notation ("D#2"), durations in seconds or tempo-relative values ("8n", "4n"), and a third argument scheduling when along the AudioContext timeline the note should play — which is how you build sequencers that stay in sync. Beyond the basic Tone.Synth, the framework ships PolySynth, samplers, filters, reverb, delay, chorus, and a full effect chain API, all TypeScript-first. The trade-offs are real: Tone is a heavyweight (~130kb+), it is Web Audio-only (no HTML5 fallback), and the browser will not start an AudioContext until a user gesture — your “press any key to begin” screen is not optional. If your product is a game, a music tool, or anything generative, that power is worth the complexity; if you just need a button that plays a click sound, it is the wrong tool.
Wavesurfer.js — The Waveform Player
Wavesurfer.js answers a different question: “how do I show the audio and let people click around in it?” It renders interactive waveforms and layers playback on top, with a plugin system for regions, timelines, spectrograms, and more. The getting-started example from the official README:
| |
That is a full playable, seekable waveform player — styling included. Plugins extend it into a serious editing surface:
| |
The plugin ecosystem is where wavesurfer shines: Regions (select/crop/annotate segments), Timeline (time ruler below the waveform), Spectrogram (frequency analysis view), Minimap (overview + detail navigation), and Envelope (volume automation). TypeScript types ship in the package, so no @types package is needed. Version 7’s API is deliberately reactive-friendly, with clear destroy/cleanup semantics for framework users. The caveats: it is a visualization-and-playback library, not a synthesis or effects framework, and the heavy visualization work means it does not belong in memory-constrained contexts alongside dozens of other components. It also shares the Web Audio autoplay-gesture constraint — the create() call needs a user gesture context in most mobile browsers before audio starts.
Pitfalls and Integration Gotchas
1. Autoplay policies are not optional. Every browser on Earth blocks audio before a user gesture. Howler handles the fallback dance internally, but Tone and Wavesurfer will silently produce no sound until you resume the AudioContext from a click/tap handler: await Tone.start() on first interaction, and create the wavesurfer instance (or call its playPause()) from a gesture too. Test on iOS Safari specifically — it is the strictest.
2. Don’t load audio with plain new Audio(). The HTML5 Audio element has no sprite support, no reliable crossfade, and its buffering behavior varies by platform. If you have more than one sound, you are already in howler territory.
3. Format coverage. WebM/Opus is small and modern; MP3 is the compatibility baseline. howler’s README recommends listing multiple sources (['sound.webm', 'sound.mp3', 'sound.wav']) — the browser picks the first it can play. For Tone and Wavesurfer, use MP3 or AAC for maximum reach; both rely on the browser’s native decoders.
4. Mixing the trio is the pro move. These libraries compose: use howler for playback inside a UI rendered by wavesurfer (wavesurfer’s own player can be driven alongside howler sprites), and schedule Tone.js notes from gameplay events while howler handles UI sounds. The mistake is trying to make one of them do another’s job — Tone has no sprites, howler has no waveforms, wavesurfer has no synths.
5. React/Vue integration needs discipline. All three are imperative APIs. Wrap them in a useEffect-style lifecycle (create on mount, destroy on unmount) or use the community hooks — forgetting the destroy step leaks Web Audio nodes, which pile up as crackling and dropped audio over a long session. Tone also recommends a single shared context: Tone.getContext() instead of creating contexts per component.
6. Long sessions and memory. HTML5 streaming keeps only a buffer window, but Web Audio buffers whole assets. For podcasts or long files with wavesurfer, use the backend: 'mediaelement' option or stream via howler’s html5: true — otherwise a 2-hour file becomes a 2-hour memory reservation.
7. Audio worklets and processing. None of the trio replaces the AudioWorklet API for real-time custom DSP. If you are writing a guitar-tuner or a low-latency effect chain, drop to raw Web Audio; use Tone for the scheduling and effects around your worklet.
If you are building the surrounding media stack, our JavaScript video player comparison covers the video side of the same problem, and the JavaScript game engine comparison shows where audio fits in full game projects. For server-side audio processing, see our Python audio libraries guide.
FAQ
What is the most popular JavaScript audio library?
Howler.js is the most-starred (25,336 stars) and the de-facto default for playback. Tone.js (14,705 stars) leads for music synthesis, and Wavesurfer.js (10,384 stars) for waveform visualization. Each dominates its own niche rather than competing head-to-head.
Can I use Howler.js and Tone.js in the same project?
Yes, and it is a common pattern: howler for UI sounds and long-form playback, Tone for synthesized/generative audio. They coexist fine because both are built on the Web Audio API — just start the Tone context from a user gesture and keep howler’s global volume and Tone’s master output separate.
Does Wavesurfer.js work with React?
Yes. Wavesurfer 7 has good React support — create the instance in a useEffect (or useRef + effect), pass a container ref, and call destroy() on cleanup. The official docs cover Vue and Svelte too. There are also community wrappers, but the raw API is small enough that many teams skip them.
Is howler.js still maintained in 2026?
Yes — the cadence has slowed (last push November 2025) because the project is feature-complete and stable; the maintainer still merges bug fixes. It remains the recommended playback layer in 2026. If you need more active development, the howler API is small enough to replace later without a rewrite.
Which library handles mobile Safari autoplay best?
Howler.js, by design — it manages the Web Audio/HTML5 fallback and resume dance internally. Tone.js and Wavesurfer.js require you to call Tone.start() or create/resume from a user gesture; neither works without it.
How do I make an audio player with a progress bar?
Wavesurfer.js gives you the waveform, click-to-seek, and a Timeline plugin out of the box. Howler gives you playback control and events (onplay, onend, onseek) that you can wire to your own progress bar. Tone.js is not the tool for this.
What about Web Audio API vs HTML5 Audio?
The Web Audio API offers precise scheduling, effects, and low latency; HTML5 Audio offers simpler streaming and broader legacy support. Howler.js bridges them automatically — that is its core value. Tone.js and Wavesurfer.js are Web Audio-native and assume modern browsers.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com