cd /news/ai-tools/running-pdf-ocr-and-ai-tools-entirel… · home › topics › ai-tools › article
[ARTICLE · art-169205] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Running PDF, OCR and AI tools entirely in the browser: what worked and what broke

A developer built Utilkits, a Next.js 16 site of 99 everyday tools whose PDF, image and on-device AI features run entirely client-side, using pdf-lib, pdf.js, qpdf compiled to WebAssembly, Tesseract.js in a web worker, and Transformers.js with onnxruntime-web. The writeup details two production failures: a self-hosted transformers.web.min.js that imported onnxruntime-web/webgpu by bare module name and fetched its .wasm engine from a CDN blocked by the content security policy, fixed by loading transformers.min.js, copying onnxruntime files into /transformers/ and setting env.backends.onnx.wasm.wasmPaths in place rather than replacing the settings object; and a background remover that broke because Transformers.js 4 lacks U2NetImageProcessor, fixed by preparing inputs in the worker from preprocessor_config.json and reading the model's real output. Each tool page carries a privacy badge that blocks fetch, XMLHttpRequest and sendBeacon and sets Google Analytics' ga-disable flag.

by read3 min views1 publishedNov 13, 2026

Most "free online PDF tools" upload your file to a server, process it there, and delete it an hour or two later. I wanted the opposite for Utilkits: 99 everyday tools, where the PDF and image tools keep the file on your device. Here is how the browser does the heavy lifting, where it falls short, and the two bugs that taught me the most.

It's a Next.js 16 app, and the PDF, image and on-device AI tools run client-side:

Job Library
Merge, split, rotate, watermark, page numbers pdf-lib
Render pages, extract text, PDF to JPG/PNG pdf.js (pdfjs-dist)
Password-protect and unlock (AES-256) qpdf compiled to WebAssembly (qpdf-wasm)
OCR Tesseract.js, in a web worker
PDF to Word / Excel / PowerPoint docx, SheetJS (xlsx), PptxGenJS, rebuilt from the extracted text
Image compression and conversion browser-image-compression and canvas

| On-device AI | Transformers.js with onnxruntime-web (WebAssembly) | The rule I hold myself to: if a tool can't run locally, the page says so. The redirect checker has to follow redirects from a server, so the how it works page lists it as an exception.

BritishWerewolf/U-2-Netp, about 4.6 MB), run in a web worker so the page never freezes.onnx-community/whisper-tiny.en). Xenova/distilbart-cnn-6-6). all-MiniLM-L6-v2, about 24 MB) finds the passages closest to the question. A DistilBERT trained on SQuAD (about 66 MB) then quotes the answer, with its page number. It quotes rather than generates, so it can't invent a citation. If it isn't confident, it says so and shows the closest passages. The models download once from Hugging Face and the browser caches them. The files themselves never leave the device.

The AI tools worked locally and failed in production on first use. The self-hosted transformers.web.min.js imported onnxruntime-web/webgpu by bare name, and no browser can resolve a bare module name without an import map. On top of that, the library's default is to fetch the onnxruntime .wasm engine from jsDelivr, which my content security policy blocks.

The fix: load transformers.min.js instead, copy the onnxruntime files from node_modules into /transformers/ before every build, and point env.backends.onnx.wasm.wasmPaths at that folder.

That fix had its own trap. Set wasmPaths on the existing settings object; don't replace the object. The runtime keeps a reference to the original, so assigning a new object does nothing and the engine still comes from the CDN.

The background remover also "worked" in development and never in production. Transformers.js 4 has no U2NetImageProcessor, and my code read an output name the model doesn't have.

Now the worker prepares the input itself:

preprocessor_config.json. input.1, and read its real saliency map, output 1959. The preparation is a set of pure functions, so the same code runs in the worker and in Node unit tests.

"Your file stays on your device" is easy to say, so every tool page has a privacy badge:

fetch, XMLHttpRequest and navigator.sendBeacon to block the page's own requests, and sets Google Analytics' official ga-disable flag. The badge also suggests the real test: load the page, turn off Wi-Fi, run the tool. If it still works, your file had nowhere to go.

Everything is at utilkits.com. The single-file tools are free, with no account. If you catch a tool sending your file anywhere, tell me in the comments.

── more in #ai-tools 4 stories · sorted by recency
── more on @utilkits 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/running-pdf-ocr-and-…] indexed:0 read:3min 2026-11-13 · —