taglib-wasm
v0.9.0
Published
TagLib for TypeScript platforms: Deno, Node.js, Bun, Electron, browsers, and Cloudflare Workers
Maintainers
Readme
TagLib-Wasm
TagLib-Wasm is the universal tagging library for TypeScript/JavaScript (TS|JS) platforms: Deno, Node.js, Bun, Cloudflare Workers, Electron, and browsers.
This project exists because the TS|JS ecosystem had no battle-tested audio tagging library that supports reading and writing music metadata to all popular audio formats. It aspires to be a universal solution for all TS|JS-capable platforms — Deno, Node.js, Bun, Electron, Cloudflare Workers, and browsers.
TagLib-Wasm stands on the shoulders of giants, including TagLib itself, Emscripten, and Wasm (WebAssembly). TagLib itself is legendary, and a core dependency of many music apps.
Features
- Blazing fast performance – Batch processing delivers 10-20x speedup for multiple files
- Full audio format support – Supports all audio formats supported by TagLib
- TypeScript first – Complete type definitions and modern API
- Wide TS/JS runtime support – Deno, Node.js, Bun, Electron, Cloudflare Workers, and browsers
- Format abstraction – Handles container format details automagically when possible
- Zero dependencies – Self-contained Wasm bundle
- Automatic runtime optimization – Uses WASI for Deno/Node.js, Emscripten for browsers
- Production ready – Growing test suite helps ensure safety and reliability
- Two API styles – Use the "Simple" API (3 functions), or the full "Core" API for more advanced applications
- Batch folder operations – Scan directories, process multiple files, find duplicates, and export metadata catalogs
Installation
Deno
import { TagLib } from "@charlesw/taglib-wasm";Node.js
npm install taglib-wasmNote: Requires Node.js v22.6.0 or higher. If you want to use the TypeScript version with Node.js, see the installation guide.
Bun
bun add taglib-wasmElectron
npm install taglib-wasmWorks in both main and renderer processes:
// Main process
import { TagLib } from "taglib-wasm";
// Renderer process (with nodeIntegration: true)
const { TagLib } = require("taglib-wasm");Deno Compiled Binaries (Offline Support)
For Deno compiled binaries that need to work offline, you can embed the WASM file:
// 1. Prepare your build by copying the WASM file
import { prepareWasmForEmbedding } from "@charlesw/taglib-wasm";
await prepareWasmForEmbedding("./taglib.wasm");
// 2. In your application, use the helper for automatic handling
import { initializeForDenoCompile } from "@charlesw/taglib-wasm";
const taglib = await initializeForDenoCompile();
// 3. Compile with the embedded WASM
// deno compile --allow-read --include taglib.wasm myapp.tsSee the complete Deno compile guide for more options including CDN loading.
For manual control:
// Load embedded WASM in compiled binaries
const wasmBinary = await Deno.readFile(
new URL("./taglib.wasm", import.meta.url),
);
const taglib = await TagLib.initialize({ wasmBinary });Quick Start
Simple API
import { applyTags, readTags, updateTags } from "taglib-wasm/simple";
// Read tags - just one function call!
const tags = await readTags("song.mp3");
console.log(tags.title, tags.artist, tags.album);
// Apply tags and get modified buffer (in-memory)
const modifiedBuffer = await applyTags("song.mp3", {
title: "New Title",
artist: "New Artist",
album: "New Album",
});
// Or update tags on disk (requires file path)
await updateTags("song.mp3", {
title: "New Title",
artist: "New Artist",
});High-Performance Batch Processing
import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
// Process multiple files in parallel - dramatically faster!
const files = ["track01.mp3", "track02.mp3", /* ... */ "track20.mp3"];
// Read just tags (18x faster than sequential)
const tags = await readTagsBatch(files, { concurrency: 8 });
// Read complete metadata including cover art detection (15x faster)
const metadata = await readMetadataBatch(files, { concurrency: 8 });
// Real-world performance:
// Sequential: ~100 seconds for 20 files
// Batch: ~5 seconds for 20 files (20x speedup!)Full API
The Full API might be a better choice for apps and utilities focused on advanced metadata management.
import { TagLib } from "taglib-wasm";
// Initialize taglib-wasm
const taglib = await TagLib.initialize();
// Load audio file
const file = await taglib.open("song.mp3");
// Read and update metadata
const tag = file.tag();
tag.setTitle("New Title");
tag.setArtist("New Artist");
// Save changes
file.save();
// Clean up
file.dispose();Batch Folder Operations
Process entire music collections efficiently:
import { findDuplicates, scanFolder } from "taglib-wasm/folder";
// Scan a music library
const result = await scanFolder("/path/to/music", {
recursive: true,
concurrency: 4,
onProgress: (processed, total, file) => {
console.log(`Processing ${processed}/${total}: ${file}`);
},
});
console.log(`Found ${result.totalFound} audio files`);
console.log(`Successfully processed ${result.totalProcessed} files`);
// Process results
for (const file of result.files) {
console.log(`${file.path}: ${file.tags.artist} - ${file.tags.title}`);
console.log(`Duration: ${file.properties?.duration}s`);
}
// Find duplicates
const duplicates = await findDuplicates("/path/to/music", ["artist", "title"]);
console.log(`Found ${duplicates.size} groups of duplicates`);Working with Cover Art
import { getCoverArt, setCoverArt } from "taglib-wasm/simple";
// Extract cover art
const coverData = await getCoverArt("song.mp3");
if (coverData) {
await Deno.writeFile("cover.jpg", coverData);
}
// Set new cover art
const imageData = await Deno.readFile("new-cover.jpg");
const modifiedBuffer = await setCoverArt("song.mp3", imageData, "image/jpeg");
// Save modifiedBuffer to file if neededWorking with Ratings
import { RatingUtils, TagLib } from "taglib-wasm";
const taglib = await TagLib.initialize();
const file = await taglib.open("song.mp3");
// Read rating (normalized 0.0-1.0)
const rating = file.getRating();
if (rating !== undefined) {
console.log(`Rating: ${RatingUtils.toStars(rating)} stars`);
}
// Set rating (4 out of 5 stars)
file.setRating(0.8);
file.save();
file.dispose();See the Track Ratings Guide for RatingUtils API and cross-format conversion details.
Container Format and Codec Detection
import { readProperties } from "taglib-wasm/simple";
// Get detailed audio properties including container and codec info
const props = await readProperties("song.m4a");
console.log(props.containerFormat); // "MP4" (container format)
console.log(props.codec); // "AAC" or "ALAC" (compressed media format)
console.log(props.isLossless); // false for AAC, true for ALAC
console.log(props.bitsPerSample); // 16 for most formats
console.log(props.bitrate); // 256 (kbps)
console.log(props.sampleRate); // 44100 (Hz)
console.log(props.length); // 180 (duration in seconds)Container format vs Codec:
- Container format – How audio data and metadata are packaged (e.g., MP4, OGG)
- Codec – How audio is compressed/encoded (e.g., AAC, Vorbis)
Supported formats:
- MP4 container (.mp4, .m4a) – Can contain AAC (lossy) or ALAC (lossless)
- OGG container (.ogg) – Can contain Vorbis, Opus, FLAC, or Speex
- MP3 – Both container and codec (lossy)
- FLAC – Both container and codec (lossless)
- WAV – Container for PCM (uncompressed) audio
- AIFF – Container for PCM (uncompressed) audio
Documentation
Getting Started
Guides
- API Reference
- Performance Guide
- Album Processing Guide - Process entire albums in seconds
- Platform Examples
- Working with Cover Art
- Track Ratings
- Cloudflare Workers Setup
- Error Handling
Development
Supported Formats
taglib-wasm is designed to support all formats supported by TagLib:
- .mp3 – ID3v2 and ID3v1 tags
- .m4a/.mp4 – MPEG-4/AAC metadata for AAC and Apple Lossless audio
- .flac – Vorbis comments and audio properties
- .ogg – Ogg Vorbis format with full metadata support
- .wav – INFO chunk metadata
- Additional formats – Opus, APE, MPC, WavPack, TrueAudio, AIFF, WMA, and more
Key Features
Extended Metadata Support
Beyond basic tags, taglib-wasm supports extended metadata:
import { Tags } from "taglib-wasm";
// AcoustID fingerprints
file.setProperty(
Tags.AcoustidFingerprint,
"AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...",
);
// MusicBrainz IDs
file.setProperty(
Tags.MusicBrainzTrackId,
"f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab",
);
// ReplayGain volume normalization
file.setProperty(Tags.TrackGain, "-6.54 dB");
file.setProperty(Tags.TrackPeak, "0.987654");View all supported tag constants →
Performance and Best Practices
Batch Processing for Multiple Files
When processing multiple audio files, use the optimized batch APIs for dramatic performance improvements:
import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
// ❌ SLOW: Processing files one by one (can take 90+ seconds for 19 files)
for (const file of files) {
const tags = await readTags(file); // Re-initializes for each file
}
// ✅ FAST: Batch processing (10-20x faster)
const result = await readTagsBatch(files, {
concurrency: 8, // Process 8 files in parallel
onProgress: (processed, total) => {
console.log(`${processed}/${total} files processed`);
},
});
// ✅ FASTEST: Read complete metadata in one batch
const metadata = await readMetadataBatch(files, { concurrency: 8 });Performance comparison for 19 audio files:
- Sequential: ~90 seconds (4.7s per file)
- Batch (concurrency=4): ~8 seconds (11x faster)
- Batch (concurrency=8): ~5 seconds (18x faster)
Smart Partial Loading
For large audio files (>50MB), enable partial loading to dramatically reduce memory usage:
// Enable partial loading for large files
const file = await taglib.open("large-concert.flac", {
partial: true,
maxHeaderSize: 2 * 1024 * 1024, // 2MB header
maxFooterSize: 256 * 1024, // 256KB footer
});
// Read operations work normally
const tags = file.tag();
console.log(tags.title, tags.artist);
// Smart save - automatically loads full file when needed
await file.saveToFile(); // Full file loaded only herePerformance gains:
- 500MB file: ~450x less memory usage (1.1MB vs 500MB)
- Initial load: 50x faster (50ms vs 2500ms)
- Memory peak: 3.3MB instead of 1.5GB
WebAssembly Streaming
For web applications, use CDN URLs to enable WebAssembly streaming compilation:
// ✅ FAST: Streaming compilation (200-400ms)
const taglib = await TagLib.initialize({
wasmUrl: "https://cdn.jsdelivr.net/npm/taglib-wasm@latest/dist/taglib.wasm",
});
// ❌ SLOWER: ArrayBuffer loading (400-800ms)
const wasmBinary = await fetch("taglib.wasm").then((r) => r.arrayBuffer());
const taglib = await TagLib.initialize({ wasmBinary });View complete performance guide →
Development
Build from Source
# Prerequisites: Emscripten SDK
# Install via: https://emscripten.org/docs/getting_started/downloads.html
# Clone and build
git clone https://github.com/CharlesWiltgen/taglib-wasm.git
cd taglib-wasm
# Build Wasm module
npm run build:wasm
# Run tests
npm testRuntime Compatibility
taglib-wasm works across all major JavaScript runtimes:
| Runtime | Status | Installation | Notes |
| ---------------------- | ------ | ------------------------- | ------------------------- |
| Deno | Full | npm:taglib-wasm | Native TypeScript |
| Node.js | Full | npm install taglib-wasm | TypeScript via tsx |
| Bun | Full | bun add taglib-wasm | Native TypeScript |
| Browser | Full | Via bundler | Full API support |
| Cloudflare Workers | Full | taglib-wasm/workers | Memory-optimized build |
| Electron | Full | npm install taglib-wasm | Main & renderer processes |
Known Limitations
- Memory Usage – Entire file must be loaded into memory (may be an issue for very large files)
- Concurrent Access – Not thread-safe (JavaScript single-threaded nature mitigates this)
- Cloudflare Workers – Limited to 128MB memory per request; files larger than ~100MB may fail
Contributing
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
License
This project uses dual licensing:
- TypeScript/JavaScript code – MIT License (see LICENSE)
- WebAssembly binary (taglib.wasm) – LGPL-2.1-or-later (inherited from TagLib)
The TagLib library is dual-licensed under LGPL/MPL. When compiled to WebAssembly, the resulting binary must comply with LGPL requirements. This means:
- You can use taglib-wasm in commercial projects
- If you modify the TagLib C++ code, you must share those changes
- You must provide a way for users to relink with a modified TagLib
For details, see lib/taglib/COPYING.LGPL
Acknowledgments
- TagLib – Excellent audio metadata library
- Emscripten – WebAssembly compilation toolchain
- WASI – WebAssembly System Interface for server-side runtimes
