The problem
Passport-photo specifications are tighter than most users realise. Singapore wants 35×45mm with a specific head-to-frame ratio. The US wants 2×2in with a different ratio. Each country has its own list of forbidden things: glasses, smiles, shadows on the face, neutral backgrounds within a specific RGB band.
Most online photo tools either reject silently or accept things they shouldn't. I wanted one that explains what's wrong, in plain language, in real time, and links the user back to the section of the rule that failed.
Architecture
Face mesh
MediaPipe 468-point face mesh runs in the browser. Detects head pose, eye-line, mouth opening, ear visibility, frame coverage. Local-first — the user's photo never leaves the device unless they hit submit.
Segmentation
rembg with u2net model produces a clean person cutout server-side for background analysis — checks the wall behind the subject for compliance (off-white range for SG, plain white for US, no shadows).
Rule engine
25+ named compliance rules, each with a pass/fail result and a human-readable explanation. Country-specific rule packs override defaults.
UX feedback
Each failed check renders as a card with the rule name, what was detected, and the official source link. The user knows exactly what to fix.
Output
Cropped, recoloured, correctly-sized JPEG ready for upload. Dimensions match the destination's spec sheet exactly.
Country support
- Singapore — 35×45mm, off-white background, head height 32-36mm.
- United States — 2×2in, plain white, head height 1-1⅜in.
- United Kingdom — 35×45mm, light grey/cream, no glasses, neutral expression.
- Japan — 35×45mm, plain background, head height 32-36mm.
- Schengen (EU) — 35×45mm, light grey, ICAO compliance.
- Canada — 50×70mm, plain white or light, specific head ratios.
What gets shipped
468
Facial landmarks
25+
Compliance rules
6
Countries supported
100%
Local face detection
Tech stack
Python
FastAPI
MediaPipe
OpenCV
rembg
u2net
Pillow
Vue
TypeScript
Railway