DEV Community

Jenna Lee
Jenna Lee

Posted on

We built 39 file tools that run entirely in the browser. Here is what worked and what did not.

Most free online converters upload your file, process it on a server and send it back. We wanted the opposite: image, PDF, video, audio, archive and spreadsheet tools where the file never leaves the user's device. The result is Squishly Tools, 39 tools that are all client side, static hosted and available in 40 languages.

Here is what we learned building it.

The stack, per file type

Images. Canvas does most of the work: decode, resize, re-encode to JPEG, WebP or AVIF with canvas.toBlob. HEIC needed a WASM decoder. The surprise was PNG. The browser's own PNG encoder is 32 bit and unoptimised, so "compressing" a PNG through Canvas often made it bigger. We switched to UPNG.js for palette quantisation, which is how services like TinyPNG get their savings.

PDF. PDF.js renders pages, pdf-lib edits them. Merging, splitting, rotating and filling forms are all pure pdf-lib. Compressing scanned PDFs works by re-encoding the embedded page images, because a scan is really just big photos inside a PDF.

Video and audio. A WebAssembly build of FFmpeg handles compression, MOV to MP4, video to GIF and MP4 to MP3. It is the heaviest part of the site, so it only loads when a media tool is opened.

Archives. JSZip for ZIP files, and the native CompressionStream and DecompressionStream APIs for gzip. Native streams are much faster than any JavaScript deflate.

Spreadsheets. SheetJS for Excel, plus small hand written tools for CSV encoding repair, splitting files above Excel's 1,048,576 row limit, and removing duplicates.

The performance bug that looked like a hang

A user reported that compressing a few large PNGs got stuck at 0%. It was not stuck, it was blocking the main thread. Colour quantisation over a 20 megapixel image ran for several seconds per file, the page froze, and the progress bar never repainted.

Three fixes took it from about 36 seconds to about 14 seconds for three 20 MP images, with the page staying responsive:

  1. Move the encoder into a Web Worker. The worker is created from a blob URL and loads the library with importScripts, so no extra build step is needed.
  2. Build the palette from a sample. The quantiser now builds its colour tree from roughly one million evenly spread pixels instead of all twenty million, then maps every pixel to it. Output quality was effectively identical.
  3. Use native compression for the final step. Instead of deflating the image data with pako in JavaScript, the worker filters the rows and hands them to CompressionStream('deflate'), then writes the result back into the PNG's IDAT chunk with a fresh CRC.

Processing up to three files in parallel workers did the rest.

Things that are harder than they look

Memory on phones. Everything happens in RAM, so a 2 GB video that a laptop handles fine can crash a phone tab. We tell users honestly where the limits are rather than pretending there are none.

"Is it really not uploading?" Users are rightly sceptical. The simplest proof is offline mode: load a tool, turn the wifi off, and it still works. We put that test in the copy because it is more convincing than any privacy policy.

Localisation at scale. 40 languages means about 3,500 static pages. The build is a single Node script that renders every tool page, guide and article per locale, writes hreflang tags, and generates the sitemap. Right to left languages get a flipped stylesheet generated from the main one.

Was it worth it?

For users, yes: no account, no upload limits, no waiting in a queue, and private documents stay private. For us, static hosting means the whole site costs almost nothing to run, which is what lets it stay free.

If you are building something similar, the short version is: Canvas and pdf-lib cover more than you expect, FFmpeg in WebAssembly is viable if you lazy load it, and anything slow belongs in a worker.

You can try the tools at squishlytools.com. Questions about any of the above are welcome in the comments.

Top comments (1)

Collapse
 
l4nj1n9 profile image
Lan Jing •

Is it opensourced ?