You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg,
lastName = "PUBLIC",
firstName = "JOHN Q",
address = "100 N SENATE AVE, INDIANAPOLIS, IN 46204",
dob = "05/10/1985",
sex = "M",
height = "5'-11\"",
weight = "175",
eyes = "BRO",
hair = "BLK",
dlNumber = "1234-56-7890",
issueDate = "05/15/2022",
expDate = "05/10/2028"
) {
const canvas = document.createElement("canvas");
canvas.width = 860;
canvas.height = 540;
const ctx = canvas.getContext("2d");
// 1. Background Gradient
let gradient = ctx.createLinearGradient(0, 0, 860, 540);
gradient.addColorStop(0, "#ffffff");
gradient.addColorStop(0.5, "#e6f0fa");
gradient.addColorStop(1, "#f2e4e1");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 860, 540);
// 2. Realistic Guilloche (micro-wave) Pattern
ctx.save();
ctx.lineWidth = 0.5;
ctx.strokeStyle = "rgba(100, 130, 180, 0.15)";
for(let i=0; i<540; i+=20) {
ctx.beginPath();
for(let x=0; x<=860; x+=10) {
let y = i + Math.sin(x * 0.03) * 12 + Math.cos(x * 0.02 + i) * 6;
if(x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
ctx.restore();
// 3. Indiana Map Watermark
ctx.save();
ctx.translate(550, 270);
ctx.scale(3, 3);
ctx.fillStyle = "rgba(220, 200, 150, 0.25)";
ctx.beginPath();
ctx.moveTo(-20, -50);
ctx.lineTo(30, -50);
ctx.lineTo(30, 50);
ctx.bezierCurveTo(20, 55, 0, 70, -20, 60);
ctx.bezierCurveTo(-25, 40, -35, 20, -30, -10);
ctx.lineTo(-20, -50);
ctx.fill();
ctx.restore();
// 4. Indy Race Car Watermark Background
ctx.save();
ctx.translate(620, 140);
ctx.scale(2.0, 2.0);
ctx.globalAlpha = 0.2;
ctx.fillStyle = "#1e3a8a";
// Draw Car Frame
ctx.beginPath();
ctx.moveTo(-60, 5);
ctx.lineTo(-50, 0); // nose
ctx.lineTo(-30, 0); // front suspension
ctx.lineTo(-10, -10); // cockpit
ctx.lineTo(20, -10); // driver area
ctx.lineTo(40, 5); // engine cowl
ctx.lineTo(60, 5);
ctx.lineTo(60, -15); // rear wing pillar
ctx.lineTo(75, -15); // rear wing
ctx.lineTo(75, 10);
ctx.lineTo(-60, 10); // undertray
ctx.fill();
// Front wing
ctx.fillRect(-65, 5, 15, 3);
// Wheels
ctx.fillStyle = "#0f172a";
ctx.beginPath(); ctx.arc(-40, 10, 8, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(45, 10, 10, 0, Math.PI*2); ctx.fill();
// Speed lines trailing the car
ctx.strokeStyle = "#1e3a8a";
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(-90, 8); ctx.lineTo(-70, 8);
ctx.moveTo(-80, 3); ctx.lineTo(-65, 3);
ctx.moveTo(-100, 13); ctx.lineTo(-65, 13);
ctx.stroke();
ctx.restore();
// 5. Header Details
ctx.font = "italic bold 44px 'Times New Roman', Times, serif";
ctx.fillStyle = "#1e3a8a";
ctx.textAlign = "center";
ctx.fillText("I N D I A N A", 430, 55);
ctx.font = "bold 20px Arial";
ctx.fillStyle = "#9f1239";
ctx.fillText("DRIVER LICENSE", 430, 80);
ctx.fillStyle = "rgba(250, 204, 21, 0.85)";
ctx.fillRect(0, 95, 860, 18);
ctx.font = "bold 12px Arial";
ctx.fillStyle = "rgba(0,0,0,0.6)";
ctx.textAlign = "right";
ctx.fillText("USA", 840, 110);
// 6. Draw Primary Cropped Photo
let photoW = 200;
let photoH = 260;
let photoX = 40;
let photoY = 135;
ctx.save();
ctx.beginPath();
if(ctx.roundRect) {
ctx.roundRect(photoX, photoY, photoW, photoH, 10);
} else {
ctx.rect(photoX, photoY, photoW, photoH);
}
ctx.clip();
let imgRatio = originalImg.width / originalImg.height;
let targetRatio = photoW / photoH;
let drawW, drawH, drawX, drawY;
if (imgRatio > targetRatio) {
drawH = photoH;
drawW = drawH * imgRatio;
drawY = photoY;
drawX = photoX - (drawW - photoW) / 2;
} else {
drawW = photoW;
drawH = drawW / imgRatio;
drawX = photoX;
drawY = photoY - (drawH - photoH) / 2;
}
ctx.drawImage(originalImg, drawX, drawY, drawW, drawH);
ctx.restore();
// Primary photo border outline
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(30, 58, 138, 0.4)";
ctx.stroke();
// 7. Write Structured Data Fields
let col1x = 260;
ctx.textAlign = "left";
function addField(label, value, lx, ly, vx, vy, vSize="16px", vColor="#000000", lSize="11px") {
ctx.font = `bold ${lSize} Arial`;
ctx.fillStyle = "#9f1239";
ctx.fillText(label, lx, ly);
ctx.font = `bold ${vSize} Arial`;
ctx.fillStyle = vColor;
ctx.fillText(value, vx, vy);
}
addField("4d DLN", dlNumber, col1x, 145, col1x + 45, 146, "22px", "#000000");
addField("3 DOB", dob, col1x + 280, 145, col1x + 325, 146, "18px", "#dc2626");
addField("4b EXP", expDate, col1x + 450, 145, col1x + 495, 146, "18px", "#dc2626");
addField("4a ISS", issueDate, col1x, 185, col1x + 45, 185, "16px", "#000000");
addField("1", lastName.toUpperCase(), col1x, 225, col1x + 15, 225, "18px", "#000000");
addField("2", firstName.toUpperCase(), col1x, 255, col1x + 15, 255, "18px", "#000000");
ctx.font = "bold 11px Arial"; ctx.fillStyle = "#9f1239"; ctx.fillText("8", col1x, 295);
const addrParts = address.split(',');
ctx.font = "bold 16px Arial"; ctx.fillStyle = "#000000";
ctx.fillText(addrParts[0].trim().toUpperCase(), col1x + 15, 295);
if(addrParts.length > 1) {
ctx.fillText(addrParts.slice(1).join(',').trim().toUpperCase(), col1x + 15, 315);
}
let statsY = 360;
addField("15 SEX", sex.toUpperCase(), col1x, statsY, col1x, statsY + 18);
addField("16 HGT", height.toUpperCase(), col1x + 65, statsY, col1x + 65, statsY + 18);
addField("17 WGT", weight.toUpperCase(), col1x + 130, statsY, col1x + 130, statsY + 18);
addField("18 EYES", eyes.toUpperCase(), col1x + 200, statsY, col1x + 200, statsY + 18);
addField("19 HAIR", hair.toUpperCase(), col1x + 270, statsY, col1x + 270, statsY + 18);
let classY = 420;
addField("9 CLASS", "C", col1x, classY, col1x + 50, classY);
addField("12 REST", "NONE", col1x + 85, classY, col1x + 140, classY);
addField("13 END", "NONE", col1x + 200, classY, col1x + 250, classY);
// Random DD (Document Discriminator) at bottom
ctx.font = "10px Courier New";
ctx.fillStyle = "#64748b";
let randomDD = "DD " + Math.floor(Math.random()*10000000000000).toString().padStart(16, "0");
ctx.fillText(randomDD, col1x, 460);
// 8. Generate Laser Engraved Ghost Photo
let ghostW = 100;
let ghostH = 130;
let ghostX = 720;
let ghostY = 370;
// Create a temporary canvas directly mapped for the ghost filter effect
const tCanvas = document.createElement("canvas");
tCanvas.width = ghostW;
tCanvas.height = ghostH;
const tCtx = tCanvas.getContext("2d");
tCtx.fillStyle = "#ffffff";
tCtx.fillRect(0,0,ghostW,ghostH);
// Keep aspect ratio matching entirely
let gScale = ghostW / photoW;
tCtx.save();
tCtx.scale(gScale, gScale);
tCtx.translate(-photoX, -photoY);
tCtx.drawImage(originalImg, drawX, drawY, drawW, drawH);
tCtx.restore();
let idata = tCtx.getImageData(0, 0, ghostW, ghostH);
let d = idata.data;
for(let i=0; i<d.length; i+=4) {
let r = d[i], g = d[i+1], b = d[i+2];
let lum = 0.299 * r + 0.587 * g + 0.114 * b;
// Boost contrast heavily to simulate engraving burn marks
lum = (lum - 128) * 2.0 + 128;
lum = Math.max(0, Math.min(255, lum));
// Add subtle horizontal scanline texturing
let row = Math.floor((i / 4) / ghostW);
if(row % 2 === 0) {
lum *= 0.8;
}
// Output warm tinted monotone (laser burn characteristic)
d[i] = lum * 0.90;
d[i+1] = lum * 0.95;
d[i+2] = lum * 1.0;
}
tCtx.putImageData(idata, 0, 0);
// Draw the ghost back onto ID with a multiply blend to burn it in
ctx.save();
ctx.globalCompositeOperation = "multiply";
ctx.globalAlpha = 0.8;
ctx.beginPath();
ctx.roundRect ? ctx.roundRect(ghostX, ghostY, ghostW, ghostH, 8) : ctx.rect(ghostX, ghostY, ghostW, ghostH);
ctx.clip();
ctx.drawImage(tCanvas, ghostX, ghostY);
ctx.restore();
// 9. Fake Realistic Signature
ctx.save();
ctx.font = "italic 44px 'Brush Script MT', 'Bradley Hand', 'Comic Sans MS', cursive";
ctx.fillStyle = "rgba(10, 10, 30, 0.85)";
ctx.translate(140, 460);
ctx.rotate(-0.06); // organic slant
ctx.textAlign = "center";
ctx.fillText(`${firstName.charAt(0).toUpperCase()}${firstName.slice(1).toLowerCase()} ${lastName.charAt(0).toUpperCase()}${lastName.slice(1).toLowerCase()}`, 0, 0);
ctx.restore();
ctx.strokeStyle = "#9f1239";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(35, 475);
ctx.lineTo(245, 475);
ctx.stroke();
// 10. Background Authenticity Seal
ctx.save();
ctx.translate(220, 250);
ctx.globalAlpha = 0.15;
ctx.beginPath(); ctx.arc(0, 0, 60, 0, Math.PI*2);
ctx.lineWidth = 6; ctx.strokeStyle = "#1e3a8a"; ctx.stroke();
ctx.beginPath(); ctx.arc(0, 0, 52, 0, Math.PI*2);
ctx.lineWidth = 1; ctx.stroke();
ctx.fillStyle = "#1e3a8a";
for(let i=0; i<12; i++) {
ctx.rotate(Math.PI*2/12);
ctx.beginPath();
ctx.moveTo(0, -50);
ctx.lineTo(5, -40);
ctx.lineTo(-5, -40);
ctx.fill();
}
ctx.restore();
// 11. Fake Hologram Polish over standard components
ctx.save();
let holo = ctx.createLinearGradient(0, 0, 860, 540);
holo.addColorStop(0, "rgba(255, 255, 255, 0.05)");
holo.addColorStop(0.2, "rgba(255, 200, 200, 0.05)");
holo.addColorStop(0.4, "rgba(200, 255, 200, 0.05)");
holo.addColorStop(0.6, "rgba(200, 200, 255, 0.05)");
holo.addColorStop(1, "rgba(255, 255, 255, 0.05)");
ctx.globalCompositeOperation = "screen";
ctx.fillStyle = holo;
ctx.fillRect(0, 0, 860, 540);
ctx.restore();
// 12. Finalize Base Shape (Card Edges & Punchout)
ctx.lineWidth = 4;
ctx.strokeStyle = "#dddddd";
ctx.beginPath();
ctx.roundRect ? ctx.roundRect(0, 0, 860, 540, 16) : ctx.rect(0, 0, 860, 540);
ctx.stroke();
ctx.globalCompositeOperation = "destination-in";
ctx.beginPath();
ctx.fillStyle = "#000";
if (ctx.roundRect) {
ctx.roundRect(0, 0, 860, 540, 16);
} else {
ctx.rect(0, 0, 860, 540);
}
ctx.fill();
return canvas;
}
Apply Changes