You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, domain = 'example.com', title = 'Home - My Website', favicon = '🌐', theme = 'light', bg = 'gradient') {
return new Promise(async (resolve, reject) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Validate and size image
if (!originalImg || !originalImg.width) {
throw new Error("Invalid or unloaded image provided.");
}
const minW = 480;
let width = originalImg.width;
let height = originalImg.height;
// Proportional scale up if below min-width
if (width < minW) {
const ratio = minW / width;
width = minW;
height = height * ratio;
}
// Dynamic scale factor for UI
const s = Math.max(0.6, width / 1200);
// UI Proportions
const padding = 40 * s;
const tabAreaH = 40 * s;
const navAreaH = 40 * s;
const headerH = tabAreaH + navAreaH;
const r = 10 * s;
canvas.width = width + padding * 2;
canvas.height = height + headerH + padding * 2;
// Reusable logic for drawing rounded rectangles
const drawRoundRect = (ctx, x, y, w, h, radii) => {
let tl = 0, tr = 0, br = 0, bl = 0;
if (typeof radii === 'number') { tl = tr = br = bl = radii; }
else if (Array.isArray(radii)) {
if (radii.length === 4) { [tl, tr, br, bl] = radii; }
else if (radii.length === 2) { [tl, tr] = radii; br = tl; bl = tr; }
}
ctx.beginPath();
ctx.moveTo(x + tl, y);
ctx.lineTo(x + w - tr, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + tr);
ctx.lineTo(x + w, y + h - br);
ctx.quadraticCurveTo(x + w, y + h, x + w - br, y + h);
ctx.lineTo(x + bl, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - bl);
ctx.lineTo(x, y + tl);
ctx.quadraticCurveTo(x, y, x + tl, y);
ctx.closePath();
};
// 1. Draw Canvas Background
if (bg.toLowerCase() === 'gradient') {
const grad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
grad.addColorStop(0, '#e2e8f0');
grad.addColorStop(1, '#94a3b8');
ctx.fillStyle = grad;
} else {
ctx.fillStyle = bg;
}
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Window Base Settings
const wX = padding;
const wY = padding;
const wW = width;
const wH = height + headerH;
const isDark = theme.toLowerCase() === 'dark';
const frameColor = isDark ? '#202124' : '#dee1e6';
const tabBg = isDark ? '#35363a' : '#ffffff';
const textColor = isDark ? '#f1f3f4' : '#3c4043';
const mutedText = isDark ? '#9aa0a6' : '#5f6368';
// 2. Draw Window Shadow and Base Frame
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowBlur = 25 * s;
ctx.shadowOffsetY = 15 * s;
ctx.fillStyle = frameColor;
drawRoundRect(ctx, wX, wY, wW, wH, r);
ctx.fill();
ctx.shadowColor = 'transparent'; // Stop shadow for subsequent inner drawings
// 3. Draw Tab Header Content (Mac Style Dots)
const dotY = wY + tabAreaH / 2;
const dotR = 6 * s;
const dotGap = 20 * s;
['#ff5f56', '#ffbd2e', '#27c93f'].forEach((color, i) => {
ctx.beginPath();
ctx.arc(wX + 20 * s + i * dotGap, dotY, dotR, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
});
// 4. Draw Active Tab
const tabX = wX + 80 * s;
const tabY = wY + 8 * s;
const tabW = Math.min(240 * s, wW - tabX - 10 * s); // responsive tab width
const tabH = tabAreaH - 8 * s;
const tabR = 8 * s;
ctx.fillStyle = tabBg;
drawRoundRect(ctx, tabX, tabY, tabW, tabH, [tabR, tabR, 0, 0]);
ctx.fill();
// Add Favicon
const textY = tabY + tabH / 2;
const favSize = 16 * s;
const titleOffset = 35 * s;
if (favicon.startsWith('data:image/')) {
const imgParams = new Image();
await new Promise(res => {
imgParams.onload = () => {
ctx.drawImage(imgParams, tabX + 12 * s, textY - favSize / 2, favSize, favSize);
res();
};
imgParams.onerror = () => res(); // fail gracefully
imgParams.src = favicon;
});
} else {
ctx.font = `${14 * s}px sans-serif`;
ctx.textBaseline = 'middle';
ctx.fillStyle = textColor;
ctx.fillText(favicon, tabX + 12 * s, textY);
}
// Add Tab Title
ctx.fillStyle = textColor;
ctx.font = `${12 * s}px sans-serif`;
let dispTitle = title;
const maxTitleW = tabW - titleOffset - 15 * s;
while(ctx.measureText(dispTitle).width > maxTitleW && dispTitle.length > 0) {
dispTitle = dispTitle.slice(0, -2) + '…';
}
ctx.fillText(dispTitle, tabX + titleOffset, textY);
// 5. Draw Navigation Bar Background
ctx.fillStyle = tabBg;
ctx.fillRect(wX, wY + tabAreaH, wW, navAreaH);
ctx.beginPath();
ctx.moveTo(wX, wY + headerH);
ctx.lineTo(wX + wW, wY + headerH);
ctx.strokeStyle = isDark ? '#000000' : '#dadce0';
ctx.lineWidth = 1;
ctx.stroke();
// 6. Draw Navigation Icons
ctx.fillStyle = mutedText;
ctx.font = `${16 * s}px sans-serif`;
const navYCenter = wY + tabAreaH + navAreaH / 2;
ctx.fillText('←', wX + 20 * s, navYCenter);
ctx.fillText('→', wX + 50 * s, navYCenter);
ctx.fillText('↻', wX + 80 * s, navYCenter);
// 7. Draw Address Bar Pill
const addrX = wX + 110 * s;
const addrY = wY + tabAreaH + 6 * s;
const addrW = wW - addrX - 20 * s;
const addrH = navAreaH - 12 * s;
ctx.fillStyle = isDark ? '#202124' : '#f1f3f4';
drawRoundRect(ctx, addrX, addrY, addrW, addrH, addrH / 2);
ctx.fill();
// 8. Draw Lock and URL text
ctx.fillStyle = mutedText;
ctx.font = `${11 * s}px sans-serif`;
ctx.fillText('🔒', addrX + 15 * s, navYCenter);
const cleanDomain = domain.replace(/^https?:\/\//, '');
ctx.fillStyle = textColor;
ctx.font = `${14 * s}px sans-serif`;
ctx.save();
ctx.beginPath();
ctx.rect(addrX + 35 * s, addrY, addrW - 40 * s, addrH);
ctx.clip(); // Ensure URL doesn't spill out of address bar
ctx.fillText(`https://${cleanDomain}`, addrX + 40 * s, navYCenter);
ctx.restore();
// 9. Draw the internal Screenshot image
ctx.save();
ctx.beginPath();
drawRoundRect(ctx, wX, wY + headerH, wW, height, [0, 0, r, r]);
ctx.clip();
ctx.drawImage(originalImg, wX, wY + headerH, width, height);
ctx.restore();
// Resolve standard promise with canvas
resolve(canvas);
} catch (err) {
reject(err);
}
});
}
Apply Changes