FormPic: crop and resize photos for forms

If you've applied for anything in India (a passport, a PAN card, UPSC, NEET, a bank exam), you've seen some version of this:

"Upload photograph: 35×45 mm, JPEG, under 50 KB."
"Upload signature: 60×20 mm, 10 to 20 KB."

You crop it in some app, export it, upload it, and the portal says no. Wrong dimensions. Or 53 KB. Try again.

The usual fix is to search "photo resizer online" and pick whichever site loads first. Those sites are covered in ads, and most of them upload your photo to a server. It's your face, going into an ID application, sitting on a machine you know nothing about. That bugged me enough that I built my own: FormPic.

What it does

You drop in a photo, pick the form, drag the crop box over your face and download. The file comes out at the exact pixel size the form wants and under the file size limit you set.

The part I care about most: the photo never leaves your browser. There's no upload step at all. It's free, there's no account, and there's no watermark. Once the page has loaded it keeps working offline, and you can take the photo with your phone or laptop camera instead of uploading one.

Try it here: formpic.devanshuverma.in

The sizes it knows about

These are the presets right now:

Document Print size Pixels Resolution
Passport size 35×45 mm 413 × 531 px 300 DPI
US passport & visa 2×2 in 600 × 600 px 300 DPI
PAN card 25×35 mm 197 × 276 px 200 DPI
UPSC 35×45 mm 413 × 531 px 300 DPI
NEET 35×45 mm 413 × 531 px 300 DPI
IBPS 35×45 mm 200 × 230 px set in pixels
SSC signature 60×20 mm 472 × 157 px 200 DPI

If your form isn't listed, you can type any width and height in pixels and lock the aspect ratio.

What makes these annoying is that you have to get the aspect ratio, the pixel dimensions and the file size right at the same time. Most tools I tried handled one of them and left the rest to me.

How it works without a server

A browser already has everything a photo resizer needs. You read the file, draw the cropped part onto a canvas at the target size, and encode it. With no backend there's nothing to store and nothing to leak, and no reason the tool can't run offline.

Here's a simplified sketch of the crop step:

async function cropAndResize(file, crop, width, height) { const bitmap = await createImageBitmap(file); const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.imageSmoothingQuality = 'high'; // source rect (the crop box) -> destination rect (the form size) ctx.drawImage(bitmap, crop.x, crop.y, crop.w, crop.h, 0, 0, width, height); return canvas; } 

Getting under 50 KB without guessing

This was the fun bit. JPEG file size doesn't go up in a straight line with quality, so you can't just say "50 KB means quality 0.7." What does work is a binary search over the quality value until you find the highest one that still fits:

const toBlob = (canvas, q) => new Promise((resolve) => canvas.toBlob(resolve, 'image/jpeg', q)); async function fitToSize(canvas, maxBytes) { let lo = 0.05, hi = 1, best = null; for (let i = 0; i < 8; i++) { const q = (lo + hi) / 2; const blob = await toBlob(canvas, q); if (blob.size <= maxBytes) { best = blob; lo = q; } else hi = q; } return best; // null means it's too big even at the lowest quality } 

Eight rounds gets you within about 0.4% of the best quality, which is plenty. If the photo is still too big at the lowest quality, there's a "Shrink to fit" option that lowers the pixel size too.

A few smaller things I spent time on:

  • Picking a form preset switches the output to JPEG, since that's what nearly every portal asks for. PNG is still there if you want a lossless copy to print.
  • The preset outlines are drawn to scale, so you can see the shape before you choose.
  • The Download button shows the final file size. You find out it's 48 KB before you're stuck on a slow government site at midnight, not after.
  • Nothing is kept. Close the tab and the photo is gone.

What it doesn't do

It won't change your background to white. I went back and forth on this, but doing it well in the browser is hard, and doing it badly looks worse than a plain wall. For now my advice is to stand in front of a white or light wall with even light. That fixes it most of the time.

A few questions I get

What's a passport photo in pixels? 35 × 45 mm at 300 DPI is 413 × 531 px. Some portals want a different upload size, though, so check the form and type its numbers in.

How do I get a photo under 20 KB? Pick JPEG, set Max file size to 20, and download.

Is it really not uploaded? Open DevTools, go to the Network tab and try it. You won't see the image go anywhere.

Tell me what's missing

I build and run FormPic on my own, and I add presets based on what people ask for. If your form or country needs a size that's not there, leave a comment with the form name, the size (mm or px) and any file size limit, and I'll add it.

I'd also like to hear from other devs about the UX, or about how you'd handle background removal client-side without shipping a huge model.

And if you know someone who's about to fight an exam portal, send them the link. It might save them an evening.

formpic.devanshuverma.in