Originally published byDev.to
Building a Browser-Based Passport Photo Maker
Passport photos have strict government specifications — exact dimensions, specific background colors, and precise head positioning. Here's how to build a tool that handles all of this entirely in the browser.
The Challenge
Each country has different requirements:
| Country | Size (mm) | Size (px at 300 DPI) | Background |
|---|---|---|---|
| US | 51×51 | 600×600 | White |
| UK/EU | 35×45 | 413×531 | Light gray |
| India | 35×45 | 413×531 | White |
| Canada | 50×70 | 591×827 | White |
| China | 33×48 | 390×567 | White |
| Japan | 35×45 | 413×531 | White |
| Australia | 35×45 | 413×531 | White |
| France | 35×45 | 413×531 | Light gray |
| Germany | 35×45 | 413×531 | Light gray |
| UAE | 43×55 | 508×650 | White |
Loading and Displaying the Image
First, load the user's image and display it with an interactive crop overlay:
js
function loadImage(file) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = URL.createObjectURL(file);
});
}
Interactive Crop Window
The crop window is an absolutely positioned div overlaid on the image. Users can drag it to reposition and resize using corner handles:
class CropWindow {
constructor(container, image, preset) {
this.container = container;
this.image = image;
this.preset = preset;
this.x = 0;
this.y = 0;
this.w = 200;
this.h = 260;
this.initHandles();
this.initKeyboard();
}
initHandles() {
// 4 corner handles for resize
const handles = ['nw', 'ne', 'sw', 'se'];
handles.forEach(pos => {
const handle = document.createElement('div');
handle.className = `crop-handle ${pos}`;
handle.addEventListener('pointerdown', (e) => this.startResize(e, pos));
this.container.appendChild(handle);
});
}
initKeyboard() {
document.addEventListener('keydown', (e) => {
const step = e.shiftKey ? 10 : 2;
switch(e.key) {
case 'ArrowUp': this.y = Math.max(0, this.y - step); break;
case 'ArrowDown': this.y = Math.min(this.maxY, this.y + step); break;
case 'ArrowLeft': this.x = Math.max(0, this.x - step); break;
case 'ArrowRight': this.x = Math.min(this.maxX, this.x + step); break;
}
this.render();
});
}
}
Background Replacement
To change the background color, we scan edge pixels and replace similar colors:
function replaceBackground(canvas, targetColor, tolerance = 30) {
const ctx = canvas.getContext('2d');
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = data.data;
// Sample background color from corners
const bg = sampleCornerPixels(pixels, canvas.width, canvas.height);
for (let i = 0; i < pixels.length; i += 4) {
const diff = colorDistance(pixels, bg, i);
if (diff < tolerance) {
pixels[i] = targetColor.r; // R
pixels[i + 1] = targetColor.g; // G
pixels[i + 2] = targetColor.b; // B
}
}
ctx.putImageData(data, 0, 0);
}
Print Layout Generation
For users who want to print at a photo lab, we need to arrange multiple copies on a 4×6 inch (1024×1536 px at 300 DPI) print sheet:
function generatePrintLayout(singlePhoto, preset, sheetWidth = 1500, sheetHeight = 1000) {
const canvas = document.createElement('canvas');
canvas.width = sheetWidth;
canvas.height = sheetHeight;
const ctx = canvas.getContext('2d');
// Fill with white background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, sheetWidth, sheetHeight);
// Calculate how many photos fit
const cols = Math.floor(sheetWidth / (preset.outW + 20));
const rows = Math.floor(sheetHeight / (preset.outH + 20));
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = col * (preset.outW + 20) + 10;
const y = row * (preset.outH + 20) + 10;
ctx.drawImage(singlePhoto, x, y, preset.outW, preset.outH);
}
}
return canvas;
}
Full Tool
The ToolBox Image Passport Photo Maker (https://toolboximage.com/tools/passport/) implements all of this with 10 country presets, 4 background colors, arrow key fine positioning, and both single photo + print layout downloads. All client-side, zero uploads.
Try it at toolboximage.com/tools/passport/ (https://toolboximage.com/tools/passport/)
🇺🇸
More news from United StatesUnited States
NORTH AMERICA
Related News
Secret Claude Tracker Shocks Users After Anthropic's Anti-Surveillance Stance
12h ago
EV Batteries Defy Expectations, Last Hundreds of Thousands of Miles
1d ago
GBase 8a Performance Anomaly Case Study: How a Single Parameter Change Sparked a Chain Reaction
1d ago
Who Else Has Inherited a Codebase With Zero Comments and a Prayer?
1d ago
完美的平庸
3h ago