# WebAssembly Tools for File Conversion in the p2r3/convert Project

> Discover the WebAssembly tools Pandoc, FFmpeg, and ImageMagick powering client-side file conversions in the p2r3/convert project. Perform conversions without server dependencies.

- Repository: [p2r3/convert](https://github.com/p2r3/convert)
- Tags: deep-dive
- Published: 2026-02-19

---

**The p2r3/convert project leverages seven distinct WebAssembly modules—Pandoc, libopenmpt, SQLite-Wasm, FFmpeg, ImageMagick, and Reflo—to execute client-side file conversions without server-side dependencies.**

The convert repository by p2r3 is a browser-based file conversion tool that relies entirely on WebAssembly tools to process documents, media, databases, and images locally. By compiling established C/C++ and Haskell libraries to Wasm, the project eliminates server-side processing while maintaining support for complex formats like Markdown, tracker modules, and video codecs.

## Pandoc WebAssembly for Document Conversion

The [`src/handlers/pandoc.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/pandoc.ts) handler wraps the **Pandoc** WebAssembly module (`pandoc.wasm`) to convert between document formats. This compiled Haskell binary handles Markdown, HTML, LaTeX, DOCX, and other text-based formats.

The handler imports [`./pandoc/pandoc.js`](https://github.com/p2r3/convert/blob/main/./pandoc/pandoc.js), which fetches `/convert/wasm/pandoc.wasm` at runtime and executes the compiled code. Below is a minimal example of initializing the handler and converting Markdown to HTML:

```typescript
import pandocHandler from "./handlers/pandoc.ts";

const pandoc = new pandocHandler();
await pandoc.init();               // loads pandoc.wasm + JS glue
const input = [{ name: "sample.md", bytes: new Uint8Array(mdData) }];
const out = await pandoc.doConvert(
  input,
  { internal: "markdown", extension: "md", mime: "text/markdown", ... },
  { internal: "html", extension: "html", mime: "text/html", ... }
);
// out[0].bytes contains the produced HTML

```

## libopenmpt WebAssembly for Audio Module Rendering

For converting tracker music formats (MOD, XM, IT, S3M) to WAV, [`src/handlers/libopenmpt.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/libopenmpt.ts) utilizes the **libopenmpt** WebAssembly module. The handler pre-fetches the binary with `fetch("/convert/wasm/libopenmpt.wasm")`, injects it into a global `libopenmpt` object, then loads the companion script [`/convert/wasm/libopenmpt.js`](https://github.com/p2r3/convert/blob/main//convert/wasm/libopenmpt.js).

```typescript
import libopenmptHandler from "./handlers/libopenmpt.ts";

const opener = new libopenmptHandler();
await opener.init();               // fetches libopenmpt.wasm, loads libopenmpt.js
const modFile = [{ name: "track.mod", bytes: new Uint8Array(modData) }];
const wavs = await opener.doConvert(modFile, /* unused */ null, null);
// wavs[0].bytes is a Uint8Array containing a WAV file

```

## SQLite-Wasm for Database Extraction

The [`src/handlers/sqlite.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/sqlite.ts) handler imports `sqlite3InitModule` from the npm package `@sqlite.org/sqlite-wasm` to read SQLite databases and extract tables as CSV files. The underlying Wasm binary is bundled by the package and instantiated on demand, eliminating the need for manual fetching.

```typescript
import sqlite3Handler from "./handlers/sqlite.ts";

const sqlite = new sqlite3Handler();
await sqlite.init();               // registers SQLite-Wasm
const dbFile = [{ name: "my.db", bytes: new Uint8Array(dbData) }];
const csvs = await sqlite.doConvert(
  dbFile,
  { internal: "sqlite3", extension: "db", mime: "application/vnd.sqlite3", ... },
  { internal: "csv", extension: "csv", mime: "text/csv", ... }
);
// each table becomes a separate CSV file in `csvs`

```

## FFmpeg WebAssembly for Media Transcoding

For video and audio transcoding (MP4, MP3, OGG, etc.), [`src/handlers/FFmpeg.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/FFmpeg.ts) drives the **FFmpeg** WebAssembly core. The handler calls `ffmpeg.load({ coreURL: "/convert/wasm/ffmpeg-core.js" })`, which internally pulls the `ffmpeg-core.wasm` binary.

```typescript
import ffmpegHandler from "./handlers/FFmpeg.ts";

const ff = new ffmpegHandler();
await ff.init();                   // loads ffmpeg-core.wasm
const mp4 = [{ name: "video.mp4", bytes: new Uint8Array(mp4Data) }];
const mp3 = await ff.doConvert(
  mp4,
  { internal: "mp4", extension: "mp4", mime: "video/mp4", ... },
  { internal: "mp3", extension: "mp3", mime: "audio/mpeg", ... }
);
// mp3[0].bytes is the transcoded MP3 data

```

## ImageMagick WebAssembly for Image Conversion

The [`src/handlers/ImageMagick.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/ImageMagick.ts) handler utilizes **ImageMagick** compiled to WebAssembly (`magick.wasm`) for converting between image formats like PNG, JPEG, and GIF. The handler fetches the binary via `fetch("/convert/wasm/magick.wasm")`, then passes the bytes to `initializeImageMagick`.

```typescript
import imageMagickHandler from "./handlers/ImageMagick.ts";

const im = new imageMagickHandler();
await im.init();                   // fetches magick.wasm
const png = [{ name: "photo.png", bytes: new Uint8Array(pngData) }];
const jpg = await im.doConvert(
  png,
  { internal: "png", extension: "png", mime: "image/png", ... },
  { internal: "jpeg", extension: "jpg", mime: "image/jpeg", ... }
);
// jpg[0].bytes holds the JPEG image

```

## Reflo WebAssembly for Audio Processing

Inside [`src/handlers/flo.worker.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/flo.worker.ts), the **Reflo** WebAssembly module (`reflo_bg.wasm`) handles specialized audio rendering for the *flo* pipeline. Running inside a Web Worker, the helper `initReflo('/convert/wasm/reflo_bg.wasm')` loads the Wasm module to apply audio filters and processing steps that complement the main conversion workflow.

## How WebAssembly Modules Are Loaded in convert

All WebAssembly assets are managed by a custom build step defined in [`vite.config.js`](https://github.com/p2r3/convert/blob/main/vite.config.js). The `copyWasm` plugin configuration copies compiled binaries into the public `wasm/` folder, ensuring they are reachable at runtime under the `/convert/wasm/` URL namespace.

Each handler follows a consistent initialization pattern:

1. Fetch the `.wasm` binary from the public path (or import from npm for SQLite-Wasm).
2. Instantiate the module using JavaScript glue code or dedicated initialization functions.
3. Expose a `doConvert` method that accepts `Uint8Array` input and returns converted bytes.

This architecture keeps all conversion logic client-side, eliminating network latency and server costs while maintaining compatibility with complex legacy formats.

## Summary

- The **p2r3/convert** project relies on seven core WebAssembly tools: **Pandoc**, **libopenmpt**, **SQLite-Wasm**, **FFmpeg**, **ImageMagick**, and **Reflo**.
- Each tool is wrapped by a TypeScript handler in `src/handlers/` that implements the `FormatHandler` interface.
- Wasm binaries are copied to `/convert/wasm/` by the [`vite.config.js`](https://github.com/p2r3/convert/blob/main/vite.config.js) build process and fetched at runtime.
- All conversions run entirely in the browser, enabling offline-capable, privacy-preserving file transformation.

## Frequently Asked Questions

### What WebAssembly tools does the convert project use?

The convert project uses **Pandoc** for documents, **libopenmpt** for tracker music, **SQLite-Wasm** for databases, **FFmpeg** for video and audio, **ImageMagick** for images, and **Reflo** for advanced audio processing. Each module is compiled from C/C++ or Haskell to Wasm and wrapped by a TypeScript handler.

### How does convert load WebAssembly modules in the browser?

Each handler fetches its corresponding `.wasm` file from the `/convert/wasm/` path at runtime. The [`vite.config.js`](https://github.com/p2r3/convert/blob/main/vite.config.js) file includes a `copyWasm` plugin that moves compiled binaries into the public `wasm/` folder during the build process. Some modules, like SQLite-Wasm, are imported directly from npm and instantiate their Wasm internally.

### Why does convert use WebAssembly instead of server-side conversion?

WebAssembly enables **client-side processing**, which eliminates server costs, reduces network latency, and preserves user privacy by keeping files local. By compiling mature tools like FFmpeg and Pandoc to Wasm, convert can handle complex formats entirely within the browser without installing native software or uploading data to external servers.

### Can I use these WebAssembly tools outside of the convert project?

Yes. Most of these WebAssembly modules are open-source projects compiled for the web. **Pandoc-wasm**, **ffmpeg.wasm**, **@sqlite.org/sqlite-wasm**, and **@imagemagick/magick-wasm** are available as standalone npm packages. You can instantiate them in any JavaScript or TypeScript project using similar fetch-and-initialize patterns shown in the convert handlers.