You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
websiteUrl = "example.com",
theme = "light",
padding = 60,
showShadow = 1,
backgroundStyle = "gradient",
bgColor1 = "#e0c3fc",
bgColor2 = "#8ec5fc"
) {
const pad = Number(padding);
const shadow = Number(showShadow);
// Scale everything relative to a 1000px wide image so that small/large screenshots mockups look uniform
const S = Math.max(0.1, originalImg.width / 1000);
const p = pad * S;
const barH = 40 * S;
const corner = 10 * S;
// Create canvas
const canvas = document.createElement('canvas');
canvas.width = originalImg.width + p * 2;
canvas.height = originalImg.height + barH + p * 2;
const ctx = canvas.getContext('2d');
// 1. Draw Background Space
const bgStyle = backgroundStyle.trim().toLowerCase();
if (bgStyle === 'gradient') {
const bgGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
bgGrad.addColorStop(0, bgColor1);
bgGrad.addColorStop(1, bgColor2);
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
} else if (bgStyle === 'color') {
ctx.fillStyle = bgColor1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// if 'transparent', stays blank
const winX = p;
const winY = p;
const winW = originalImg.width;
const winH = originalImg.height + barH;
// Reusable helper for rounded rectangles path
function drawRoundRect(ctx, x, y, width, height, radius) {
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();
}
// 2. Base Window & Drop Shadow
ctx.save();
if (shadow > 0) {
ctx.shadowColor = "rgba(0, 0, 0, 0.25)";
ctx.shadowBlur = Math.max(10, 30 * S);
ctx.shadowOffsetY = Math.max(5, 15 * S);
}
ctx.beginPath();
drawRoundRect(ctx, winX, winY, winW, winH, corner);
// Ensure base area underneath the screenshot is fully opaque white
// to prevent background bleed if originalImg has transparency
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.restore();
// 3. Inner elements starting with clipping logic to force inner contents to stay within rounded corners
ctx.save();
ctx.beginPath();
drawRoundRect(ctx, winX, winY, winW, winH, corner);
ctx.clip();
const isDark = theme.trim().toLowerCase() === 'dark';
const topBarColor = isDark ? "#2d2d2d" : "#f3f4f6";
const addressBarColor = isDark ? "#1a1a1a" : "#e5e7eb";
const textColor = isDark ? "#a1a1aa" : "#6b7280";
// Render Top Bar (Safari-esque)
ctx.fillStyle = topBarColor;
ctx.fillRect(winX, winY, winW, barH);
// Mac Window Controls (Traffic Lights)
const colors = ["#ff5f56", "#ffbd2e", "#27c93f"];
const dotRadius = 5.5 * S;
const dotSpacing = 18 * S;
const dotStartX = winX + 18 * S;
const dotY = winY + barH / 2;
for(let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.arc(dotStartX + i * dotSpacing, dotY, dotRadius, 0, Math.PI * 2);
ctx.fillStyle = colors[i];
ctx.fill();
}
// Address Box Container
const addrW = Math.max(300 * S, winW * 0.4);
const addrX = winX + (winW - addrW) / 2;
const addrH = 26 * S;
const addrY = winY + (barH - addrH) / 2;
ctx.fillStyle = addressBarColor;
ctx.beginPath();
drawRoundRect(ctx, addrX, addrY, addrW, addrH, 6 * S);
ctx.fill();
// Apply Website URL Text
if (websiteUrl) {
ctx.fillStyle = textColor;
let fontSize = 13 * S;
if (fontSize > addrH * 0.7) {
fontSize = addrH * 0.7;
}
ctx.font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Adding the maxWidth constraint limits texts expanding outside of the address bar container
ctx.fillText(websiteUrl, winX + winW / 2, addrY + addrH / 2 + (1 * S), addrW - (20 * S));
}
// Finally draw original image inside the actual viewport portion
ctx.drawImage(originalImg, winX, winY + barH, winW, originalImg.height);
// Finish off with an inner subtle bezel border around the entire window for maximum realism
ctx.strokeStyle = isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)";
ctx.lineWidth = 1 * S;
ctx.beginPath();
drawRoundRect(ctx, winX, winY, winW, winH, corner);
ctx.stroke();
// Restore states for safety
ctx.restore();
return canvas;
}
Apply Changes