You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
firstName = "JOHN",
lastName = "DOE",
address = "123 FICTIONAL ST\nDENVER, CO 80202",
dob = "01/01/1990",
licenseNumber = "98-765-4321",
sex = "M",
height = "5-10",
weight = "180",
eyes = "BRO",
hair = "BLK",
expDate = "01/01/2030"
) {
// 1. Setup Canvas (CR80 ratio approximately 860x540)
const canvas = document.createElement("canvas");
canvas.width = 860;
canvas.height = 540;
const ctx = canvas.getContext("2d");
// 2. Load Google Fonts
const fontCSS = document.createElement("link");
fontCSS.href = "https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&family=Oswald:wght@500;700&family=Roboto:wght@400;700;900&display=swap";
fontCSS.rel = "stylesheet";
document.head.appendChild(fontCSS);
try {
await document.fonts.load('700 52px Oswald');
await document.fonts.load('700 36px "Dancing Script"');
await document.fonts.load('700 24px Roboto');
await document.fonts.load('400 12px Roboto');
} catch (e) {
console.warn('Custom fonts load timeout. Using fallbacks.', e);
}
// Helper: Rounded Rectangle Path
function makeRoundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
// 3. Base Card Canvas Clipping (Rounded card corners)
ctx.save();
makeRoundRect(ctx, 0, 0, 860, 540, 20);
ctx.clip();
// 4. Background Gradient
const bgGrd = ctx.createLinearGradient(0, 0, 0, 540);
bgGrd.addColorStop(0, "#DDF0F9"); // Sky light blue
bgGrd.addColorStop(0.35, "#FFFFFF"); // Snowcap white
bgGrd.addColorStop(1, "#EBF1EA"); // Terrain pale green
ctx.fillStyle = bgGrd;
ctx.fillRect(0, 0, 860, 540);
// 5. Stylized Mountains (Colorado Theme)
ctx.fillStyle = 'rgba(11, 79, 139, 0.12)';
ctx.beginPath();
ctx.moveTo(0, 540); ctx.lineTo(0, 350); ctx.lineTo(120, 260); ctx.lineTo(250, 330);
ctx.lineTo(400, 210); ctx.lineTo(550, 350); ctx.lineTo(700, 180); ctx.lineTo(860, 300); ctx.lineTo(860, 540);
ctx.fill();
ctx.fillStyle = 'rgba(20, 110, 80, 0.08)';
ctx.beginPath();
ctx.moveTo(0, 540); ctx.lineTo(0, 420); ctx.lineTo(180, 310); ctx.lineTo(350, 400);
ctx.lineTo(520, 280); ctx.lineTo(680, 410); ctx.lineTo(860, 360); ctx.lineTo(860, 540);
ctx.fill();
// 6. Guilloche/Security Waves overlay
ctx.lineWidth = 0.5;
for (let j = 0; j < 5; j++) {
ctx.beginPath();
ctx.strokeStyle = `rgba(50, 120, 180, ${0.4 - j * 0.06})`;
for(let x = 0; x <= 860; x += 4) {
let y = 300 + j * 35 + Math.sin((x + j*20) * 0.03) * 20 + Math.cos(x * 0.015) * 15;
if (x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
// 7. Top Header Ribbon Decor
ctx.beginPath();
ctx.moveTo(0, 100);
ctx.bezierCurveTo(300, 120, 500, 60, 860, 115);
ctx.lineTo(860, 0); ctx.lineTo(0, 0); ctx.closePath();
const topGrd = ctx.createLinearGradient(0, 0, 860, 0);
topGrd.addColorStop(0, 'rgba(10, 70, 140, 0.1)');
topGrd.addColorStop(1, 'rgba(10, 120, 60, 0.1)');
ctx.fillStyle = topGrd;
ctx.fill();
// 8. Header Texts
ctx.font = '700 52px Oswald, sans-serif';
ctx.fillStyle = '#0F3C80';
ctx.textAlign = 'center';
ctx.fillText('COLORADO', 430, 60);
ctx.font = '700 18px Oswald, sans-serif';
ctx.fillStyle = '#1B5B90';
ctx.fillText('USA', 430, 85);
ctx.fillText('DRIVER LICENSE', 430, 105);
// 9. Colorado Flag Graphic
ctx.textAlign = 'left';
function drawCOFlag(cx, cy, w, h) {
ctx.save();
ctx.translate(cx, cy);
ctx.strokeStyle = 'rgba(0,0,0,0.1)'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, w, h);
ctx.fillStyle = '#002868'; ctx.fillRect(0, 0, w, h/3);
ctx.fillStyle = '#FFFFFF'; ctx.fillRect(0, h/3, w, h/3);
ctx.fillStyle = '#002868'; ctx.fillRect(0, 2*h/3, w, h/3);
const rRad = h * 0.35;
ctx.fillStyle = '#BF0A30';
ctx.beginPath();
ctx.arc(w/2 - 2, h/2, rRad, -Math.PI/4, Math.PI/4, true);
ctx.arc(w/2 - 2, h/2, rRad*0.6, Math.PI/4, -Math.PI/4, false);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#FFD300';
ctx.beginPath();
ctx.arc(w/2 - 2, h/2, rRad*0.6, 0, Math.PI*2);
ctx.fill();
ctx.restore();
}
drawCOFlag(750, 25, 80, 50);
// 10. REAL ID Star Graphic
function drawRealIDStar(cx, cy, r) {
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.arc(0, 0, r, 0, 2*Math.PI);
ctx.fillStyle = '#D6B055';
ctx.fill();
ctx.beginPath();
let spikes = 5, rot = Math.PI / 2 * 3;
let step = Math.PI / spikes;
ctx.moveTo(0, -(r * 0.7));
for(let i=0; i<spikes; i++) {
ctx.lineTo(Math.cos(rot) * r * 0.7, Math.sin(rot) * r * 0.7); rot += step;
ctx.lineTo(Math.cos(rot) * r * 0.3, Math.sin(rot) * r * 0.3); rot += step;
}
ctx.closePath();
ctx.fillStyle = '#FFFFFF';
ctx.fill();
ctx.restore();
}
drawRealIDStar(715, 50, 18);
// 11. Helper to Draw Cropped Photo
function drawCropped(img, bx, by, bw, bh, applyFilter = false) {
if (!img || img.width === 0) return;
const imgRatio = img.width / img.height;
const boxRatio = bw / bh;
let sx, sy, sw, sh;
if (imgRatio > boxRatio) {
sh = img.height; sw = img.height * boxRatio;
sx = (img.width - sw) / 2; sy = 0;
} else {
sw = img.width; sh = img.width / boxRatio;
sx = 0; sy = (img.height - sh) / 2;
}
ctx.save();
makeRoundRect(ctx, bx, by, bw, bh, 15);
ctx.clip();
if (applyFilter && ctx.filter !== undefined) {
ctx.filter = 'grayscale(100%) opacity(35%) contrast(1.2)';
} else if (applyFilter) {
ctx.globalAlpha = 0.35;
}
ctx.drawImage(img, sx, sy, sw, sh, bx, by, bw, bh);
ctx.restore();
ctx.strokeStyle = 'rgba(0,0,0,0.1)';
ctx.stroke();
}
// 12. Draw Photos
drawCropped(originalImg, 30, 140, 195, 255); // Main Profile
drawCropped(originalImg, 690, 310, 145, 190, true); // Ghost Profile
// 13. Data Fields Typography System
function drawField(label, value, x, y, opts = {}) {
const lblCol = opts.labelColor || '#BF0000';
const valCol = opts.valueColor || '#000000';
const valFont = opts.valueFont || '700 16px Roboto, sans-serif';
const lblFont = opts.labelFont || '600 11px Roboto, sans-serif';
const maxWidth = opts.maxWidth || 250;
ctx.font = lblFont; ctx.fillStyle = lblCol;
ctx.fillText(label, x, y);
ctx.font = valFont; ctx.fillStyle = valCol;
ctx.fillText(value, x, y + 20, maxWidth);
}
// Standard Layout Details
drawField('4a ISS', '07/15/2021', 250, 150, { labelColor: '#1A5B80'});
drawField('4d DL No.', licenseNumber, 360, 150, { labelColor: '#1A5B80'});
drawField('8 DOB', dob, 550, 150, { labelColor: '#BF0000', valueColor: '#BF0000', valueFont: '900 18px Roboto' });
drawField('4b EXP', expDate, 700, 150, { labelColor: '#BF0000', valueColor: '#BF0000', valueFont: '900 18px Roboto' });
drawField('1 LAST', lastName, 250, 205, { labelColor: '#1A5B80', valueFont: '700 24px Roboto, sans-serif', maxWidth: 420 });
drawField('2 FIRST', firstName, 250, 260, { labelColor: '#1A5B80', valueFont: '700 24px Roboto, sans-serif', maxWidth: 420 });
let addrLines = address.split(/\\n|\n/);
if (addrLines.length === 1 && address.includes(',')) {
let idx = address.indexOf(',');
addrLines = [address.substring(0, idx), address.substring(idx+1).trim()];
}
drawField('8 ADDRESS', addrLines[0], 250, 315, { labelColor: '#1A5B80', valueFont: '700 18px Roboto, sans-serif', maxWidth: 420 });
if(addrLines[1]) {
ctx.font = '700 18px Roboto, sans-serif';
ctx.fillStyle = '#000000';
ctx.fillText(addrLines[1].trim(), 250, 355, 420);
}
const row5Y = 410;
drawField('15 SEX', sex, 250, row5Y, { labelColor: '#1A5B80', maxWidth: 60 });
drawField('16 HGT', height, 330, row5Y, { labelColor: '#1A5B80', maxWidth: 60 });
drawField('17 WGT', weight, 410, row5Y, { labelColor: '#1A5B80', maxWidth: 60 });
drawField('18 EYES', eyes, 490, row5Y, { labelColor: '#1A5B80', maxWidth: 60 });
drawField('19 HAIR', hair, 570, row5Y, { labelColor: '#1A5B80', maxWidth: 60 });
const row6Y = 470;
drawField('9 ISSUED', 'DENVER', 250, row6Y, { labelColor: '#1A5B80' });
drawField('12 CLASS', 'R', 370, row6Y, { labelColor: '#1A5B80' });
drawField('END', 'NONE', 460, row6Y, { labelColor: '#1A5B80' });
drawField('RESTR', 'A', 550, row6Y, { labelColor: '#1A5B80' });
// 14. Signature Overlay
ctx.font = '700 36px "Dancing Script", cursive';
ctx.fillStyle = '#0B1C56'; // Dark Pen Ink
ctx.textAlign = 'center';
ctx.fillText(`${firstName} ${lastName}`, 127, 435, 200);
// 15. Base Boundary Edge
ctx.restore(); // Restore from clipping area
ctx.strokeStyle = '#CCCCCC';
ctx.lineWidth = 1;
makeRoundRect(ctx, 0.5, 0.5, 859, 539, 20);
ctx.stroke();
return canvas;
}
Apply Changes