wasm-audio-decoders ships FLAC, Opus and AAC decoders as WebAssembly
Browser and NodeJS Web Assembly audio decoder libraries that are highly optimized for size and performance.
At a glance
- What is it?
- WASM Audio Decoders is a collection of Emscripten-built audio decoders for the browser and Node.js, each with a threaded Web Worker mode, published as separate npm packages between 67 KiB and 226 KiB. It suits a web app that needs to decode audio without the browser codec stack, and it is not a transcoder or an encoder.
- Who is it for?
- Adopt wasm-audio-decoders if you need to decode audio in a browser tab or in Node.js where the native codec path is unavailable or unwanted, particularly for FLAC and Ogg Opus where the browser support is uneven. Do not adopt it to encode, to transcode, or to play a file the browser already handles natively, and be careful with the Opus machine learning build, since enabling it takes the bundle from 114.9 KiB to 4.0 MiB.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 57 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Seven decoders, seven npm packages
WASM Audio Decoders is a collection of Web Assembly audio decoder libraries that are highly optimized for browser use, and the collection is split by codec rather than shipped as one bundle. Each module supports synchronous decoding on the main thread as well as asynchronous, threaded decoding through a built-in Web Worker implementation. The packages cover a broad slice of what a browser actually encounters. mpg123-decoder handles MPEG Layer I, II and III into PCM and is based on mpg123. The scoped @wasm-audio-decoders packages cover FLAC and Ogg FLAC, Ogg Opus, raw Opus, Ogg Vorbis and AAC, with the FLAC and Ogg containers using the codec-parser library to split the stream. The last two are worth separating from the rest, because the raw decoders are a lower level. opus-decoder and the @wasm-audio-decoders/opus-ml package decode raw Opus audio frames and are intended for users that already have Opus frames extracted from a container, for example Ogg, Matroska or ISOBMFF. The same applies to the AAC package, which supports raw AAC frames using an AudioSpecificConfig, that is, from an ISOBMFF esds box.
Bundle sizes, and what the Opus ML build really costs
The size figures are published per decoder and they are the number to plan around. The FLAC decoder is the smallest at 67.2 KiB minified, followed by mpg123-decoder at 77.0 KiB, opus-decoder at 85.5 KiB, ogg-vorbis at 99.4 KiB, ogg-opus at 114.9 KiB and the AAC decoder at 226.4 KiB. The exception is the Opus machine learning build, which is listed separately at 3.9 MiB and is the package to think about if you care about download size. The ogg-opus-decoder also ships the machine learning enhancements, and the documentation is explicit that the bundle size increases to 4.0 MiB when they are enabled, which is a 35 fold jump from the 114.9 KiB baseline. The enhancements are described as Opus 1.5 machine learning enhancements for high quality speech decoding, so the trade is bundle weight against speech quality, and the trade is very steep on the weight side. Multichannel limits differ by codec as well, with FLAC and AAC supporting up to 8 channels and the Opus family up to 255.
Inline WASM, yEnc and gzip, and why bundlers stay out of the way
The build approach is the part that explains the small bundle sizes. Each decoder is built with inline WASM to reduce bundling complexity with transpilers like Webpack, and the inlined WASM is encoded using yEnc for efficient binary encoding and is gzip compressed for reduced file size. Putting the WASM inside the JavaScript rather than shipping it as a separate .wasm file removes the need for the bundler to understand WebAssembly module resolution, which is the usual source of friction in toolchains that did not anticipate it. The build tooling matches that goal. The minify script passes the source path, an output name, a compression iteration count and the module and minified module names into build.js, with the compression iterations set per decoder in the Makefile, so the balance between size and startup cost is tuned per codec rather than globally. Pre-built minified JavaScript files are available from NPM and in each decoder's dist folder, so most users never run the toolchain at all.
Synchronous on the main thread, threaded in a Web Worker
Every decoder ships with browser and Node.js support and built-in Web Worker support, and the two modes answer different needs. The synchronous path decodes on the main thread, which is simple and fine for short files or small buffers, and it avoids the overhead of spinning up a worker. The threaded path moves decoding into a Web Worker, which is what you want for anything that would otherwise block the user interface while a few megabytes of PCM are produced. The worker implementation is part of each package rather than something you assemble, so there is no separate worker bundle to wire up, and the packages expose the same API either way, which is what makes switching between the two a configuration change rather than a rewrite. The repository's own demo directory shows both patterns in use, with a per-decoder demo script and its minified and source-mapped variants, plus a shared index.html and small sample Ogg files for the Opus cases.
Building the decoders from source
Building is a four step process and it needs a specific toolchain. The prerequisites are a Linux or Linux-like environment to build, for example WSL on Windows, NodeJS 18.x or higher, and Emscripten 4.0.7 installed following the upstream instructions. You clone the repository and initialise the git sub-modules, because the codec sources come in as submodules.
git clone https://github.com/eshaz/wasm-audio-decoders.git
cd wasm-audio-decoders
git submodule update --initThen the build dependencies and the per-decoder dependencies are installed.
npm install
npm run install-decodersThe Emscripten environment has to be sourced into the shell you want to build in, for example by sourcing the emsdk_env.sh file from your Emscripten installation, and then the libraries are configured and built.
# only required for first time build, OR after updating the `Makefile`
npm run configure
# builds the project
npm run buildThe configure step runs make configure and the build runs make clean followed by a parallel make, and the results land in each library's dist folder. The Makefile at the root of the repository defines a configure target that fans out to per-codec configure steps, and a dist target that depends on all seven decoder outputs, so a full build produces every codec at once.
Testing, and the fixtures that keep decoders honest
The test setup is more involved than a typical JavaScript library, and that is a good sign for a decoder. The test script runs a TypeScript type check first and then the Jest suite under experimental VM modules, and the type check itself is a separate script that compiles the test file with tsc in noEmit mode against the nodenext module target and the esnext target. The repository also has a generate-data script that finds the expected WAV files under the test directory and gzips them in place, which tells you the test data is stored as reference WAV output and compared against it. The devDependencies list confirms the shape of the fixtures,, and a pinned codec-parser version used by the container-aware decoders. There is also a du script that walks the src directory for minified JavaScript files and prints their sizes in kilobytes, which is how the published bundle size figures are produced rather than estimated.
Where this set of decoders is the wrong tool
The boundaries are clear from the format list. These libraries decode, and nothing more. There is no encoder here, so if you need to produce FLAC or Opus rather than read it, this is the wrong project. The second boundary is container handling. The Opus and AAC decoders expect frames that have already been extracted from a container, and the Ogg and FLAC packages do that extraction themselves through codec-parser, so a caller working with a Matroska or an ISOBMFF file has to do that step themselves or use a different library. The third is the browser codec situation, which the documentation addresses directly by stating that these decoders are in practice just as fast, and in some cases faster, than the browser implementation. That is a claim about the cases where the browser has no native path, and a project that already decodes AAC or MP3 natively in the browser has no reason to carry 226 KiB of WebAssembly to do the same work. The fourth is the build environment, since producing the binaries yourself needs Linux or WSL, a pinned Emscripten 4.0.7 and the submodules, which is a real cost if you intend to patch a codec rather than consume one.
Editorial conclusion
Adopt wasm-audio-decoders if you need to decode audio in a browser tab or in Node.js where the native codec path is unavailable or unwanted, particularly for FLAC and Ogg Opus where the browser support is uneven. Do not adopt it to encode, to transcode, or to play a file the browser already handles natively, and be careful with the Opus machine learning build, since enabling it takes the bundle from 114.9 KiB to 4.0 MiB. Verify two things before you commit: that your target environment has the WebAssembly SIMD support the machine learning build requires, since the README states that requirement explicitly, and that the format you need is actually covered, because raw Opus and raw AAC decoders expect frames you have already extracted from a container rather than the container itself. The practical first step is to install the package for the one codec you need and call it in a Node.js script before wiring it into a browser worker.
Frequently asked questions
What is wasm-audio-decoders?
It is a collection of Web Assembly audio decoder libraries highly optimized for browser use, each supporting synchronous decoding on the main thread and asynchronous threaded decoding through a built-in Web Worker implementation. The collection covers MPEG, FLAC, Opus, Ogg Vorbis and AAC.
How do I install one of the WASM Audio Decoders?
Each decoder is published separately to npm, for example mpg123-decoder, ogg-opus-decoder and opus-decoder under unscoped names, and the FLAC, Ogg Vorbis, AAC and Opus ML decoders under the @wasm-audio-decoders scope. Pre-built minified JavaScript files are also available in each decoder's dist folder.
How large are the decoder bundles?
The published minified sizes are 67.2 KiB for FLAC, 77.0 KiB for mpg123, 85.5 KiB for raw Opus, 99.4 KiB for Ogg Vorbis, 114.9 KiB for Ogg Opus and 226.4 KiB for AAC. Enabling the Opus 1.5 machine learning enhancements raises the Ogg Opus bundle to 4.0 MiB, and the separate opus-ml package is 3.9 MiB.
What does the Opus machine learning build require?
The @wasm-audio-decoders/opus-ml package uses the Opus 1.5 machine learning enhancements for high quality speech decoding and the README states that WebAssembly SIMD support is required for the browser and Node.js support it provides.
Can wasm-audio-decoders decode an Opus file inside an MP4?
Not directly. The raw Opus and AAC decoders are intended for users that already have frames extracted from a container such as Ogg, Matroska or ISOBMFF, while the Ogg Opus and FLAC packages do their own container parsing through codec-parser.
How do I build the decoders from source?
You need Linux or WSL, NodeJS 18.x or higher and Emscripten 4.0.7. Then clone the repository, run git submodule update --init, run npm install and npm run install-decoders, source the Emscripten environment, and run npm run configure followed by npm run build, with the results in each library's dist folder.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/eshaz-wasm-audio-decoders)Was this useful?