DEV Community

HONG XIAO
HONG XIAO

Posted on

I built 260+ web tools that never upload your files. Here's what actually runs in a browser tab in 2026.

There is something quietly absurd about the standard "free online tool" flow.

You want to shrink a 40 MB photo. So you upload 40 MB to a stranger's server, wait in a queue behind everyone else who had the same idea, download a 2 MB file back, and hope the original got deleted. You moved 42 MB across the internet to run an operation your laptop could have finished in 300 ms without the network ever waking up.

That was the itch. I ended up scratching it for about a year, and the result is ToolMole — 260-odd tools that run entirely inside the browser tab. No upload step. Not "we delete your files after an hour" — there is no server-side file handling to delete anything from.

This post is mostly about the engineering, including the parts that didn't work.

The constraint that shaped everything

One rule: the file never leaves the device.

That sounds like a privacy feature, and it is one, but it's more useful as an architectural constraint. It rules out the entire category of "just shell out to ImageMagick on a box somewhere," which is what almost every tool site actually is behind the marketing. Once you can't do that, you have to answer a harder question for every single tool: can this genuinely run in a browser?

The honest answer, tool by tool, turned out to be "yes" far more often than I expected in 2020, and still "no" in a few places that matter. More on those below.

What the stack actually is

No framework. The tool pages are vanilla JS with a shared runtime for file handling, worker orchestration, and the drop-zone UI. Each tool lazy-loads only the WASM it needs, because bundling a video encoder into a JSON formatter is how you end up with a 30 MB "lightweight" tool page.

The heavy lifting splits roughly three ways:

WebAssembly for codecs and file formats. Image encode/decode, video transcoding, PDF manipulation, archive handling. This is the boring, load-bearing 80%. The WASM ecosystem for media is genuinely good now — the same C libraries the desktop tools use, compiled once, shipped as a static asset, cached forever.

Web Audio API for anything sound-shaped. Pitch shifting, EQ, reverb, BPM detection, the spectrum analyzer. Browsers have had a real DSP graph built in for over a decade and it is wildly underused outside of games. A ten-band EQ applied to an uploaded file is maybe 40 lines of BiquadFilterNode wiring.

WebGPU where it earns its place. Mostly image filters and pixel-level work where a shader beats a for loop over an ImageData buffer by an order of magnitude, with a Canvas2D fallback for browsers that haven't caught up.

Everything heavy runs in a Web Worker. This is non-negotiable and I learned it the annoying way: a 200 MB video decode on the main thread doesn't just drop frames, it makes the tab look crashed, and users close crashed tabs.

Four things that were harder than they should have been

  1. SharedArrayBuffer and the cross-origin isolation tax.

Multi-threaded WASM needs SharedArrayBuffer, which needs cross-origin isolation, which means serving Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. Do that globally and you break every third-party embed, analytics script, and font CDN you have.

The fix is to isolate per-route rather than site-wide, so the video and image pages get the strict headers and the rest of the site stays normal. It's a header-config problem, not a code problem, but it cost me a full weekend of "why is this fast on localhost and single-threaded in production."

  1. Memory ceilings are real and they arrive without warning.

WASM32 gives you a 4 GB address space, and browsers will happily hand you an allocation failure well before that on a phone. A naive "read the whole file into an ArrayBuffer, hand it to the encoder" pipeline dies on a 2 GB video.

Anything large has to stream: chunked reads, incremental encode, write out through a WritableStream or into OPFS rather than accumulating the whole output in memory. This rewrite touched more tools than any other single change I made.

  1. Safari.

Predictable, but worth naming specifically because HEIC is the case that bites hardest. The format that every iPhone shoots in is the format with the thinnest browser decoder support, so an "iPhone photos to JPG" tool that runs client-side needs its own decoder shipped as WASM. Which is fine. It's just funny that converting Apple's own format is the part Apple's browser helps with least.

  1. Progress bars are a UX problem, not a technical one.

With no server, there's no upload percentage to show. Users have been trained by twenty years of tool sites to read "0%" as "nothing is happening" and hit refresh. I had to add deliberately granular progress reporting out of the worker — not because it's needed, but because instant, silent completion reads as failure.

Where the model breaks, and I'm not going to pretend otherwise

Six of the tools are AI tools: image generation, background removal, upscaling, face restoration, photo restoration, voice cloning.

Those upload. They have to. A diffusion model or a voice-cloning network is not running in a browser tab in any form a normal user would tolerate, and shipping a multi-gigabyte model over the wire to avoid a 2 MB upload is not privacy engineering, it's cosplay. Those run on rented GPUs.

What I decided was: say so on the page, above the button, in the same type size as everything else. Not in the privacy policy, not in a footnote, not in grey 11px text under the fold. If the honest answer is "this one is different," the honest answer belongs where the user is standing when they make the decision.

I'd rather lose the clean "nothing is ever uploaded" tagline than have it be technically-true-with-an-asterisk.

What's live

Roughly 265 tools across: image (39), calculators (59), developer utilities (40), audio (27), video (17), text (18), design/CSS (15), PDF (10), print/education (9), hardware tests (9), plus notes apps, archives, and a few games.

The developer-facing set is probably the most relevant to this crowd — JSON/YAML/XML formatting, JWT decoding, regex testing, cron parsing, hashing, diffing, base conversion, subnet math, minify/beautify for JS/CSS/HTML. All of it local, which for JWT decoding and hashing in particular is the difference between a tool you can use on work tokens and one you can't.

No accounts, no quotas, no watermarks, no file-size caps. 13 languages.

👉 toolmole.com

What I'd like from you

Genuinely, the useful feedback is the breakage:

Which tool did you throw a weird file at, and how did it fail?
Anything unusable on your browser/OS combination?
Which tool in your workflow still forces you through an upload step? That's my roadmap.

There's a feedback page, or just leave it in the comments here — I read those.

If you're building something similar: the short version is that the browser is a far more capable file-processing environment than most of us are still designing for, the constraint of "no server" makes the architecture better rather than worse, and the day you need SharedArrayBuffer is the day you should read the COOP/COEP spec properly instead of pasting headers from Stack Overflow. Ask me how I know.

Top comments (0)