How local-first browser tools work: PDFs, images and workers
A site that merges PDFs and compresses images without ever receiving them sounds like magic. It is just a handful of browser capabilities used well.
The building blocks
Reading files without uploading them
The browser lets a page read a file the user selects or drops through the File API. Calling file.arrayBuffer() gives the bytes in memory, in the page, and no network request is involved.
Writing files back
The result is wrapped in a Blob, and a temporary object URL plus a download link hands it to the user. Again, no server round trip.
Processing
- PDF structure edits (merge, split, rotate, watermark, page numbers) use a pure-JavaScript library,
pdf-lib, that reads and writes the PDF object model directly. - PDF rendering (turning pages into images, detecting blank pages, extracting text) uses Mozilla’s
pdf.js. It runs its heavy parsing in a Web Worker, a background thread, so the page stays responsive. - Images are decoded with
createImageBitmap, drawn on a<canvas>, and re-encoded withcanvas.toBlob()as JPEG, PNG or WebP. The browser’s own optimised codecs do the work. - Hashing and randomness use the Web Crypto API:
crypto.subtle.digestandcrypto.getRandomValues.
Design decisions that matter
Keep memory in check
Everything lives in RAM. Rendering a 300-page PDF at high resolution as images would exhaust memory, so the tools render one page at a time, release each canvas immediately and clamp the maximum canvas size.
Be honest about what is impossible locally
Some things are better done on a server or are not feasible in a browser: OCR on hundreds of scanned pages, cracking passwords, exotic formats. A good local-first tool says so instead of faking it. For example, our unlock tool needs the correct password or a file that only restricts printing, and it says that the unlocked copy is rebuilt from page images.
Never use innerHTML with user data
Filenames and pasted text are untrusted. The UI is built with createElement and textContent, so a file named <img onerror=…> is just text.
Lock the site down with CSP
A strict Content Security Policy means even a compromised dependency cannot quietly phone home: connect-src 'self' restricts network requests to the site’s own origin, and script-src 'self' blocks injected scripts.
Offline by default
A service worker caches the site’s own assets, so a tool you have used keeps working with no connection. For a privacy-focused tool, that is also a nice property to demonstrate: turn off Wi-Fi and it still works.
Trade-offs
Local-first is not free. Performance depends on the visitor’s device, the main thread can stall on huge inputs if work is not chunked or moved to workers, and bundle size matters because the code ships to the client. The answer is code-splitting: each tool is its own lazily loaded module, so a visit to the JSON formatter never downloads the PDF library.