Skip to content

Runtime Compatibility

taglib-wasm is designed to work seamlessly across all major JavaScript runtimes. This document outlines the specific features and considerations for each runtime.

🟢 Supported Runtimes

✅ Deno 2.0+

  • Status: Fully supported via npm specifier
  • Installation: import { TagLib } from "npm:taglib-wasm"
  • Features:
    • Native TypeScript support
    • Built-in Web APIs
    • Excellent Wasm performance
    • Security sandbox
  • File Loading: Deno.readFile()
typescript
import { TagLib } from "npm:taglib-wasm";

const taglib = await TagLib.initialize();
const audioData = await Deno.readFile("song.mp3");
using file = taglib.openFile(audioData);

✅ Bun 1.0+

  • Status: Fully supported
  • Installation: bun add taglib-wasm
  • Features:
    • Native TypeScript support
    • Fast startup and execution
    • Excellent Wasm performance
    • Node.js compatibility
  • File Loading: Bun.file().arrayBuffer()
typescript
import { TagLib } from "taglib-wasm";

const taglib = await TagLib.initialize();
const audioData = await Bun.file("song.mp3").arrayBuffer();
using file = taglib.openFile(new Uint8Array(audioData));

✅ Node.js 22.6.0+

  • Status: Fully supported
  • Installation: npm install taglib-wasm
  • Features:
    • Mature ecosystem
    • Extensive package support
    • Good Wasm performance
  • File Loading: fs.readFile() or fs.promises.readFile()
typescript
import { TagLib } from "taglib-wasm";
import { readFile } from "fs/promises";

const taglib = await TagLib.initialize();
const audioData = await readFile("song.mp3");
using file = taglib.openFile(audioData);

✅ Browsers (Chrome 57+, Firefox 52+, Safari 11+)

  • Status: Fully supported
  • Installation: Via CDN or bundler
  • Features:
    • Wide compatibility
    • Web APIs
    • Service Worker support
  • File Loading: File API, fetch(), or FileReader
typescript
import { TagLib } from "taglib-wasm";

const taglib = await TagLib.initialize();

// From file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const audioData = new Uint8Array(await file.arrayBuffer());
using tagFile = taglib.openFile(audioData);

// From fetch
const response = await fetch("song.mp3");
const audioData = new Uint8Array(await response.arrayBuffer());
using tagFile = taglib.openFile(audioData);

✅ Cloudflare Workers

  • Status: Fully supported
  • Installation: npm install taglib-wasm
  • Features:
    • Edge computing
    • Automatic scaling
    • Global deployment
    • KV/R2/D1 integration
  • File Loading: Request body or fetch from storage
  • Documentation: See Cloudflare Workers Guide
typescript
export default {
  async fetch(request: Request): Promise<Response> {
    const taglib = await TagLib.initialize();
    const audioData = new Uint8Array(await request.arrayBuffer());
    using file = taglib.openFile(audioData);

    // Process metadata...

    return new Response(JSON.stringify(metadata));
  },
};

🔄 WebAssembly Runtime Selection

TagLib-Wasm includes two WebAssembly implementations and automatically selects the optimal one for your environment:

EnvironmentImplementationReason
DenoWASINative filesystem, best performance
BunWASINative filesystem via node:fs provider
Node.js 24 LTSWASIAdd --experimental-wasm-exnref for WASI; auto-fallback to Emscripten
Node.js 25+WASINative filesystem, exnref supported natively
BrowsersEmscriptenRequired for web compatibility
Cloudflare WorkersEmscriptenWASI not available

Checking the Active Implementation

The runtime is selected automatically based on your environment. No manual checking is needed -- TagLib-Wasm always picks the optimal implementation.

Performance Benefits

WASI mode provides:

  • 10x faster serialization via MessagePack (vs JSON in Emscripten)
  • Native memory management with RAII patterns
  • Smaller binary size (~28KB vs ~2MB)

Advanced: Manual Override

In rare cases, you may want to force a specific implementation:

typescript
// Force Emscripten mode (in-memory I/O, works everywhere)
const taglib = await TagLib.initialize({ forceWasmType: "emscripten" });

forceWasmType lets you choose the specific Wasm backend ("wasi" or "emscripten").

Most users never need to configure this. The automatic selection

provides optimal performance for each environment. The WASI backend uses path-based filesystem I/O on Deno/Node.js, reading only metadata headers instead of loading entire files into memory. :::

🔧 Runtime-Specific Features

Memory Management

All runtimes use the same memory management approach:

  • Emscripten's allocate() for JS/Wasm data transfer
  • Automatic garbage collection for JavaScript objects
  • Use using statements for automatic C++ object disposal (or dispose() as fallback)

File System Access

Each runtime has different file system capabilities:

RuntimeFile SystemSecurityBest For
DenoSandboxed, permission-basedHighServer-side, CLI tools
BunFull accessMediumServer-side, build tools
Node.jsFull accessMediumServer-side, classic apps
BrowserLimited (File API only)HighClient-side, web apps
WorkersNone (Request/KV/R2 only)HighEdge computing, APIs

Performance Characteristics

RuntimeStartupWasm PerformanceMemory UsageTypeScript
BunVery FastExcellentLowNative
DenoFastExcellentMediumNative
WorkersVery FastExcellentLowNative
Node.jsMediumGoodMediumVia loader
BrowserFastGoodMediumVia build

📦 Installation Matrix

RuntimePackage ManagerCommand
Denonpm specifierimport { TagLib } from "npm:taglib-wasm"
Bunbunbun add taglib-wasm
Node.jsnpmnpm install taglib-wasm
Node.jsyarnyarn add taglib-wasm
Node.jspnpmpnpm add taglib-wasm
Workersnpmnpm install taglib-wasm
BrowserCDN<script type="module" src="...">

🧪 Testing Across Runtimes

The project includes comprehensive tests that run on all supported runtimes:

bash
# Test with Deno (recommended)
deno run --allow-read test-systematic.ts

# Test with Bun
bun run test-systematic.ts

# Test with Node.js  
npm test

# Test in browser
# Open examples/browser/index.html

🚧 Known Limitations

General Limitations

  • File Writing: Changes only affect in-memory representation
  • Large Files: Memory usage scales with file size
  • Thread Safety: Single-threaded execution (JavaScript limitation)

Runtime-Specific Limitations

Deno

  • Requires --allow-read permission for file access
  • Some Node.js modules may not be compatible
  • Offline Support: Compiled binaries can embed WASM for offline usage (see Deno Compile Guide)

Bun

  • Relatively new runtime, ecosystem still developing
  • Some npm packages may have compatibility issues

Node.js

  • Requires Node.js 22.6.0 or higher
  • Node.js 24 LTS: add --experimental-wasm-exnref for optimal WASI performance (auto-falls back to Emscripten with a warning if omitted)
  • Node.js 25+: WASI works natively, no flag needed
  • Requires TypeScript loader for direct .ts execution

Browser

  • No direct file system access (security limitation)
  • Wasm files must be served with correct MIME type
  • May require additional build configuration

Cloudflare Workers

  • 128MB memory limit per request
  • 50ms CPU time (free tier) or 30s (paid tier)
  • No file system access
  • Request/response size limited to 100MB

🎯 Deno Compiled Binaries

TagLib-Wasm includes special support for creating offline-capable Deno compiled binaries:

Automatic Offline Support

typescript
import { initializeForDenoCompile } from "taglib-wasm";

// Automatically uses embedded WASM in compiled binaries
// Falls back to network fetch in development
const taglib = await initializeForDenoCompile();

Manual WASM Embedding

  1. Prepare the WASM file:

    typescript
    import { prepareWasmForEmbedding } from "taglib-wasm";
    await prepareWasmForEmbedding("./taglib.wasm");
  2. Initialize with embedded WASM:

    typescript
    import { TagLib } from "taglib-wasm";
    
    const wasmBinary = await Deno.readFile("./taglib.wasm");
    const taglib = await TagLib.initialize({ wasmBinary });
  3. Compile with embedded assets:

    bash
    deno compile --allow-read --include taglib.wasm your-app.ts

Benefits

  • ✅ No network access required
  • ✅ Single executable file
  • ✅ Faster startup (no WASM fetch)
  • ✅ Works in air-gapped environments

Example

See the complete example in examples/deno/offline-compile.ts.

💡 Best Practices

Universal Code

Write code that works across all runtimes:

typescript
// Good: Runtime-agnostic
import { TagLib } from "taglib-wasm";
const taglib = await TagLib.initialize();

// Avoid: Runtime-specific APIs in shared code
// const audioData = await Deno.readFile("file.mp3"); // Deno only
// const audioData = await Bun.file("file.mp3").arrayBuffer(); // Bun only

Error Handling

Account for different runtime capabilities:

typescript
async function loadAudioFile(path: string): Promise<Uint8Array> {
  // Runtime detection
  if (typeof Deno !== "undefined") {
    return await Deno.readFile(path);
  } else if (typeof Bun !== "undefined") {
    return new Uint8Array(await Bun.file(path).arrayBuffer());
  } else {
    // Node.js or browser
    const fs = await import("fs/promises");
    return await fs.readFile(path);
  }
}

Configuration

Use runtime-appropriate configuration:

typescript
const config = {
  memory: {
    // Adjust based on runtime capabilities
    initial: typeof Bun !== "undefined" ? 32 * 1024 * 1024 : 16 * 1024 * 1024,
    maximum: 256 * 1024 * 1024,
  },
  debug: process.env.NODE_ENV === "development",
};

const taglib = await TagLib.initialize(config);

📚 Additional Resources


This universal compatibility ensures taglib-wasm can be used in any modern JavaScript environment, from servers to browsers to edge computing platforms.

Released under the MIT License.