You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
headlineText = "CREATIVE POSTER",
subheadingText = "DESIGN & ART EVENT",
fontFamily = "'Anton', sans-serif",
textColor = "#FFFFFF",
overlayColor = "rgba(0, 0, 0, 0.4)",
effect = "duotone", // Can be "duotone", "grayscale", or "none"
duotoneColor1 = "#2a3b98", // The dark color (shadows) for duotone effect
duotoneColor2 = "#f7d000" // The light color (highlights) for duotone effect
) {
/**
* Dynamically loads a Google Font stylesheet into the document's head.
* Avoids adding duplicate links.
* @param {string} fontName The name of the Google Font to load.
*/
const loadGoogleFont = (fontName) => {
const fontId = `google-font-${fontName.replace(/\s/g, '-')}`;
if (!document.getElementById(fontId)) {
const link = document.createElement('link');
link.id = fontId;
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(/\s/g, '+')}&display=swap`;
document.head.appendChild(link);
}
};
/**
* Converts a hex color string to an RGB object.
* @param {string} hex The hex color string (e.g., "#FF5733").
* @returns {{r: number, g: number, b: number}|null} An object with r, g, b properties or null if invalid.
*/
const hexToRgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
};
// --- Font Loading ---
// Extract the primary font name (e.g., 'Anton' from "'Anton', sans-serif")
const primaryFontName = fontFamily.split(',')[0].replace(/['"]/g, '').trim();
loadGoogleFont(primaryFontName);
try {
await document.fonts.load(`12px "${primaryFontName}"`);
} catch (e) {
console.warn(`Font '${primaryFontName}' could not be loaded. Browser will use a fallback font.`);
}
// --- Canvas Setup ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
willReadFrequently: true
});
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// --- Image Drawing and Effects ---
// 1. Draw the original image
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 2. Apply the chosen visual effect
if (effect !== 'none') {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
if (effect === 'grayscale') {
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
} else if (effect === 'duotone') {
const color1 = hexToRgb(duotoneColor1);
const color2 = hexToRgb(duotoneColor2);
if (color1 && color2) {
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Calculate luminance (perceived brightness)
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
const ratio = luminance / 255;
// Interpolate between the two duotone colors based on luminance
data[i] = color1.r + (color2.r - color1.r) * ratio;
data[i + 1] = color1.g + (color2.g - color1.g) * ratio;
data[i + 2] = color1.b + (color2.b - color1.b) * ratio;
}
}
}
ctx.putImageData(imageData, 0, 0);
}
// 3. Draw a semi-transparent overlay to improve text readability
ctx.fillStyle = overlayColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// --- Text Drawing ---
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const maxWidth = canvas.width * 0.9;
// -- Headline --
let headlineFontSize = Math.floor(canvas.width / 9);
ctx.font = `bold ${headlineFontSize}px ${fontFamily}`;
// Adjust font size dynamically to fit within the canvas width
while (ctx.measureText(headlineText.toUpperCase()).width > maxWidth && headlineFontSize > 10) {
headlineFontSize--;
ctx.font = `bold ${headlineFontSize}px ${fontFamily}`;
}
const headlineY = canvas.height / 2 - headlineFontSize * 0.6;
ctx.fillText(headlineText.toUpperCase(), canvas.width / 2, headlineY);
// -- Subheading --
let subheadingFontSize = Math.floor(canvas.width / 22);
ctx.font = `${subheadingFontSize}px ${fontFamily}`;
// Adjust font size dynamically
while (ctx.measureText(subheadingText.toUpperCase()).width > maxWidth && subheadingFontSize > 8) {
subheadingFontSize--;
ctx.font = `${subheadingFontSize}px ${fontFamily}`;
}
const subheadingY = headlineY + headlineFontSize;
ctx.fillText(subheadingText.toUpperCase(), canvas.width / 2, subheadingY);
return canvas;
}
Apply Changes