How I Built GlassCompress
After GlassPad, I kept hearing the same request: “Can you shrink this photo before I email it?” Most online compressors want an upload. Many strip EXIF in ways you cannot see. Passport and social sizes are buried behind paywalls. GlassCompress is my answer — a free tool at glasscompress.madushka.com that runs entirely in your browser. Nothing leaves your device.
The problem
Travel clients send me huge JPEGs from phones. Job applications need passport-size crops. Social posts want exact aspect ratios. Desktop tools work, but not everyone has one installed — and uploading personal photos to a random website feels wrong.
- No server upload — decode and encode in-memory only
- Live output size while you drag quality or crop
- Presets for passport, Instagram, Facebook, YouTube, OG images, and common ratios
- HEIC/HEIF decode so phone photos work without a desktop converter
- Flip/mirror, Help modal, GlassPad handoff, and installable PWA
- Batch ZIP and image→PDF for paperwork
Stack and architecture
Same philosophy as GlassPad: static HTML, CSS, and vanilla JavaScript. No React, no build step for the tool itself. It ships inside madushka.com on a Cloudflare Worker that routes glasscompress.madushka.com to the same assets as the legacy /tools/compress/ path (301 redirect to the subdomain).
The encode path uses a dedicated Web Worker with OffscreenCanvas so heavy JPEG/WebP/PNG (and AVIF where supported) work stays off the main thread. HEIC lands via a client-side decoder before that pipeline. The main script handles UI, debounced re-encode, crop/flip overlay, Help, and downloads.
Pipeline: decode → crop → resize → encode
Each dropped file becomes an in-memory item: original dimensions, blob URL for preview, optional per-item rotation and crop rectangle. On every settings change, the pipeline:
- Draws the source image to a canvas (EXIF orientation handled at decode)
- Applies the crop region in pixel space
- Resizes to preset output or max-edge cap
- Encodes with quality slider or binary-searches toward a target KB
Revoking blob URLs too early blanked the preview on first ship. Keep object URLs alive until the item is removed — a small lifecycle detail that matters a lot in UX.
Crop UX (v1 → v2)
Early feedback: the crop frame felt “stuck” — users could drag but had no obvious save path and on mobile the stage sat below the fold. Fixes shipped in layers:
- Toolbar with Reset frame and Download selection under the crop stage
- Compact hero when images are loaded; scroll crop into view on mobile
- Use full image one-click, arrow-key nudge, pinch-to-zoom, double-tap to reset zoom
Presets lock aspect ratio (passport mm/cm with DPI, 1080×1350 posts, story, covers). Free mode keeps all handles. The crop math lives in one module; the overlay is pure DOM + pointer events.
Presets and batch
Presets are data, not hard-coded UI branches — passport sizes, social templates, and ratio locks (1:1, 4:5, 16:9). Batch mode reuses the same settings per item, caps at 20 images, and zips with JSZip. PDF export uses pdf-lib for A4 sheets when clients need print-ready pages.
No analytics on file contents. No upload endpoint. The Worker only serves static files and SEO feeds — the compression never hits my server.
Branding and discovery
GlassCompress follows the GlassPad pattern: product subdomain, glass UI, shared madushka.com footer links, tools hub card, command palette entry, case study at /projects/glasscompress/, and its own robots.txt + sitemap.xml on the subdomain for Search Console.
What I'd do differently
- Ship crop toolbar in v1 — users need an obvious “done” affordance
- Treat HEIC as day-one scope — most phone photos arrive as HEIC, not JPEG
- Automated visual tests for crop overlay on narrow viewports
- Document GlassPad ↔ GlassCompress handoff in the first Help pass, not as a later footnote
If you build your own
- Keep files on-device and say so loudly.
- Show output bytes before download — trust beats mystery.
- Invest in crop on mobile; compression is the easy part.
- Use a Worker for routing and SEO, not for user images.
That is the GlassCompress playbook: constrain the stack, decode HEIC early, encode in a worker, polish crop and flip, hand off to GlassPad when needed, never upload the photo.
Try it at glasscompress.madushka.com — or read How I Built GlassPad for the sibling story.