FileTransmuteFree online file converter
EngineeringWebAssemblyPrivacy

How a Client-Side File Converter Works (Technical Explainer)

By Ismail Sab Ajnalkar9 min read

A client-side file converter does its work inside the browser tab: the file is read into memory with the File API, transformed by JavaScript or by a WebAssembly-compiled native library, and handed back as a Blob download. No HTTP request carries the file anywhere, so privacy is not a policy commitment — it is a consequence of the architecture. This is how each layer of that stack actually works.

Step one: the file never becomes an upload

When you drop a file onto a web page, the browser hands the page a File object — a handle to bytes on disk, not a copy on a server. Reading it with the File API or a stream gives you an ArrayBuffer in the tab's memory. From that point the file is just data in a sandboxed process on your own machine, and it stays that way unless the page explicitly makes a network call with it. A client-side converter simply never makes that call.

The output side mirrors this. The result is assembled as a Blob, URL.createObjectURL gives it a local blob: URL, and an anchor click saves it. The download never touches a network, which is why conversion works with the network interface disabled — a useful thing to test.

WebAssembly: how native codecs got into the tab

Most real conversion logic already exists as decades-old C and C++ — libavcodec, libraw, image codecs, PDF renderers. WebAssembly is a portable binary instruction format that lets those libraries be compiled (usually via Emscripten) into a module the browser executes at near-native speed inside its sandbox.

Three properties make this work for conversion. It is fast, because Wasm is JIT-compiled to real machine code rather than interpreted. It is memory-safe by construction: a Wasm module operates on a linear memory buffer it cannot escape, so a malformed file cannot reach outside the tab. And it has no ambient capabilities — no file system, no sockets — unless the host explicitly imports them, which a converter does not.

Images: the Canvas API

Image conversion barely needs a library. Decode the source into an ImageBitmap, draw it to a canvas (or an OffscreenCanvas in a worker, to keep the main thread responsive), and re-encode with canvas.toBlob() specifying the target MIME type and quality. That single pipeline covers JPG, PNG and WebP in every modern browser, and it handles resizing and rotation on the way through by transforming the drawing context.

Formats the browser cannot decode natively fall back to Wasm: HEIC from an iPhone, or camera raw such as CR3 and NEF, are decoded by a WebAssembly build of the relevant C library and then re-enter the same canvas pipeline. That is why HEIC to JPG can run locally at all.

PDFs: pdf.js to read, pdf-lib to write

PDF is two problems, and they need two libraries. Reading a PDF — extracting its text content, rendering a page to pixels — is what pdf.js does: Mozilla's parser and renderer, the same engine behind Firefox's built-in PDF viewer. It gives you a text layer with positions, and it can rasterise any page to a canvas at an arbitrary scale, which is what PDF to JPG and OCR-on-scanned-pages depend on.

Writing a PDF — building the document object model, adding pages, drawing content, saving valid bytes — is what pdf-lib does. Structural operations are pure object-graph manipulation and are close to instant: Merge PDF copies page objects from several documents into one; splitting drops the pages you did not ask for; watermarking draws onto each page's content stream; rotation flips one entry in a page dictionary.

Compression is the interesting case, because almost all of a bloated PDF's weight is embedded images at camera resolution. Compress PDF renders each page with pdf.js at a screen-appropriate DPI, re-encodes the result, and rebuilds the document with pdf-lib. Read engine plus write engine, both in the tab.

Audio and video: FFmpeg compiled to WebAssembly

FFmpeg is the reference implementation of essentially all media conversion, and it has been compiled to WebAssembly. The build is large — on the order of 30 MB for the core — which shapes the whole design: it is loaded lazily, only when you open a media tool, never as part of the main bundle, and the browser's HTTP cache keeps it for subsequent visits. It runs in a Web Worker, so a long transcode does not freeze the UI.

Inside, it is real FFmpeg: a virtual in-memory filesystem receives the input bytes, the usual command-line arguments are passed to it, and the output file is read back out of that virtual FS as a Uint8Array. Stream copies are close to free — MP4 to MP3 mostly just demuxes an existing AAC or MP3 stream — while a genuine re-encode is bounded by your CPU, which is the honest trade for not uploading a gigabyte of video.

Neural models: OCR and speech, on your device

The newest layer is small transformer and CNN models running locally through an ONNX or Wasm runtime, with WebGPU acceleration where the browser exposes it. Weights are fetched once, cached, and executed in the tab. Image to Text runs a recognition model over the line regions of an image; Text to Speech runs a speech model that synthesises audio from a phoneme sequence; Background Remover runs a segmentation model that outputs an alpha mask which is then composited on a canvas.

These are the conversions where the privacy argument is sharpest, because they are exactly the ones people run on sensitive material — the scanned letter, the private note. Local inference means the model comes to the data rather than the data going to the model.

Proving the claim, and admitting the limits

Any site can assert that it does not upload your files. It can also be checked. The straightforward test is to disconnect from the network and convert something anyway. The instrumented version is to wrap fetch, XMLHttpRequest and sendBeacon at page load, tally the bytes of every request body, and display the running total: for a client-side conversion it stays at zero, and that counter is on every File Transmute page.

The limits are real and worth stating. Everything runs in your device's memory, so a very large video can exhaust a tab where a server would not blink. Heavy engines mean a one-off download on first use. And a few things genuinely cannot be done in a browser: high-fidelity Office rendering needs a real Office or LibreOffice engine, and PostScript/EPS rasterisation needs Ghostscript, which is AGPL and belongs on a server rather than in a shipped client bundle. Those tools are labelled as server-based and the uploaded file is deleted immediately after conversion. Everything else — which is nearly everything — has no reason to leave your machine.

Try these tools