Skip to content

CharlesWiltgen/taglib-wasm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

TagLib-Wasm

Tests npm version npm downloads License: MIT
Built with Built with Emscripten Built with WebAssembly Built with TagLib
Deno Node.js Bun Cloudflare Workers Electron Browsers

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
  • βœ… 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-wasm

Note: 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-wasm

Electron

npm install taglib-wasm

Works 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.ts

See 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 (10-20x Faster!)

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 needed

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

πŸ“– View Full Documentation

Getting Started

Guides

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 & 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 here

Performance 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 test

View full development guide β†’

🌐 Runtime 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

About

TagLib for TypeScript platforms: Deno, Node.js, Bun, browsers, and Cloudflare Workers

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors 2

  •  
  •