You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, state="CALIFORNIA", idNumber="F1234567", name="JOHN DOE", dob="01/01/1990", address="123 FAKE ST, MADEUP CITY, CA 90000", sex="M", height="5'-9\"", eyes="BRN", expDate="01/01/2030", carClass="C") {
// Create the canvas and context (Standard DL Aspect Ratio: ~3.375 x 2.125. Using 800 x 500)
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 500;
const ctx = canvas.getContext('2d');
// 1. Draw smooth gradient background
const bgGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
bgGrad.addColorStop(0, '#e8f4f8');
bgGrad.addColorStop(0.5, '#fcedf2');
bgGrad.addColorStop(1, '#e9ecf5');
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Add abstract guilloche-like wave patterns
ctx.lineWidth = 1.5;
ctx.strokeStyle = 'rgba(10, 80, 200, 0.1)';
for(let i = 0; i < 20; i++) {
ctx.beginPath();
ctx.moveTo(0, i * 30);
ctx.bezierCurveTo(250, i * 30 + 200, 550, i * 30 - 200, 800, i * 30);
ctx.stroke();
}
for(let i = 0; i < 20; i++) {
ctx.beginPath();
ctx.moveTo(0, i * 30 - 100);
ctx.bezierCurveTo(300, i * 30 - 300, 500, i * 30 + 300, 800, i * 30);
ctx.stroke();
}
// 3. Draw faint Official State Seal watermark in the background
ctx.save();
ctx.translate(450, 270);
ctx.globalAlpha = 0.07;
ctx.beginPath();
ctx.arc(0, 0, 140, 0, Math.PI * 2);
ctx.lineWidth = 12;
ctx.strokeStyle = '#0d47a1';
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 120, 0, Math.PI * 2);
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = '#0d47a1';
ctx.font = 'bold 34px "Arial", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('OFFICIAL STATE', 0, -30);
ctx.fillText('SEAL', 0, 30);
ctx.restore();
// 4. Header Section
// Top colored band
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.fillRect(0, 0, 800, 90);
// State Name
ctx.fillStyle = '#0d47a1';
ctx.font = 'bold 42px "Arial", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'alphabetic';
ctx.fillText(state.toUpperCase(), 400, 48);
// Driver License Subtitle
ctx.fillStyle = '#b71c1c';
ctx.font = 'bold 20px "Arial", sans-serif';
ctx.fillText('DRIVER LICENSE', 400, 76);
// 5. "REAL ID" Star on Top Right
const starCx = 740, starCy = 45, starR = 26;
ctx.fillStyle = '#FFC107'; // Gold
ctx.beginPath();
ctx.arc(starCx, starCy, starR, 0, Math.PI * 2);
ctx.fill();
// Inner white star
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
let rot = Math.PI / 2 * 3;
let step = Math.PI / 5;
const outerRadius = 14;
const innerRadius = 6;
ctx.moveTo(starCx, starCy - outerRadius);
for(let i = 0; i < 5; i++) {
let x = starCx + Math.cos(rot) * outerRadius;
let y = starCy + Math.sin(rot) * outerRadius;
ctx.lineTo(x, y);
rot += step;
x = starCx + Math.cos(rot) * innerRadius;
y = starCy + Math.sin(rot) * innerRadius;
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(starCx, starCy - outerRadius);
ctx.closePath();
ctx.fill();
// Helper to center-crop an image onto the canvas to avoid stretching
function drawImageCenterCrop(context, img, dx, dy, dw, dh) {
const imgRatio = img.width / img.height;
const canvasRatio = dw / dh;
let sw, sh, sx, sy;
if (imgRatio < canvasRatio) {
sw = img.width;
sh = img.width / canvasRatio;
sx = 0;
sy = (img.height - sh) / 2;
} else {
sh = img.height;
sw = img.height * canvasRatio;
sy = 0;
sx = (img.width - sw) / 2;
}
context.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
}
// 6. Draw Main Photo
const pX = 40;
const pY = 110;
const pW = 200;
const pH = 260;
// Photo border
ctx.fillStyle = '#fff';
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.shadowBlur = 6;
ctx.fillRect(pX - 4, pY - 4, pW + 8, pH + 8);
ctx.shadowColor = 'transparent'; // Reset shadow
ctx.shadowBlur = 0;
// The photo itself
drawImageCenterCrop(ctx, originalImg, pX, pY, pW, pH);
// 7. Draw Ghost Photo (Secondary smaller translucent photo)
ctx.globalAlpha = 0.35;
const gpW = 110;
const gpH = 143;
const gpX = 650;
const gpY = 270;
drawImageCenterCrop(ctx, originalImg, gpX, gpY, gpW, gpH);
ctx.globalAlpha = 1.0;
// 8. Labels and Text Fields Setup
const leftCol = 270;
const rightCol = 530;
let cY = 135;
function drawField(label, value, x, y, valueOffset, useRedLabel = true) {
ctx.textAlign = 'left';
ctx.textBaseline = 'alphabetic';
ctx.font = 'bold 15px "Arial", sans-serif';
ctx.fillStyle = useRedLabel ? '#c62828' : '#333';
ctx.fillText(label, x, y);
ctx.fillStyle = '#000';
ctx.font = 'bold 22px "Arial", sans-serif';
ctx.fillText(value, x + valueOffset, y);
}
// Line 1: DL/ID & EXP
drawField('DL / ID', idNumber, leftCol, cY, 60);
drawField('EXP', expDate, rightCol, cY, 45);
cY += 40;
// Parse name into First and Last
let nameParts = name.trim().split(' ');
let lastName = nameParts.length > 1 ? nameParts.pop() : name;
let firstName = nameParts.length > 0 ? nameParts.join(' ') : '';
// Line 2: Last Name
drawField('LN', lastName, leftCol, cY, 45);
cY += 35;
// Line 3: First Name
drawField('FN', firstName, leftCol, cY, 45);
cY += 40;
// Line 4 & 5: Address (Word Wrap)
ctx.font = 'bold 15px "Arial", sans-serif';
ctx.fillStyle = '#c62828';
ctx.fillText('ADDRESS', leftCol, cY);
ctx.fillStyle = '#000';
ctx.font = 'bold 19px "Arial", sans-serif';
function wrapAddress(text, startX, startY, maxWidth, lineHeight) {
const words = text.replace(/\s+/g, ' ').split(' ');
let line = '';
let currentY = startY;
for(let i = 0; i < words.length; i++) {
let testLine = line + words[i] + ' ';
let metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth && i > 0) {
ctx.fillText(line.trim(), startX, currentY);
line = words[i] + ' ';
currentY += lineHeight;
} else {
line = testLine;
}
}
ctx.fillText(line.trim(), startX, currentY);
return currentY;
}
// Address has a max width so it doesn't overlap ghost image
cY = wrapAddress(address, leftCol + 90, cY, 280, 25);
cY += 45;
// Line 6: Date of Birth and Class
drawField('DOB', dob, leftCol, cY, 50);
drawField('CLASS', carClass, rightCol, cY, 70, false);
cY += 40;
// Line 7: Stats
drawField('SEX', sex, leftCol, cY, 45, false);
drawField('HT', height, leftCol + 100, cY, 35, false);
drawField('EYES', eyes, leftCol + 215, cY, 55, false);
// 9. Signature (bottom under the picture)
ctx.font = 'italic 36px "Times New Roman", serif';
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
ctx.fillText(name, pX + (pW / 2), pY + pH + 40);
// Draw a line under signature
ctx.beginPath();
ctx.moveTo(pX, pY + pH + 45);
ctx.lineTo(pX + pW, pY + pH + 45);
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.stroke();
// 10. Fake 1D Barcode Pattern (bottom right area)
ctx.fillStyle = '#111';
let bcX = 350;
let bcY = 430;
let bcHeight = 45;
for(let i = 0; i < 50; i++) {
let barWidth = Math.random() * 4 + 1;
let barGap = Math.random() * 4 + 1;
if (bcX + barWidth > 750) break; // Don't overflow canvas
ctx.fillRect(bcX, bcY, barWidth, bcHeight);
bcX += barWidth + barGap;
}
return canvas;
}
Apply Changes