@audio/encode
v1.8.0
Published
Encode audio data in node or browser
Downloads
1,538
Readme
@audio/encode 
Try it in the browser: Audio converter, Audio recorder. Runs on this package, nothing is uploaded.
Encode raw audio samples to any format. JS / WASM – no ffmpeg, no native bindings, works in both node and browser. Small API, minimal size, near-native performance, stream encoding.
import encode from '@audio/encode';
const buf = await encode.wav(channelData, { sampleRate: 44100 });Supported formats:
| Format | Package | Engine |
|--------|---------|--------|
| WAV | @audio/encode-wav | JS |
| MP3 | @audio/encode-mp3 | WASM |
| OGG Vorbis | @audio/encode-ogg | WASM |
| Opus | @audio/encode-opus | WASM (libopus, single file) |
| WebM | @audio/encode-webm | WASM (libopus, single file) |
| FLAC | @audio/encode-flac | WASM |
| AAC | @audio/encode-aac | WebCodecs*, else WASM (Fraunhofer FDK): LC, HE, HEv2 |
| AIFF | @audio/encode-aiff | JS |
| CAF | @audio/encode-caf | JS |
| QOA | @audio/encode-qoa | JS |
| WavPack (wv) | @audio/encode-wavpack | WASM (libwavpack, single file) — lossless, hybrid lossy, 16/24/float, APEv2 tags |
| M4A / MP4 (m4a, mp4) | @audio/encode-mp4 | JS muxer — AAC, Opus, FLAC, MP3 or PCM in ISOBMFF, iTunes tags + chapters, fragmented when streamed; remux() replaces or strips the audio track of an existing video without touching the video stream |
| ALAC (m4a with codec: 'alac') | @audio/encode-alac | JS — Apple Lossless, port of Apple's reference encoder (Apache-2.0), bit-exact |
* AAC uses the native WebCodecs AudioEncoder where the browser supports the configuration (Chromium, Safari), else the Fraunhofer FDK AAC encoder compiled to WebAssembly (Node, Firefox), loaded on first use.
Whole-file encode
Specify the format as method name. Input is Float32Array[] (one per channel), a single Float32Array (mono), or an AudioBuffer.
import encode from '@audio/encode';
const wav = await encode.wav(channelData, { sampleRate: 44100 });
const aiff = await encode.aiff(channelData, { sampleRate: 44100 });
const caf = await encode.caf(channelData, { sampleRate: 44100 });
const mp3 = await encode.mp3(channelData, { sampleRate: 44100, bitrate: 128 });
const ogg = await encode.ogg(channelData, { sampleRate: 44100, quality: 5 });
const flac = await encode.flac(channelData, { sampleRate: 44100 });
const opus = await encode.opus(channelData, { sampleRate: 48000, bitrate: 96 });
const webm = await encode.webm(channelData, { sampleRate: 48000, bitrate: 96 });
const qoa = await encode.qoa(channelData, { sampleRate: 44100 });
const aac = await encode.aac(channelData, { sampleRate: 44100, bitrate: 128 });encode.formats lists the supported names and encode.mime maps each to a MIME type — handy for format-agnostic pipelines.
Chunked encoding
Call with just options (no data) to create a streaming encoder:
import encode from '@audio/encode';
const enc = await encode.mp3({ sampleRate: 44100, bitrate: 128 });
const a = await enc(chunk1); // Uint8Array
const b = await enc(chunk2);
const c = await enc(null); // end of stream — flush + free
// explicit control: enc.flush(), enc.free()Streaming
Pass an async iterable as data — returns an async generator:
import encode from '@audio/encode'
for await (let buf of encode.mp3(audioSource, { sampleRate: 44100, bitrate: 128 })) {
// buf is Uint8Array
}Works with any async iterable source.
Unbounded streams
stream: true emits bytes as they encode, metadata in the header, memory flat however long it runs (a pipe, a socket, a week of recording). Totals the header can't know yet say "unknown": WAV/AIFF sizes 0xFFFFFFFF (read to the end), CAF's -1 data size, QOA's samples: 0, FLAC's STREAMINFO zeros; m4a/mp4 become fragmented (moof+mdat per second). After the end, enc.head() returns the header with its final totals — write it over the start of a file; a pipe keeps the "unknown" one.
import { open } from 'node:fs/promises'
const file = await open('live.wav', 'w')
const enc = await encode.wav({ sampleRate: 48000, channels: 2, stream: true, meta: { title: 'Live' } })
for await (let pcm of source) await file.write(await enc(pcm))
await file.write(await enc())
let head = enc.head() // RF64 past 4 GB
if (head) await file.write(head, 0, head.length, 0)
await file.close()Options
| Option | Description | Applies to |
|--------|-------------|------------|
| sampleRate | Output sample rate (required) | all |
| bitrate | Target bitrate in kbps | mp3, opus, webm, aac |
| quality | Quality 0–10 (VBR) | ogg, mp3 |
| channels | Output channel count | all |
| bitDepth | Bit depth: 16/24/32 (wav), 16/24 (aiff, flac), 16/32 (caf) | wav, aiff, flac, caf |
| compression | FLAC compression level 0–8 | flac |
| application | 'audio', 'voip', or 'lowdelay' | opus, webm |
| meta | Tags (see below) | wav, mp3, flac, aiff, ogg, opus, m4a |
| chapters | [{ time, title }] (seconds): ID3 CHAP/CTOC, iTunes chpl | mp3, m4a |
| profile | 'lc', 'he' (HE-AAC), 'hev2' (HE-AACv2, stereo) | aac, m4a |
| stream | Emit as it encodes; head() after (see above) | all |
| frames | With stream: the exact length, when known upfront, so the header goes out exact (a canonical WAV; a pipe gets true totals) | wav, aiff, caf, qoa, flac, mp3 |
Metadata
Pass meta (and, for wav, markers/regions) straight to the encoder:
let bytes = await encode.flac(channelData, {
sampleRate: 44100,
meta: { title: 'Hare Krishna', artist: 'Prabhupada', year: '1966' }
})Tags work for wav, mp3, flac, aiff, ogg, opus and m4a. Cue markers and regions are wav-only. opus, wv and m4a bake tags into their headers as they encode; the others splice tags into the finished file, so a chunked encode of wav/mp3/flac/aiff/ogg with meta buffers until flush — unless stream: true, which writes the tags ahead of the audio and streams.
You can also tag already-encoded bytes via @audio/encode/meta:
import { wav } from '@audio/encode/meta'
let out = wav(bytes, {
meta: { title: 'Hare Krishna', artist: 'Prabhupada', year: '1966' },
markers: [{ sample: 44100, label: 'verse' }],
regions: [{ sample: 88200, length: 44100, label: 'chorus' }]
})Each codec sub-package also exposes its writer directly:
import { writeMeta } from '@audio/encode-mp3/meta'
let tagged = writeMeta(mp3Bytes, { meta: { title: 'foo' } })See also
- decode – decode any audio format to raw samples.
- wasm-media-encoders – compact WASM MP3 & Vorbis encoders.
- AudioEncoder – native WebCodecs encoder API.
License
ॐ

