You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
frameColor1 = '#D32F2F', // Muted Red (for main colored band)
frameColor2 = '#FFF9E0', // Softer Cream (for image background and canvas background)
borderColor = '#3E2723', // Dark Brown (for outermost border)
textColor = '#212121', // Dark Gray / Black
mainText = "THE SPECTACULAR",
subText = "EXTRAVAGANZA!",
starColor = '#FFB300', // Amber/Yellow
borderThickness = 15, // Thickness of the outermost border in pixels
padding = 20, // Padding around the image, inside the image's direct background area
bandThickness = 50, // Thickness of the main colored band (e.g., red band) in pixels
fontNameParam = "Bangers", // Font for the text, e.g., "Bangers", "Luckiest Guy", "Arial"
textureOpacity = 0.08 // Opacity for the subtle paper texture (0 to 1, 0 for no texture)
) {
let effectiveFontName = fontNameParam;
const fontMap = {
"Bangers": "https://fonts.gstatic.com/s/bangers/v24/FeVQS0hpEzpageX3msxaa_s2W.woff2",
"Luckiest Guy": "https://fonts.gstatic.com/s/luckiestguy/v18/KFOlCnAfPSnP8EJ0WMfgkFUTvyc.woff2",
"Pacifico": "https://fonts.gstatic.com/s/pacifico/v22/FwZY7-Qmy14u9lezJ-6H6Mk.woff2",
"Arial": null,
"Verdana": null,
"Georgia": null,
"Times New Roman": null,
"serif": null,
"sans-serif": null
};
let fontToLoadUrl = fontMap[effectiveFontName];
if (fontToLoadUrl === undefined) { // Font not in our predefined map
// If it's not a generic CSS font family, fall back. Otherwise, try to use it as is.
const genericFamilies = ["serif", "sans-serif", "monospace", "cursive", "fantasy"];
if (!genericFamilies.includes(effectiveFontName.toLowerCase())) {
console.warn(`Font "${effectiveFontName}" not in predefined map and not a generic family. Falling back to Arial.`);
effectiveFontName = "Arial";
} // else, we assume it might be a system font the browser knows.
fontToLoadUrl = null;
}
if (fontToLoadUrl) {
try {
if (!document.fonts.check(`12px "${effectiveFontName}"`)) {
const fontFace = new FontFace(effectiveFontName, `url(${fontToLoadUrl})`);
await fontFace.load();
document.fonts.add(fontFace);
}
} catch (e) {
console.error(`Font ${effectiveFontName} (${fontToLoadUrl}) failed to load, falling back to Arial:`, e);
effectiveFontName = "Arial";
}
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const imgWidth = originalImg.width;
const imgHeight = originalImg.height;
const mainTextFontSize = Math.max(20, Math.min(80, Math.round(imgWidth / 12)));
const subTextFontSize = Math.max(16, Math.min(60, Math.round(mainTextFontSize * 0.75)));
ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
const mainTextMetrics = ctx.measureText(mainText);
const mainTextActualHeight = (mainTextMetrics.actualBoundingBoxAscent || mainTextFontSize * 0.75) + (mainTextMetrics.actualBoundingBoxDescent || mainTextFontSize * 0.25);
ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
const subTextMetrics = ctx.measureText(subText);
const subTextActualHeight = (subTextMetrics.actualBoundingBoxAscent || subTextFontSize * 0.75) + (subTextMetrics.actualBoundingBoxDescent || subTextFontSize * 0.25);
const topTextSpace = mainText.length > 0 ? Math.max(bandThickness, mainTextActualHeight * 1.6) : bandThickness * 0.6;
const bottomTextSpace = subText.length > 0 ? Math.max(bandThickness, subTextActualHeight * 1.6) : bandThickness * 0.6;
canvas.width = imgWidth + 2 * padding + 2 * bandThickness + 2 * borderThickness;
canvas.height = imgHeight + 2 * padding + 2 * bandThickness + 2 * borderThickness + topTextSpace + bottomTextSpace;
ctx.fillStyle = frameColor2;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = borderColor;
ctx.fillRect(0, 0, canvas.width, borderThickness);
ctx.fillRect(0, canvas.height - borderThickness, canvas.width, borderThickness);
ctx.fillRect(0, 0, borderThickness, canvas.height);
ctx.fillRect(canvas.width - borderThickness, 0, borderThickness, canvas.height);
ctx.fillStyle = frameColor1;
ctx.fillRect(
borderThickness,
borderThickness,
canvas.width - 2 * borderThickness,
canvas.height - 2 * borderThickness
);
const imgBgX = borderThickness + bandThickness;
const imgBgY = borderThickness + bandThickness + topTextSpace;
const imgBgW = imgWidth + 2 * padding;
const imgBgH = imgHeight + 2 * padding;
ctx.fillStyle = frameColor2;
ctx.fillRect(imgBgX, imgBgY, imgBgW, imgBgH);
ctx.drawImage(
originalImg,
imgBgX + padding,
imgBgY + padding,
imgWidth,
imgHeight
);
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
if (mainText.length > 0) {
ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
ctx.textBaseline = 'middle';
ctx.fillText(mainText, canvas.width / 2, borderThickness + topTextSpace / 2);
}
if (subText.length > 0) {
ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
ctx.textBaseline = 'middle';
ctx.fillText(subText, canvas.width / 2, canvas.height - borderThickness - bottomTextSpace / 2);
}
function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
if (outerRadius <= 1 || innerRadius <= 0.5) return;
let rot = Math.PI / 2 * 3;
let x = cx;
let y = cy;
let step = Math.PI / spikes;
ctx.beginPath();
ctx.moveTo(cx, cy - outerRadius);
for (let i = 0; i < spikes; i++) {
x = cx + Math.cos(rot) * outerRadius;
y = cy + Math.sin(rot) * outerRadius;
ctx.lineTo(x, y);
rot += step;
x = cx + Math.cos(rot) * innerRadius;
y = cy + Math.sin(rot) * innerRadius;
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(cx, cy - outerRadius);
ctx.closePath();
ctx.fill();
}
ctx.fillStyle = starColor;
const baseStarOuterRadius = Math.max(5, bandThickness * 0.30);
const baseStarInnerRadius = baseStarOuterRadius / 2;
if (baseStarOuterRadius > 1) {
// Corner stars (on frameColor1 band, within its thickness)
drawStar(borderThickness + bandThickness / 2, borderThickness + bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
drawStar(canvas.width - borderThickness - bandThickness / 2, borderThickness + bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
drawStar(borderThickness + bandThickness / 2, canvas.height - borderThickness - bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
drawStar(canvas.width - borderThickness - bandThickness / 2, canvas.height - borderThickness - bandThickness / 2, 5, baseStarOuterRadius, baseStarInnerRadius);
const textStarScale = 0.8;
if (mainText.length > 0) {
ctx.font = `${mainTextFontSize}px '${effectiveFontName}'`;
const mtWidth = ctx.measureText(mainText).width;
const mtY = borderThickness + topTextSpace / 2;
if (mtWidth > 0) {
drawStar(canvas.width / 2 - mtWidth / 2 - baseStarOuterRadius * textStarScale - 5, mtY, 5, baseStarOuterRadius * textStarScale, baseStarInnerRadius * textStarScale);
drawStar(canvas.width / 2 + mtWidth / 2 + baseStarOuterRadius * textStarScale + 5, mtY, 5, baseStarOuterRadius * textStarScale, baseStarInnerRadius * textStarScale);
}
}
if (subText.length > 0) {
ctx.font = `${subTextFontSize}px '${effectiveFontName}'`;
const stWidth = ctx.measureText(subText).width;
const stY = canvas.height - borderThickness - bottomTextSpace / 2;
if (stWidth > 0) {
drawStar(canvas.width / 2 - stWidth / 2 - baseStarOuterRadius * textStarScale * 0.8 - 5, stY, 5, baseStarOuterRadius * textStarScale * 0.8, baseStarInnerRadius * textStarScale * 0.8);
drawStar(canvas.width / 2 + stWidth / 2 + baseStarOuterRadius * textStarScale * 0.8 + 5, stY, 5, baseStarOuterRadius * textStarScale * 0.8, baseStarInnerRadius * textStarScale * 0.8);
}
}
}
if (textureOpacity > 0 && textureOpacity <= 1) {
ctx.save();
ctx.globalAlpha = textureOpacity;
const textureCanvas = document.createElement('canvas');
const textureCtx = textureCanvas.getContext('2d');
const textureSize = 64;
textureCanvas.width = textureSize;
textureCanvas.height = textureSize;
const iData = textureCtx.createImageData(textureSize, textureSize);
const data = iData.data;
for (let i = 0; i < data.length; i += 4) {
const shade = Math.floor(Math.random() * 40) + 215; // Light gray noise (215-255)
data[i] = shade; data[i + 1] = shade; data[i + 2] = shade; data[i + 3] = 255;
}
textureCtx.putImageData(iData, 0, 0);
ctx.fillStyle = ctx.createPattern(textureCanvas, 'repeat');
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
}
return canvas;
}
Apply Changes