# How the Convert Repository Performs File Conversions Entirely in the Browser

> Discover how the convert repository achieves in browser file conversions using WebAssembly wrappers for FFmpeg and ImageMagick, eliminating server dependence. Learn about client-side pathfinding for optimal sequences.

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

---

**The convert repository performs file conversions entirely in the browser by chaining WebAssembly (WASM) wrappers for native tools like FFmpeg and ImageMagick, orchestrated through a client-side pathfinding engine that determines optimal conversion sequences without any server interaction.**

The `p2r3/convert` project is an open-source file conversion tool that performs file conversions entirely in the browser, eliminating the need for server uploads or native software installations. By leveraging WebAssembly modules and a sophisticated graph-based routing system implemented in TypeScript, this browser-based converter chains multiple native tools client-side to transform files between dozens of formats.

## Core Architecture of the Browser-Based Converter

### UI and Orchestration Layer

In [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts), the application handles file selection, format detection, and conversion orchestration. The `fileSelectHandler` function (lines 89-151) extracts MIME types from user files and populates the available format lists, while the `convertButton.onclick` handler (lines 311-360) triggers the conversion pipeline that performs file conversions entirely in the browser.

### Format Definitions and Handlers

The [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts) file defines the core interfaces: `FileFormat` describes MIME types and extensions, while `FormatHandler` specifies the contract that every conversion wrapper must implement. Each handler in `src/handlers/` wraps a specific WASM library:

- **FFmpeg.ts**: Wraps the FFmpeg WASM core for video and audio processing
- **ImageMagick.ts**: Handles raster and vector image conversions via the Magick WASM module
- **libopenmpt**: Supports tracker music formats (referenced in the handlers index)

### The TraversionGraph Pathfinding Engine

The [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts) module constructs a directed graph where nodes represent MIME types and edges represent available conversions. When the convert repository performs file conversions entirely in the browser, this graph runs a Dijkstra-style search to find the optimal path, considering factors like lossiness, category changes, handler priority, and previously failed "dead-ends."

The `attemptConvertPath` function in [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts) (lines 171-247) walks the path returned by `TraversionGraph.searchPath()`, calling each handler's `doConvert()` method sequentially until the final format is reached.

## How WASM Enables Serverless Conversion

### WebAssembly Module Loading

Each handler initializes its WASM binary via `fetch()` requests. For example, the FFmpeg handler loads [`/convert/wasm/ffmpeg-core.js`](https://github.com/p2r3/convert/blob/main//convert/wasm/ffmpeg-core.js), while ImageMagick loads `/convert/wasm/magick.wasm`. These binaries execute inside sandboxed WebAssembly virtual machines, ensuring the convert repository can perform file conversions entirely in the browser without executing native code on the host OS.

### Zero-Copy Byte Handling

The repository uses efficient binary handling throughout the pipeline:

1. Input files are read using `File.arrayBuffer()` and converted to `Uint8Array`
2. Handlers receive these buffers directly without string conversion overhead
3. Output buffers return as `Uint8Array` and convert to downloadable Blobs via `URL.createObjectURL()`

### Pure-Client Orchestration

All decision-making, graph construction, and error handling run as ordinary JavaScript/TypeScript on the main thread. The UI remains responsive during long conversions through `requestAnimationFrame` yields, ensuring smooth performance while the convert repository performs file conversions entirely in the browser.

## Practical Implementation Examples

### Standard UI Workflow

The typical user interaction flows through [`index.html`](https://github.com/p2r3/convert/blob/main/index.html) elements controlled by [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts):

```html
<input id="file-input" type="file" multiple hidden />
<div id="file-area">Drop files here or click</div>
<div id="from-list" class="format-list"></div>
<div id="to-list" class="format-list"></div>
<button id="convert-button" class="disabled">Convert</button>

```

When a user drops a file, `fileSelectHandler` extracts the MIME type, auto-selects the matching input format, and populates the format lists. Pressing **Convert** triggers the pipeline that allows the convert repository to perform file conversions entirely in the browser.

### Programmatic Conversion API

Developers can embed the conversion engine directly:

```typescript
import handlers from "./src/handlers/index.js";
import { TraversionGraph } from "./src/TraversionGraph.js";
import { ConvertPathNode } from "./src/FormatHandler.js";

// Initialize all WASM handlers
await Promise.all(handlers.map(h => h.init()));
const cache = new Map<string, FileFormat[]>();
handlers.forEach(h => cache.set(h.name, h.supportedFormats ?? []));

// Build conversion graph
const graph = new TraversionGraph();
graph.init(cache, handlers);

// Define source and target formats (example: PNG to MP4)
const srcFormat = cache.get("ImageMagick")!.find(f => f.mime === "image/png")!;
const dstFormat = cache.get("FFmpeg")!.find(f => f.mime === "video/mp4")!;

// Create path nodes
const fromNode = new ConvertPathNode(handlers.find(h => h.name === "ImageMagick")!, srcFormat);
const toNode = new ConvertPathNode(handlers.find(h => h.name === "FFmpeg")!, dstFormat);

// Find optimal conversion path
const pathIter = graph.searchPath(fromNode, toNode, true);
const path = (await pathIter.next()).value;

// Execute conversion chain
let fileData = [{ name: "example.png", bytes: await pngFile.arrayBuffer() as Uint8Array }];
for (let i = 0; i < path.length - 1; ++i) {
  const handler = path[i + 1].handler;
  const inFmt = path[i].format;
  const outFmt = path[i + 1].format;
  fileData = await handler.doConvert(fileData, inFmt, outFmt);
}

```

This mirrors the `attemptConvertPath` logic in [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts) while exposing the underlying `TraversionGraph` API for custom workflows that perform file conversions entirely in the browser.

## Summary

- The **convert repository** performs file conversions entirely in the browser by chaining WebAssembly wrappers for native tools like FFmpeg and ImageMagick.
- The **TraversionGraph** in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts) implements a Dijkstra-style pathfinding algorithm to determine optimal conversion sequences across multiple handlers.
- **Zero-copy byte handling** ensures efficient data flow from `File.arrayBuffer()` through WASM handlers to downloadable output Blobs without server interaction.
- Each **FormatHandler** in `src/handlers/` initializes its WASM module via `fetch()` and executes conversions through sandboxed WebAssembly virtual machines.
- The UI layer in [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts) orchestrates the entire pipeline from file selection through path execution to final download, all within the browser context.

## Frequently Asked Questions

### How does the convert repository determine which conversion path to use?

The repository uses the **TraversionGraph** class in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts) to construct a directed graph where nodes represent MIME types and edges represent possible conversions provided by handlers. When processing a request, it runs a Dijkstra-style search that considers factors like lossiness, category changes, handler priority, and previously failed "dead-ends" to find the optimal sequence. The `searchPath()` method returns an iterator of possible paths, allowing the system to attempt alternatives if intermediate conversions fail.

### What WebAssembly libraries power the browser-based conversions?

According to the handler implementations in `src/handlers/`, the repository wraps several industry-standard native tools compiled to WebAssembly. These include **FFmpeg** ([`src/handlers/FFmpeg.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/FFmpeg.ts)) for video and audio processing, **ImageMagick** ([`src/handlers/ImageMagick.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/ImageMagick.ts)) for image format conversions, and **libopenmpt** for tracker music formats. Each handler fetches its respective WASM binary—such as [`/convert/wasm/ffmpeg-core.js`](https://github.com/p2r3/convert/blob/main//convert/wasm/ffmpeg-core.js) or `/convert/wasm/magick.wasm`—and executes conversions inside sandboxed WebAssembly virtual machines.

### How does the convert repository handle large files without freezing the browser?

The repository maintains UI responsiveness through several mechanisms. All conversion orchestration in [`src/main.ts`](https://github.com/p2r3/convert/blob/main/src/main.ts) uses pure JavaScript on the main thread with `requestAnimationFrame` yields to prevent blocking during long operations. The system employs **zero-copy byte handling**, using `File.arrayBuffer()` and `Uint8Array` buffers to stream data directly into WASM handlers without string conversion overhead. Additionally, the `TraversionGraph` pathfinding runs client-side, allowing the browser to manage memory and processing without server round-trips that could introduce latency.

### Can developers add support for new file formats to the convert repository?

Yes, developers can extend the system by implementing the **FormatHandler** interface defined in [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts). A new handler must expose a `name` property, a `supportedFormats` array describing MIME types and extensions, an `init()` method that loads the WebAssembly module via `fetch()`, and a `doConvert()` method that accepts `Uint8Array` input and returns converted `Uint8Array` output. After implementation, the handler must be exported from [`src/handlers/index.ts`](https://github.com/p2r3/convert/blob/main/src/handlers/index.ts) to be automatically registered with the `TraversionGraph` during initialization, immediately making its conversion capabilities available to the pathfinding engine.