You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, theme = "museum", comicEffect = 1) {
// Determine maximum dimensions to prevent memory issues for large images
const MAX_SIZE = 700;
let imgW = originalImg.width;
let imgH = originalImg.height;
// Scale proportionally if image exceeds max dimension
if (imgW > MAX_SIZE || imgH > MAX_SIZE) {
const aspect = imgW / imgH;
if (imgW > imgH) {
imgW = MAX_SIZE;
imgH = MAX_SIZE / aspect;
} else {
imgH = MAX_SIZE;
imgW = MAX_SIZE * aspect;
}
}
imgW = Math.floor(imgW);
imgH = Math.floor(imgH);
// Apply Comic/Halftone stylized effect using an offscreen canvas
const fxCanvas = document.createElement('canvas');
fxCanvas.width = imgW;
fxCanvas.height = imgH;
const fxCtx = fxCanvas.getContext('2d');
fxCtx.drawImage(originalImg, 0, 0, imgW, imgH);
if (Number(comicEffect) === 1) {
const imgData = fxCtx.getImageData(0, 0, imgW, imgH);
const data = imgData.data;
const levels = 6;
for (let i = 0; i < data.length; i += 4) {
let r = data[i], g = data[i+1], b = data[i+2];
// Boost contrast & saturation for that popping comic look
r = r < 128 ? r * 0.8 : Math.min(255, r * 1.25);
g = g < 128 ? g * 0.8 : Math.min(255, g * 1.25);
b = b < 128 ? b * 0.8 : Math.min(255, b * 1.25);
// Posterization (reduce color palette)
data[i] = Math.floor(r / (255 / levels)) * (255 / levels);
data[i+1] = Math.floor(g / (255 / levels)) * (255 / levels);
data[i+2] = Math.floor(b / (255 / levels)) * (255 / levels);
}
fxCtx.putImageData(imgData, 0, 0);
// Apply a comic book halftone dot overlay pattern
fxCtx.globalCompositeOperation = "multiply";
fxCtx.fillStyle = "rgba(0, 0, 0, 0.12)";
const dotSize = 3;
for(let y = 0; y < imgH; y += dotSize * 2.5) {
for(let x = 0; x < imgW; x += dotSize * 2.5) {
fxCtx.beginPath();
// Offset every other row for hexagonal dot pattern
let offsetX = (Math.floor(y / (dotSize * 2.5)) % 2 === 0) ? 0 : dotSize * 1.25;
fxCtx.arc(x + offsetX, y, dotSize / 2, 0, Math.PI * 2);
fxCtx.fill();
}
}
fxCtx.globalCompositeOperation = "source-over";
}
// Set Layout padding constants
const PADDING_FRAME = 20; // 20px wood frame width
const PADDING_MAT = 40; // 40px internal matboard padding
const TOTAL_OBJ_PAD = PADDING_FRAME + PADDING_MAT;
const frameW = imgW + (TOTAL_OBJ_PAD * 2);
const frameH = imgH + (TOTAL_OBJ_PAD * 2);
// Create Main Wall Canvas
const canvas = document.createElement("canvas");
const wallWidth = frameW + 240;
const wallHeight = frameH + 300;
canvas.width = wallWidth;
canvas.height = wallHeight;
const ctx = canvas.getContext("2d");
// 1. Draw Gallery Background Wall Map
let grad = ctx.createLinearGradient(0, 0, 0, wallHeight);
if (theme === "dark" || theme === "museum") {
grad.addColorStop(0, "#3a3c42");
grad.addColorStop(1, "#121316");
} else {
grad.addColorStop(0, "#fdfdfd");
grad.addColorStop(1, "#d4d4d4");
}
ctx.fillStyle = grad;
ctx.fillRect(0, 0, wallWidth, wallHeight);
// Spotlight Cone overhead effect
const spotX = wallWidth / 2;
const spotY = 0;
const spotGrad = ctx.createRadialGradient(spotX, spotY, 10, spotX, spotY, wallHeight * 0.8);
spotGrad.addColorStop(0, "rgba(255, 255, 255, 0.15)");
spotGrad.addColorStop(1, "rgba(255, 255, 255, 0)");
ctx.fillStyle = spotGrad;
ctx.beginPath();
ctx.moveTo(spotX - 100, 0);
ctx.lineTo(spotX + 100, 0);
ctx.lineTo(wallWidth, wallHeight);
ctx.lineTo(0, wallHeight);
ctx.fill();
const frameX = (wallWidth - frameW) / 2;
const frameY = 80;
// 2. Draw Frame Shadow
ctx.shadowColor = "rgba(0, 0, 0, 0.7)";
ctx.shadowBlur = 30;
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 15;
ctx.fillStyle = "#111";
ctx.fillRect(frameX, frameY, frameW, frameH);
ctx.shadowColor = "transparent"; // Reset shadow
// 3. Draw Outer Photo Frame
let frameGrad = ctx.createLinearGradient(frameX, frameY, frameX + frameW, frameY + frameH);
frameGrad.addColorStop(0, "#222");
frameGrad.addColorStop(0.5, "#000");
frameGrad.addColorStop(1, "#222");
ctx.fillStyle = frameGrad;
ctx.fillRect(frameX, frameY, frameW, frameH);
// 4. Draw Inner Matboard
ctx.fillStyle = "#f8f8f6";
ctx.fillRect(frameX + PADDING_FRAME, frameY + PADDING_FRAME, frameW - PADDING_FRAME * 2, frameH - PADDING_FRAME * 2);
// Inner mat shadow
ctx.strokeStyle = "rgba(0, 0, 0, 0.1)";
ctx.lineWidth = 2;
ctx.strokeRect(frameX + PADDING_FRAME, frameY + PADDING_FRAME, frameW - PADDING_FRAME * 2, frameH - PADDING_FRAME * 2);
ctx.strokeRect(frameX + TOTAL_OBJ_PAD, frameY + TOTAL_OBJ_PAD, imgW, imgH);
// 5. Draw Target Comic Image
ctx.drawImage(fxCanvas, frameX + TOTAL_OBJ_PAD, frameY + TOTAL_OBJ_PAD);
// 6. Optional subtle glass glare effect over the image
ctx.fillStyle = "rgba(255, 255, 255, 0.05)";
ctx.beginPath();
ctx.moveTo(frameX + PADDING_FRAME, frameY + PADDING_FRAME);
ctx.lineTo(frameX + frameW / 2, frameY + PADDING_FRAME);
ctx.lineTo(frameX + PADDING_FRAME, frameY + frameH / 2);
ctx.fill();
// 7. Draw the Gallery Plaque below
const plaqueW = 260;
const plaqueH = 90;
const plaqueX = (wallWidth - plaqueW) / 2;
const plaqueY = frameY + frameH + 60;
// Plaque Shadow
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetY = 5;
ctx.fillStyle = "#e0e0e0"; // Metallic Museum Card
ctx.fillRect(plaqueX, plaqueY, plaqueW, plaqueH);
ctx.shadowColor = "transparent";
// Plaque Border Highlight
ctx.strokeStyle = "#fff";
ctx.lineWidth = 2;
ctx.strokeRect(plaqueX + 2, plaqueY + 2, plaqueW - 4, plaqueH - 4);
// 8. San Diego Comic-Con Eye Logo (Geometric replica)
const eyeX = wallWidth / 2;
const eyeY = plaqueY + 25;
ctx.fillStyle = "#000";
ctx.beginPath();
ctx.moveTo(eyeX - 25, eyeY); // Left tip
ctx.quadraticCurveTo(eyeX, eyeY - 25, eyeX + 25, eyeY); // Top arch
ctx.quadraticCurveTo(eyeX, eyeY + 25, eyeX - 25, eyeY); // Bottom arch
ctx.fill();
// Inner white iris
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(eyeX, eyeY, 8, 0, Math.PI * 2);
ctx.fill();
// Inner black pupil
ctx.fillStyle = "#000";
ctx.beginPath();
ctx.arc(eyeX, eyeY, 4, 0, Math.PI * 2);
ctx.fill();
// 9. Informational Texts
ctx.fillStyle = "#222";
ctx.textAlign = "center";
// Title Line
ctx.font = "bold 14px Arial, Helvetica, sans-serif";
ctx.fillText("SAN DIEGO COMIC-CON", wallWidth / 2, eyeY + 28);
// Subtitle Line
ctx.font = "11px Arial, Helvetica, sans-serif";
ctx.fillText("EXCLUSIVE IMAGE GALLERY", wallWidth / 2, eyeY + 45);
return canvas;
}
Apply Changes