Decode any audio format to raw samples.
JS / WASM – no ffmpeg, no native bindings, works in both node and browser.
Small API, minimal size, near-native performance, lazy-loading, chunked decoding.
import decode from '@audio/decode';
const { channelData, sampleRate } = await decode(anyAudioBuffer);| Format | Package | Size | Engine |
|---|---|---|---|
| MP3 | @audio/decode-mp3 | 92 KB | WASM |
| WAV | @audio/decode-wav | 4 KB | JS |
| OGG Vorbis | @audio/decode-vorbis | 164 KB | WASM |
| FLAC | @audio/decode-flac | 133 KB | WASM |
| Opus | @audio/decode-opus | 178 KB | WASM |
| M4A / AAC / ALAC | @audio/decode-aac | 368 KB | WASM + JS |
| QOA | @audio/decode-qoa | 8 KB | JS |
| AIFF | @audio/decode-aiff | 20 KB | JS |
| CAF | @audio/decode-caf | 7 KB | JS |
| WebM | @audio/decode-webm | 263 KB | WASM |
| AMR | @audio/decode-amr | 241 KB | WASM |
| WMA | @audio/decode-wma | 91 KB | WASM |
Auto-detects format. Input can be ArrayBuffer, Uint8Array, Buffer, or anything that materializes to bytes — a Blob/File (browser file input, drag-drop) or a fetch Response.
import decode from '@audio/decode'
let { channelData, sampleRate } = await decode(buf)
let fromFile = await decode(fileInput.files[0]) // File
let fromUrl = await decode(await fetch(url)) // Responselet dec = await decode.mp3()
let a = await dec(chunk1) // { channelData, sampleRate }
let b = await dec(chunk2)
await dec() // closeimport decode from '@audio/decode'
for await (let { channelData, sampleRate } of decode.mp3(response.body)) {
// process chunks
}Works with ReadableStream, fetch body, Node stream, or any async iterable.
Formats: mp3, flac, opus, oga, m4a, wav, qoa, aac, aiff, caf, webm, amr, wma.
Works from CDN without a bundler – codecs load on demand via dynamic import, only for formats you actually decode:
<script type="module">
import decode from 'https://esm.sh/@audio/decode'
let { channelData, sampleRate } = await decode(buf)
</script>Self-hosting instead of a CDN? Use an import map to point @audio/decode and each @audio/decode-* package you need to its local path – every codec is a self-contained bundle (WASM inlined, no transitive deps).
Read tags, pictures, markers and regions without decoding samples. Available for
wav, mp3, flac, oga (Ogg Vorbis), opus, and m4a.
import { wav, mp3, flac, oga, opus, m4a } from '@audio/decode/meta'
let { meta, sampleRate, markers, regions } = mp3(bytes)
// meta: { title, artist, album, year, bpm, key, comment, pictures, raw, ... }
// markers: [{ sample, label }]
// regions: [{ sample, length, label }]Each codec sub-package also exposes its parser directly:
import { parseMeta } from '@audio/decode-wav/meta'
let info = parseMeta(wavBytes)Each @audio/decode-* package is a self-contained ESM module — import directly in a worker:
// decode-worker.js
import decode from '@audio/decode-mp3'
self.onmessage = async ({ data }) => {
let pcm = await decode(data)
self.postMessage(pcm, pcm.channelData.map(ch => ch.buffer))
}
// main.js
let worker = new Worker('./decode-worker.js', { type: 'module' })
worker.postMessage(mp3buf, [mp3buf])
worker.onmessage = ({ data }) => { /* { channelData, sampleRate } */ }- encode – encode PCM into any audio format.
- audio-type – detect audio format from buffer.
Licensing: the umbrella and most codec atoms are MIT; three atoms inherit their upstream codec implementation's license — @audio/decode-aac GPL-2.0, @audio/decode-wma GPL-2.0-or-later, @audio/decode-amr Apache-2.0. Install only the codecs whose licenses fit your project — the umbrella loads them on demand.
