Dev.to WebDev 🛠 Dev 👁 0 📖 6 min read

I Built a Privacy-First WhatsApp Image Resizer That Never Uploads Your Photos

The Problem That Started It All You've been there. You take the perfect photo, set it as your WhatsApp profile picture, and... WhatsApp butchers it. The crop cuts off half your face. Your Status image has weird black b

The Problem That Started It All

You've been there. You take the perfect photo, set it as your WhatsApp profile picture, and... WhatsApp butchers it. The crop cuts off half your face. Your Status image has weird black bars. Your sticker is "too large" at 500 KB.

I got tired of opening Photoshop every time I wanted to resize an image for WhatsApp. So I built WaSize — a free, privacy-first WhatsApp image resizer that runs entirely in your browser.

No uploads. No signups. No watermarks. No server-side processing.

Every pixel is processed locally using the Canvas API. Close the tab, and your image is gone forever.

What WaSize Does

WaSize handles every WhatsApp image format you'll encounter:

Type Size Ratio
Profile Picture (DP) 640 × 640 1:1
High Quality DP 800 × 800 1:1
Status 1080 × 1920 9:16
Sticker 512 × 512 1:1
Group Icon 640 × 640 1:1
Channel Cover 1080 × 360 3:1
Catalog Image 1080 × 1080 1:1
Chat Wallpaper 1080 × 2400 9:20

But it's not just preset sizes. You get:

  • Crop, Fit, No-Crop, and Stretch modes — choose how your image fills the frame
  • Blurred background fills — when you use No-Crop, empty space gets a gorgeous soft blur of your own photo
  • Circle preview — see exactly what WhatsApp's circular DP crop will look like before downloading
  • Safe area overlay — for Status images, shows where WhatsApp's top/bottom bars will obscure content
  • Target file size compression — set a limit (100 KB, 200 KB, etc.) and the binary search algorithm finds the best quality that fits
  • JPG, PNG, and WebP export with quality control
  • Zoom, rotate, and flip with smooth preview
  • EXIF stripping — location data and camera metadata are automatically removed

The Tech Stack (Deliberately Boring)

Here's the thing — there's no React. No Next.js. No build step. No node_modules folder with 847 packages.

WaSize/
├── index.html          # Single-page app
├── css/style.css       # Vanilla CSS, light/dark mode
├── js/
│   ├── app.js          # UI wiring, presets, controls (~1200 lines)
│   ├── image-editor.js # Canvas rendering engine (~660 lines)
│   └── compression.js  # Encoding + binary search (~200 lines)
├── images/             # Favicons, OG images
└── vercel.json         # Deployment config

Total JavaScript: ~2,100 lines. That's it. The whole app.

Why vanilla JS? Because:

  1. Zero build time — edit a file, refresh the browser
  2. No framework churn — this code will work in 2030 without migration
  3. Smaller payload — users on slow connections get a working tool faster
  4. Full control — Canvas operations need precise timing; no virtual DOM overhead

The Architecture: Three Modules, Zero Dependencies

1. compression.js — The Binary Search Compressor

This is the module I'm most proud of. When a user sets a target file size (say, 100 KB for a sticker), naive approaches either overshoot or destroy quality. WaSize uses binary search over quality values:

// Simplified version of the actual algorithm
// The real code runs 6 binary-search iterations
// for ~1.5% quality precision

var SEARCH_STEPS = 6;
var MIN_QUALITY = 0.05;
var SOFT_FLOOR = 0.55;

// Binary search: find the highest quality
// that produces a file under the target size
for (var step = 0; step < SEARCH_STEPS; step++) {
  var midQuality = (lo + hi) / 2;
  var blob = await encode(canvas, format, midQuality);

  if (blob.size <= targetBytes) {
    lo = midQuality;  // quality can go higher
    bestBlob = blob;
  } else {
    hi = midQuality;  // quality must go lower
  }
}

If the quality floor is reached and the file is still too large, the module can optionally shrink the dimensions in rounds until the target is met. This is critical for WhatsApp stickers, which must be under 100 KB.

The module also handles a sneaky browser quirk: some older Safari versions silently return a PNG when you ask for WebP. WaSize tests for real encoding support at runtime:

function canEncode(format) {
  var c = document.createElement('canvas');
  c.width = c.height = 2;
  return c.toDataURL('image/' + format)
          .indexOf('data:image/' + format) === 0;
}

2. image-editor.js — The Canvas Rendering Engine

The editor handles the visual heavy lifting: loading images, downsampling large photos, managing a small preview copy for smooth dragging, and rendering the exact same scene for both preview and export.

The geometry model is the key design decision:

frame  = output image (frameW × frameH)
image  = drawn centered, then:
         - moved by (nx, ny) as fractions of frame size
         - scaled by base × zoom
         - rotated and flipped in screen space

Storing pan as fractions rather than pixels means the preview (300px canvas) and the full-size export (e.g., 1080×1920) always match perfectly. No rounding drift.

For the blurred background (used in No-Crop mode), I took an approach that's fast even on phones:

var BLUR_LONG_SIDE = 96;  // Build the blur at tiny size
// Scale down to 96px → apply CSS-like blur → scale up
// Result: beautiful soft blur, minimal GPU/CPU cost

Building the blur at 96px and scaling up is dramatically faster than blurring a full-resolution image, and the visual result is identical because it's a background blur anyway.

Memory management matters on mobile. The editor explicitly releases canvas memory:

function release(c) {
  if (c && c.width) { c.width = 0; c.height = 0; }
}

Setting a canvas to 0×0 frees its pixel buffer immediately — important on iPhones where the GPU memory budget is tight.

3. app.js — The Glue

The largest module (~1,200 lines) wires everything together:

  • Preset management (DP, Status, Sticker, etc.)
  • Drag-and-drop + file input handling
  • Live size estimation as you adjust quality
  • Responsive UI state management
  • Keyboard shortcuts (arrow keys to pan, +/- to zoom, R to rotate)

The rule of thumb throughout: show what the user needs now, hide what they don't need yet.

Privacy Architecture

This isn't a marketing claim — it's an architectural decision baked into the code:

  1. No fetch() or XMLHttpRequest to an image-processing endpoint — you can verify in DevTools
  2. Canvas API only — toDataURL() and toBlob() run in the browser's rendering engine
  3. EXIF stripping is automatic — the Canvas API doesn't preserve metadata, so location data, camera info, and timestamps are removed by design
  4. No account system — there's nothing to store
  5. Close the tab = gone — no localStorage, no IndexedDB, no service worker cache of your images

I believe a family photo shouldn't need to travel to a server just to become a 640×640 square.

Tricky Problems I Solved

The iOS Safari Canvas Limit

iOS Safari has a hard limit on canvas pixel area (~16.7 megapixels). A 4000×5000 photo from a modern phone is 20 MP — it silently fails.

WaSize detects large images and downsamples them before processing:

var MAX_AREA = 12600000; // Stay well under 16.7 MP
var LARGE_IMAGE_PIXELS = 20e6; // Warn above ~20 MP

Multi-Step Downscaling for Sharp Results

Browsers do a terrible job scaling a 4032px image directly to 640px in one step — it looks mushy. WaSize uses halving steps:

// Scale 4032 → 2016 → 1008 → 640
// Each step is a 2× reduction, which browsers handle well
while (currentWidth / 2 >= targetWidth) {
  // Draw to half-size canvas
  // Free the previous canvas
  // Repeat
}

The result is noticeably sharper than a single-step resize.

WhatsApp's Circle Crop

WhatsApp displays profile pictures in a circle, but stores them as squares. Users constantly get surprised when corners are cut off. WaSize's circle preview overlay shows exactly what will be visible before you download.

SEO & Accessibility (Because They Matter)

WaSize is a content-heavy tool page, so I invested in:

  • Schema.org structured data — WebApplication, FAQPage, Article, and Organization markup
  • Semantic HTML — proper heading hierarchy, ARIA labels, role attributes
  • Skip navigation link — keyboard users can jump straight to the resizer
  • Full keyboard support — every control works without a mouse
  • Light/dark mode — respects prefers-color-scheme with proper theme-color meta tags
  • Responsive tables — the size reference tables work on mobile with data-label attributes

Deployment

WaSize is deployed on Vercel with simple static hosting. No server functions, no edge middleware, no database.

// vercel.json highlights
{
  "headers": [
    { "source": "/(.*)", "headers": [
      { "key": "X-Content-Type-Options", "value": "nosniff" },
      { "key": "X-Frame-Options", "value": "DENY" },
      { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
    ]}
  ]
}

Security headers, clean URLs, and a custom 404 page. That's the entire backend.

What I Learned

  1. Vanilla JS is underrated. For a focused tool, you don't need a framework. The code is readable, fast, and won't break when React 47 ships.

  2. Binary search solves more problems than you think. Finding the optimal quality for a target file size is a search problem, not a formula.

  3. Mobile-first means memory-first. Canvas pixel buffers eat GPU memory fast. Free them aggressively.

  4. Privacy can be architecture, not policy. If the code never uploads the image, you don't need a privacy policy section explaining how you handle uploads.

  5. Small scope ships. WaSize does one thing — resize images for WhatsApp. No AI upscaling, no batch processing, no social features. And that focus is why it actually works well.

Try It Out

👉 wasize.com — Free, no signup, no upload

Open DevTools and watch the Network tab. You won't see your image leave your device.

If you have feedback, find a bug, or want to discuss the Canvas API approach, drop a comment below or reach out at [email protected].

WaSize is an independent project and is not affiliated with WhatsApp or Meta.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.