As a developer, I do the thing you're supposed to do: I open devtools and watch the network tab. A few months ago I needed a quick image compressor, picked a popular one, and dropped a file in. Sure enough, a POST request with my full-resolution image went straight to some API endpoint.
The privacy policy said files get deleted after an hour. I had no way to verify that. Neither do you.
That gap between what a policy promises and what the network tab can prove is what bothered me. So I built the alternative: Compressify, a free image compressor where nothing ever leaves your browser. After the app loads, the only requests you'll see are analytics pings. Your images stay in tab-local memory, get compressed by WebAssembly codecs, and come back as downloads.
This post is about what it actually took to build that.
Standing on Squoosh's shoulders
I didn't write the codecs. Google's Squoosh team already proved browser-side image compression works, and the jSquash project packages those same encoders for the web: AVIF, MozJPEG, JPEG XL, PNG, and WebP, all running natively in the browser. On top of those outputs, the app reads HEIC/HEIF (iPhone photos), GIF (first frame), and SVG as input.
The codecs live in a worker pool of three, so large batches compress in parallel without freezing the UI. Cancellation is wired through the whole pipeline: queued jobs are dequeued instantly, and in-flight jobs kill and replace their worker, which frees the WASM heap immediately instead of letting a long AVIF encode run into the void.
Target file size is a binary search, not a guess
A target file size mode can't ask a server to "make it fit." Everything has to happen client-side, so the approach is: encode at some quality, measure the output, adjust, repeat.
Binary search over the quality parameter converges fast (quality to size is roughly monotonic) and lands the highest quality that fits under the limit. Drop in a file, ask for 100 KB, and instead of guessing percentages you get the best-looking file that passes.
The offline problem
A client-side app should survive with no internet, so Compressify is a PWA with a service worker precaching everything. That hit a wall fast: the WASM codecs are heavy, and the HEIC decoder alone is about two megabytes. Precaching that for every visitor, including the majority who will never touch a HEIC file, felt wrong.
The fix was lazy-loading codecs per format. The HEIC decoder only downloads when you actually drop a HEIC file, then gets runtime-cached for offline use. If you never process iPhone photos, you never pay that bandwidth cost.
Privacy claims need auditing, not adjectives
"Nothing gets uploaded" is easy to type and harder to keep true. Fonts, analytics, error trackers: every dependency is a potential leak. I ended up auditing every network call the app makes. Two results of that audit:
- Elements that render user file names carry
data-clarity-mask="true", so session recordings never capture them. Analytics payloads contain file types and counts, never names or contents. - The domain never sends email, so its DMARC policy is set to reject at DNS level. Nobody can spoof mail from it either.
Perfect Lighthouse scores turned out to be a maintenance decision, too. The homepage is prerendered at build time with a Vite plugin and hydrated on the client, which keeps Largest Contentful Paint around 0.8 seconds. Every feature I add since then threatens that number. My previous post covers that grind in detail: a font-display: optional CLS bug, an analytics shim that silently dropped every event because gtag.js only processes arguments objects, and how prerendering without breaking hydration actually works.
What it adds up to
No sign-up, no watermark, no limits, no uploads, works offline as an installable PWA, and a network tab that stays quiet. Input formats cover iPhone photos, GIFs, and SVG; output formats cover everything modern; and you can target an exact file size, resize, compare formats, or grab the whole batch as a ZIP.
If you ever need to shrink a file and want it to stay on your device, it's at https://compressify.org. And if you've built something where the privacy claim required auditing your dependencies the same way, I'd genuinely like to hear how you handled it.
Top comments (0)