You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
title = "Top 10 Secrets Every Blogger Should Know!",
subtitle = "NEW BLOG POST • @blogger",
overlayColor = "#000000",
overlayOpacity = 0.8,
fontFamily = "Montserrat"
) {
// 1. Load the Google Font dynamically
const link = document.createElement('link');
link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/\s+/g, '+')}:wght@400;700&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
try {
// Wait for the fonts to be fully loaded before drawing on the canvas
await document.fonts.load(`700 16px "${fontFamily}"`);
await document.fonts.load(`400 16px "${fontFamily}"`);
} catch (e) {
console.warn("Font loading issue, falling back to system fonts:", e);
}
// 2. Set up the canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// 3. Draw the original image
ctx.drawImage(originalImg, 0, 0, width, height);
// 4. Calculate RGB for the overlay gradient from the hex color
let hex = overlayColor.replace('#', '');
if (hex.length === 3) hex = hex.split('').map(x => x + x).join('');
let num = parseInt(hex, 16);
let r = num >> 16;
let g = (num >> 8) & 255;
let b = num & 255;
// 5. Apply the Blogger-style Gradient Overlay (bottom shadow for text visibility)
const gradient = ctx.createLinearGradient(0, height * 0.3, 0, height);
gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.0)`);
gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, ${overlayOpacity})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 6. Draw a chic inner frame/border commonly used in aesthetic blog post images
const margin = Math.max(width, height) * 0.03;
ctx.strokeStyle = "rgba(255, 255, 255, 0.45)";
ctx.lineWidth = Math.max(2, width * 0.005);
ctx.strokeRect(margin, margin, width - 2 * margin, height - 2 * margin);
// 7. Text configuration
const padding = margin * 2.5;
const maxTextWidth = width - 2 * padding;
const titleSize = Math.max(24, Math.floor(width * 0.065));
const subtitleSize = Math.max(14, Math.floor(width * 0.025));
ctx.textAlign = "left";
ctx.textBaseline = "bottom";
// Add shadow to text for maximum readability over any background
ctx.shadowColor = "rgba(0, 0, 0, 0.7)";
ctx.shadowBlur = width * 0.015;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// 8. Draw Subtitle (Category / Author) at the very bottom
ctx.fillStyle = "#ffffff";
ctx.font = `400 ${subtitleSize}px "${fontFamily}", sans-serif`;
let currentY = height - padding;
if (subtitle) {
ctx.fillText(subtitle.toUpperCase(), padding, currentY);
// Add a small divider line above the subtitle
ctx.fillRect(padding, currentY - subtitleSize * 1.5, width * 0.1, Math.max(2, subtitleSize * 0.1));
currentY -= (subtitleSize * 2.5);
}
// 9. Draw Title (Word Wrapping)
ctx.font = `700 ${titleSize}px "${fontFamily}", sans-serif`;
const words = title.split(' ');
const lines = [];
let currentLine = words[0] || '';
// Wrap words into lines
for (let i = 1; i < words.length; i++) {
let testLine = currentLine + " " + words[i];
let metrics = ctx.measureText(testLine);
if (metrics.width > maxTextWidth) {
lines.push(currentLine);
currentLine = words[i];
} else {
currentLine = testLine;
}
}
if (currentLine) {
lines.push(currentLine);
}
// Draw lines from the bottom up to ensure correct placement above the subtitle
for (let i = lines.length - 1; i >= 0; i--) {
ctx.fillText(lines[i], padding, currentY);
currentY -= (titleSize * 1.25);
}
return canvas;
}
Apply Changes