You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
firstName = "JOHN",
lastName = "DOE",
dob = "01/01/1990",
exp = "01/01/2028",
dlNumber = "Z1234567",
addressL1 = "123 FAKE STREET",
addressL2 = "LOS ANGELES, CA 90028",
sex = "M",
hair = "BRN",
eyes = "BRN",
hgt = "5'-10\"",
wgt = "160 lb",
issDate = "01/01/2023"
) {
const canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 504;
const ctx = canvas.getContext("2d");
// Load signature font dynamically
try {
const fontName = "Caveat";
const link = document.createElement("link");
link.href = `https://fonts.googleapis.com/css2?family=${fontName}:wght@700&display=swap`;
link.rel = "stylesheet";
document.head.appendChild(link);
await document.fonts.load(`700 40px '${fontName}'`);
} catch (e) {
// Silently fail and fallback to standard cursive fonts
}
// 1. Background Gradient Base
const bgGrad = ctx.createLinearGradient(0, 0, 800, 504);
bgGrad.addColorStop(0, "#e8fbff");
bgGrad.addColorStop(0.5, "#fffdf2");
bgGrad.addColorStop(1, "#f2ecf9");
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Guilloche Security Wavy Lines
ctx.save();
ctx.globalAlpha = 0.15;
ctx.lineWidth = 1;
for (let y = -50; y < 550; y += 15) {
ctx.strokeStyle = y % 30 === 0 ? "#003a70" : "#a82046";
ctx.beginPath();
for (let x = 0; x <= 800; x += 10) {
let wave = Math.sin((x + y) * 0.02) * 5 + Math.cos(x * 0.05) * 3;
if (x === 0) ctx.moveTo(x, y + wave);
else ctx.lineTo(x, y + wave);
}
ctx.stroke();
}
ctx.restore();
// 3. Faded Golden California State Map Overlay
ctx.save();
ctx.globalAlpha = 0.2;
const mapGrad = ctx.createLinearGradient(200, 0, 600, 500);
mapGrad.addColorStop(0, "#ffd700");
mapGrad.addColorStop(1, "#ffa500");
ctx.fillStyle = mapGrad;
ctx.beginPath();
const mapPoints = [
[300, 30], [420, 30], [420, 140],
[600, 460], [570, 490], [480, 470],
[430, 410], [320, 230], [270, 110]
];
ctx.moveTo(mapPoints[0][0], mapPoints[0][1]);
for (let i = 1; i < mapPoints.length; i++) {
ctx.lineTo(mapPoints[i][0], mapPoints[i][1]);
}
ctx.closePath();
ctx.fill();
ctx.restore();
// Helper functions for drawing
function roundRectPath(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
function drawCover(ctx, img, x, y, w, h) {
const imgRatio = img.width / img.height;
const canvasRatio = w / h;
let renderW, renderH, offsetX = 0, offsetY = 0;
if (imgRatio < canvasRatio) {
renderW = w;
renderH = w / imgRatio;
offsetY = (renderH - h) / 2;
} else {
renderH = h;
renderW = h * imgRatio;
offsetX = (renderW - w) / 2;
}
ctx.drawImage(img, x - offsetX, y - offsetY, renderW, renderH);
}
function label(text, x, y, color = "#a82046") {
ctx.font = "11px Arial";
ctx.fillStyle = color;
ctx.fillText(text, x, y);
}
function val(text, x, y, font = "bold 16px Arial", color = "black", maxW = null) {
ctx.font = font;
ctx.fillStyle = color;
if (maxW) ctx.fillText(text, x, y, maxW);
else ctx.fillText(text, x, y);
}
// 4. Header Bar and Top Text
ctx.fillStyle = "rgba(100, 180, 240, 0.15)";
ctx.fillRect(0, 0, 800, 80);
ctx.fillStyle = "#194a8d";
ctx.font = "italic bold 44px 'Georgia', 'Times New Roman', serif";
ctx.fillText("California", 240, 52);
val("USA", 520, 35, "bold 16px Arial", "#a82046");
val("DRIVER LICENSE", 270, 75, "bold 16px Arial", "black");
// 5. Photos (Main and Ghost)
// Main Photo
ctx.save();
roundRectPath(ctx, 30, 100, 200, 300, 10);
ctx.clip();
drawCover(ctx, originalImg, 30, 100, 200, 300);
ctx.restore();
// Photo border
ctx.save();
roundRectPath(ctx, 30, 100, 200, 300, 10);
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,0,0,0.3)";
ctx.stroke();
ctx.restore();
// Ghost Photo
ctx.save();
ctx.filter = "grayscale(100%) opacity(35%)"; // Makes it look authentically printed
roundRectPath(ctx, 630, 250, 120, 160, 5);
ctx.clip();
drawCover(ctx, originalImg, 630, 250, 120, 160);
ctx.restore();
// Faded DOB over Ghost image
ctx.save();
ctx.font = "bold 18px Arial";
ctx.fillStyle = "rgba(168, 32, 70, 0.5)";
ctx.translate(690, 330);
ctx.rotate(-Math.PI / 6);
ctx.textAlign = "center";
ctx.fillText(dob, 0, 0);
ctx.restore();
// 6. Detailed Text Fields
// Identification
label("DL", 270, 100, "#194a8d");
val(dlNumber, 295, 100, "bold 20px Arial", "black", 200);
label("EXP", 530, 100, "#a82046");
val(exp, 565, 100, "bold 20px Arial", "#a82046");
// Identifying Data
label("LN", 270, 140, "#a82046");
val(lastName, 295, 140, "bold 22px Arial", "black", 400);
label("FN", 270, 180, "#a82046");
val(firstName, 295, 180, "bold 22px Arial", "black", 400);
// Address
val(addressL1, 295, 215, "16px Arial", "black", 450);
val(addressL2, 295, 240, "16px Arial", "black", 450);
// Date of Birth
label("DOB", 270, 295, "#a82046");
val(dob, 310, 295, "bold 24px Arial", "#a82046");
// Physical Attributes row 1
let lineY = 345;
label("ISS", 270, lineY, "#194a8d"); val(issDate, 295, lineY, "bold 14px Arial");
label("SEX", 390, lineY, "#194a8d"); val(sex, 420, lineY, "bold 14px Arial");
label("HAIR", 455, lineY, "#194a8d"); val(hair, 490, lineY, "bold 14px Arial");
label("EYES", 540, lineY, "#194a8d"); val(eyes, 580, lineY, "bold 14px Arial");
// Physical Attributes row 2
let lineY2 = 380;
label("HGT", 270, lineY2, "#194a8d"); val(hgt, 300, lineY2, "bold 14px Arial");
label("WGT", 370, lineY2, "#194a8d"); val(wgt, 405, lineY2, "bold 14px Arial");
label("CLASS", 475, lineY2, "#194a8d"); val("C", 525, lineY2, "bold 14px Arial");
// 7. Signature Line & Signature
ctx.beginPath();
ctx.moveTo(270, 470);
ctx.lineTo(750, 470);
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.stroke();
ctx.font = "700 40px 'Caveat', 'Brush Script MT', 'Bradley Hand', 'Comic Sans MS', cursive";
ctx.fillStyle = "#101010";
ctx.fillText(`${firstName} ${lastName}`, 300, 465, 450);
// 8. Design Motifs (REAL ID, Donor Dot, Barcode)
// REAL ID Golden Star
ctx.beginPath();
ctx.arc(740, 50, 22, 0, Math.PI * 2);
ctx.fillStyle = "#d4af37";
ctx.fill();
ctx.fillStyle = "#ffffff";
ctx.font = "24px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("★", 740, 52);
// Pink Donor
ctx.beginPath();
ctx.arc(740, 440, 18, 0, Math.PI * 2);
ctx.fillStyle = "#ffc0cb";
ctx.fill();
ctx.fillStyle = "black";
ctx.font = "bold 9px Arial";
ctx.fillText("DONOR", 740, 440);
ctx.textAlign = "left";
ctx.textBaseline = "alphabetic";
// Bottom Decorative Barcode Element
ctx.fillStyle = "rgba(0,0,0,0.7)";
let bx = 30;
while (bx < 400) {
let bw = Math.random() > 0.5 ? 2 : (Math.random() > 0.5 ? 3 : 1);
let gap = Math.random() * 3 + 1;
if (bx + bw > 400) break;
ctx.fillRect(bx, 485, bw, 10);
bx += bw + gap;
}
return canvas;
}
Apply Changes