Skip to content

Scorpio3310/compress-pro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Compress Pro | Private, In-Browser Image, Video, PDF, Audio & Font Compressor

License Privacy SvelteKit Cloudflare WebAssembly Vite Tailwind CSS

Keywords: image compressionpdf compressionvideo compressionwebpavifheicremove exiffont converterttf to woff2font subsettingvariable fontwebassemblywebcodecsclient-sideprivateno upload

Compress Pro — private, in-browser file compressor

Compress images, video, audio and PDFs — convert and subset fonts — and strip EXIF, entirely in your browser. No uploads, no ads, no login, nothing stored server-side. Free & open source — files never leave your machine.

👀 Overview

Compress Pro is a free, open-source, private-by-design file compressor. It shrinks images, video, audio and PDFs — converts and subsets fonts (TTF/OTF/WOFF/WOFF2), creates and extracts archives (ZIP, 7Z, TAR, tarballs; opens RAR, ISO, CAB, DEB, RPM and more), and strips EXIF metadata — running entirely in the browser via WebAssembly + WebCodecs. Fonts especially benefit from the privacy angle: they are licensed software most EULAs forbid handing to third-party servers.

The whole app is prerendered and served as static assets, so files never leave the machine. Compression runs in Web Workers, so the UI stays responsive. There are no ads, no login and nothing stored server-side — privacy is the product's core promise and its SEO angle. It deploys to Cloudflare Workers for ~$0/month.

🚀 Features

  • 🖼️ Images — JPG, PNG, WebP, GIF, HEIC, SVG. Squoosh codecs (jSquash): MozJPEG with trellis quantization (3–6% smaller at the same quality), libwebp (effort 6), libavif, oxipng; lossy PNG via libimagequant (icodec, quality 100 stays lossless). Optional max-dimension downscale (Lanczos3) and target-size mode ("fit under 500 KB", binary search).
  • 📄 PDF — Ghostscript 10 in wasm (@okathira/ghostpdl-wasm): explicit DPI + JPEG-quality control, bicubic downsampling, duplicate-image detection, font subsetting, metadata stripping. 5 preset levels or target size ("fit under 2 MB") via binary search over a quality ladder. Before/after page-1 comparison via pdf.js.
  • 🎬 Video — compress and convert MP4/MOV/WebM/MKV via WebCodecs (the browser's hardware encoders) orchestrated by mediabunny — no ffmpeg, ~realtime encodes. H.264 (MP4/MOV), VP9 (WebM) or animated GIF output, quality or target-size mode, longest-side downscale, fps cap, remove-audio; audio otherwise transmuxes losslessly when container-legal. GIFs convert back to silent MP4/MOV/WebM too.
  • 🎵 Audio — compress and convert to MP3/M4A/WAV/FLAC/OGG/OPUS/WEBA — from any audio the browser can demux (LAME wasm for MP3, libFLAC wasm for FLAC, FFmpeg-AAC wasm fallback where WebCodecs lacks AAC), extract audio from video (MP4 → MP3/WAV), bitrate or target-size mode.
  • 🔤 Fonts — convert between TTF, OTF, WOFF, WOFF2 (and legacy EOT) as lossless repackaging of the raw font tables (Google's woff2 codec in wasm — glyphs, kerning and hinting survive byte-for-byte); subset fonts to chosen character sets or exact text via HarfBuzz (kerning/ligatures among kept glyphs preserved), and pin variable fonts to static instances per axis. Outlines are never converted lossily — a CFF font asked to become TTF keeps its honest .otf extension.
  • 🔐 PDF unlock & protect — remove a known password or add one (128-bit encryption) — the password never leaves the device.
  • 📦 Archives — create ZIP, 7Z (optional AES-256 + hidden file names), TAR, TAR.GZ/BZ2/XZ and per-file gz/bz2/xz; extract ZIP, RAR (v4+v5), 7Z, tarballs, ISO, CAB, DEB, RPM, CPIO, LHA, ARJ and .Z — password-protected archives included; convert archive→archive (RAR → ZIP, ZIP → 7Z…) with folder structure preserved. Plain ZIP stays on fflate; everything else runs 7-Zip compiled to wasm (7z-wasm) in a worker, with nested payloads (deb → data.tar → files) unwrapped automatically.
  • 🧹 EXIF removal — lossless byte surgery on JPG/PNG/WebP: EXIF/GPS, XMP, Photoshop blocks, comments and PNG text chunks are cut out with pixels copied verbatim; ICC profiles kept by default (toggle), orientation re-embedded so photos never turn sideways. Each result row discloses what was found.
  • ✨ Auto format (default) — on the JPG/PNG/WebP tabs the worker races MozJPEG vs WebP (and AVIF for images ≤4 MP under cross-origin isolation) per image and keeps the smallest file; animation stays WebP, transparency never falls to JPEG, and the untouched original wins when nothing beats it.
  • ⚡ Parallel + threaded codecs — batches fan out across a pool of image workers with per-row live status, and the app is cross-origin isolated (COOP/COEP), which activates the bundled multithreaded AVIF and oxipng builds.
  • 📴 Offline PWA — a service worker precaches the shell and prerendered pages and runtime-caches each codec wasm on first use, so the app keeps working offline — any tool used once compresses with no connection at all.
  • 🧰 PDF tools — merge PDFs (reorderable, optional compress-after-merge), extract/remove pages (1-3,7,12-), render pages to JPG/PNG (72/150/300 DPI, multi-page → ZIP), and build a PDF from images.
  • 🗜️ Batch + ZIP — multi-file batches keep input order; download everything as a ZIP (fflate, client-side).
  • 📋 Clipboard & drag-drop — paste files/screenshots anywhere (⌘V / Ctrl+V routes them to the right tab), drop files anywhere on the page, and copy compressed images back to the clipboard.

🧰 Tools & Converters

Every tool is its own real, prerendered page.

Compress

Tool Engine
Compress JPG MozJPEG + trellis
Compress PNG oxipng / libimagequant
Compress WebP libwebp
Compress GIF gifsicle
Compress HEIC libheif (icodec)
Compress SVG svgo
Compress PDF Ghostscript
Compress Video WebCodecs + mediabunny
Compress MP4 WebCodecs + mediabunny
Compress MOV WebCodecs + mediabunny
Compress any image Auto format race
Compress JPG to 100 KB target-size search
Compress Audio mediabunny + LAME / libFLAC
Convert fonts raw sfnt repack + Google woff2
Subset font HarfBuzz subsetter
Variable font → static HarfBuzz instancer
Zip & Unzip files fflate + 7-Zip wasm
Create 7Z 7-Zip wasm, AES-256
Create TAR.GZ tar + gzip/bzip2/xz
Extract RAR unRAR-derived decoder
Extract ISO ISO9660/Joliet/UDF reader
Resize images Lanczos3 downscale
Remove EXIF lossless byte surgery

Convert

🛠️ Tech Stack

🏗️ Architecture

  • src/lib/workers/ — Web Workers (gs, image, svg, video, font, archive); rpc.ts lazy-spawns a pool of image workers (least-busy routing, capped at min(cores, 4)) with typed payload/result/progress contracts in protocol.ts. src/lib/compress.ts schedules files through runWithConcurrency (src/lib/concurrency.ts), keeps input order (rows/ZIP), and supports cancel — in-flight worker calls abort while finished files keep their results.
  • src/lib/codecs/ — main-thread facades; pdf.ts holds the Ghostscript argument builder and target ladder; the shared binary search lives in target-search.ts.
  • Fully prerendered — every request is served from the free static-asset layer; content-hashed, immutable-cached wasm downloads once.
  • SEO — every format and converter is a real prerendered route served by the single src/routes/[[tool=tool]]/+page.svelte, so tab switches never remount. All copy lives in src/lib/seo.ts (FORMATS + CONVERTERS + TOOLS). Adding a tool means extending prerender.entries in svelte.config.js.
  • robots.txt is served by the Worker per request — only the canonical host (PUBLIC_SITE_URL) gets Allow; any other host (preview, staging) gets Disallow: /, so non-production deployments can never be indexed.
  • llms.txt — a prerendered llms.txt index of every tool page, generated from seo.ts like the sitemap, so AI answer engines (ChatGPT, Perplexity) can cite the right page directly. Its companion /llms-full.txt inlines the full content of every page in one document; both are referenced as comments in the production robots.txt.
  • Agent-ready surface — every tool page has a prerendered markdown twin (append .md to its URL; homepage: /index.md; emitter: src/lib/markdown.ts). The production robots.txt declares Content-Signal: ai-train=yes, search=yes, ai-input=yes, _headers adds an RFC 8288 Link header pointing at llms.txt and the skills index, /.well-known/agent-skills/ serves a digest-verified Agent Skills index (src/lib/agent-skills.ts), and pages register WebMCP tools (src/lib/webmcp.ts) in browsers that expose navigator.modelContext (or document.modelContext). Accept: text/markdown negotiation serves that same twin at the page's own URL: a thin Worker wrapper (worker/index.js, wrapping the adapter worker and scoped by run_worker_first in wrangler.jsonc so it only fires for HTML navigations) returns the prerendered .md — with Content-Type: text/markdown, Vary: Accept and an x-markdown-tokens estimate — while browsers keep HTML by default. The .md twins stay directly fetchable too.
  • DNS-AID — DNS-layer discovery via SVCB records under _agents (_index._agents, _a2a._agents) that point agents at this HTTPS surface. DNS lives in Cloudflare (no DNS-as-code), so it's published with pnpm dns:aid (scripts/dns-aid.mjs) — see docs/dns-aid.md for the records, publishing, DNSSEC, and verification. The records are honest: no server-side A2A/MCP endpoint is claimed (the app is 100% in-browser).

📦 Setup

Requires Node ≥ 22.12 and pnpm 10 (packageManager is pinned; engine-strict is on).

pnpm install

🛠️ Development

pnpm dev      # vite dev
pnpm check    # svelte-check
pnpm lint     # prettier --check + eslint
pnpm format   # prettier --write
pnpm test     # vitest (pure-logic unit tests)
pnpm preview  # build + wrangler dev (workerd-faithful, http://localhost:8787)
pnpm og       # regenerate the Open Graph images (after copy changes in generate-og.mjs)
pnpm icons    # regenerate favicon.ico / apple-touch / PWA icons from static/favicon.svg
pnpm demo-assets  # regenerate the before/after demo proofs (runs each sample through the real tool page)
pnpm bench:memory # peak-memory benchmark harness → docs/memory-bench.md

🧪 E2E + visual report

The compression pipelines run as WASM in the browser, so their correctness is tested end-to-end with Playwright (Chromium) against the real UI: upload → settings → compress → download → verify bytes in Node (sharp / pixelmatch for format, dimensions, frames, alpha and pixel diffs vs e2e/thresholds.ts; pdf-lib page-size fingerprints; fflate ZIP inspection).

pnpm test:e2e          # full suite (builds the visual report)
pnpm test:e2e:smoke    # @smoke subset (<30 s)
pnpm test:e2e:quick    # skips @slow tests + the real-file suite — fast iteration
pnpm test:e2e:xbrowser # @xbrowser capability smoke on Firefox + WebKit (needs `playwright install firefox webkit`)
pnpm test:e2e:ui       # Playwright UI mode
pnpm test:e2e:preview  # same suite against the built app on wrangler (:8787)
pnpm report            # rebuild + open the visual report
pnpm fixtures          # (re)generate synthetic inputs into tests/fixtures/generated/
pnpm check:e2e         # typecheck the e2e suite (tsc -p e2e/tsconfig.json --noEmit)
  • Fixtures are deterministic and self-verified at generation time (scripts/generate-fixtures.mjs). Real-world samples go in tests/fixtures/real/ (git-ignored, ~160 MB local) and unlock extra tests; everything skips cleanly when they're absent.
  • Visual report at test-results/report/index.html — every case as a card with original vs output side by side, pixel-diff toggle, metrics and warnings, sorted worst-first and filterable. Global setup wipes the report data on EVERY playwright invocation, so a complete report only comes out of a full pnpm test:e2e run — quick/smoke runs leave a partial one.
  • E2E_CALIBRATE=1 pnpm test:e2e records pixel-diff ratios without failing on budgets — use it when recalibrating e2e/thresholds.ts.

🌐 Deployment

npx wrangler login
pnpm deploy   # build + publish to compress-pro.com

Deploys to Cloudflare Workers on the compress-pro.com custom domain, provisioned automatically from wrangler.jsonc (workers_dev is off to avoid a duplicate indexable host). The free plan is plenty — everything is served as static assets, which are free and unlimited.

Build provenance

Every build bakes two Vite define constants into the bundle, shown in the site footer:

  • __BUILD_DATE__ — ISO date of the build.
  • __COMMIT__ — the exact git commit: WORKERS_CI_COMMIT_SHA on Cloudflare's Git-integrated builds, git rev-parse locally (suffixed -dirty if the working tree has uncommitted changes), dev outside a repo.

Production is built automatically from this repository, so the footer hash links to the precise source state a deployment is running — anyone can cross-check the live site against the repo.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run pnpm check && pnpm lint && pnpm test
  5. Submit a pull request

📄 License

MIT © Nik Klemenc

Note

The app code is MIT, but several bundled WASM engines are copyleft: Ghostscript (AGPL-3.0), gifsicle (GPL-2.0), LAME and FFmpeg's AAC encoder (LGPL), and the HEIC / lossy-PNG codecs inside icodec (GPL/LGPL family). The permissive rest: MozJPEG / libwebp / libavif / libFLAC (BSD-style), oxipng (MIT), and others. This repository publishes the complete corresponding source, which is what the copyleft licenses require — but if you redistribute a build (especially closed-source or modified), those terms apply to you too. Full per-package breakdown: THIRD_PARTY_LICENSES.md.

🙏 Acknowledgments

Built on the work of jSquash, icodec, @okathira/ghostpdl-wasm, mediabunny, gifsicle-wasm-browser, svgo, pdf-lib and pdf.js.