You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, intensity = 5, screamText = 'DAAAAAAHHHH!', textColor = '#e53935', fontSize = 12) {
/**
* Dynamically loads a font from Google Fonts.
* @param {string} fontName The name of the font to load.
*/
const loadWebFont = async (fontName) => {
const fontId = `font-${fontName.replace(/\s/g, '-')}`;
if (document.getElementById(fontId)) {
// Style tag already exists
await document.fonts.load(`1em ${fontName}`);
return;
}
try {
const link = document.createElement('link');
link.id = fontId;
link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(/\s/g, '+')}&display=swap`;
link.rel = 'stylesheet';
document.head.appendChild(link);
// Wait for the font to be loaded and ready.
await document.fonts.load(`1em ${fontName}`);
} catch (e) {
console.error(`Could not load Google Font "${fontName}". Using fallback.`, e);
}
};
/**
* Converts an SVG string into an Image object.
* @param {string} svgString The raw SVG content.
* @returns {Promise<Image>} A promise that resolves to the loaded Image object.
*/
const loadSvgAsImage = (svgString) => {
return new Promise((resolve, reject) => {
const img = new Image();
// Using btoa is a simple and effective way to create a data URI for SVGs.
img.src = 'data:image/svg+xml;base64,' + btoa(svgString);
img.onload = () => resolve(img);
img.onerror = () => reject('Failed to load SVG image');
});
};
// --- Asset Definitions ---
const rabbidSvg = `
<svg viewBox="0 0 100 120" xmlns="http://www.w3.org/2000/svg">
<g stroke="black" stroke-width="2.5" stroke-linejoin="round">
<path d="M40 50 C 35 15, 45 0, 50 0 C 55 0, 45 15, 50 50" fill="white" />
<path d="M60 50 C 65 15, 55 0, 50 0 C 45 0, 55 15, 50 50" fill="white" />
<path d="M41 40 C 38 20, 45 10, 48 10 C 51 10, 45 20, 48 40" fill="#F8BBD0" stroke="none" />
<path d="M59 40 C 62 20, 55 10, 52 10 C 49 10, 55 20, 52 40" fill="#F8BBD0" stroke="none" />
<ellipse cx="50" cy="75" rx="45" ry="42" fill="white" />
<circle cx="28" cy="70" r="10" fill="#E53935"/>
<circle cx="72" cy="70" r="10" fill="#E53935"/>
<circle cx="26" cy="68" r="3" fill="white"/>
<circle cx="70" cy="68" r="3" fill="white"/>
<ellipse cx="50" cy="98" rx="30" ry="18" fill="black" />
</g>
</svg>`;
const plungerSvg = `
<svg viewBox="0 0 50 100" xmlns="http://www.w3.org/2000/svg">
<g stroke="black" stroke-width="2">
<rect x="20" y="0" width="10" height="80" fill="#A1887F" />
<path d="M 0 78 C 0 95, 50 95, 50 78 L 45 80 C 45 100, 5 100, 5 80 Z" fill="#D32F2F" />
</g>
</svg>`;
try {
// Load all assets concurrently for efficiency
const fontPromise = loadWebFont('Bangers');
const rabbidPromise = loadSvgAsImage(rabbidSvg);
const plungerPromise = loadSvgAsImage(plungerSvg);
const [_, rabbidAsset, plungerAsset] = await Promise.all([fontPromise, rabbidPromise, plungerPromise]);
// --- Canvas Setup ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// Draw the base image
ctx.drawImage(originalImg, 0, 0);
// --- Draw Rabbids and Plungers ---
const numObjects = Math.max(1, Math.min(50, Math.round(intensity * 3)));
for (let i = 0; i < numObjects; i++) {
const asset = Math.random() > 0.4 ? rabbidAsset : plungerAsset;
const assetAspectRatio = asset.width / asset.height;
// Size is 10-30% of the smaller image dimension
const size = (Math.random() * 0.20 + 0.1) * Math.min(canvas.width, canvas.height);
const w = size;
const h = size / assetAspectRatio;
// Random position and rotation
const x = Math.random() * (canvas.width - w) + w / 2;
const y = Math.random() * (canvas.height - h) + h / 2;
const angle = (Math.random() - 0.5) * Math.PI * 0.8; // Random angle up to ~72 degrees
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.globalAlpha = Math.random() * 0.3 + 0.7; // Add some transparency
ctx.drawImage(asset, -w / 2, -h / 2, w, h);
ctx.restore();
}
// --- Draw Text ---
const calculatedFontSize = Math.max(20, (canvas.width * fontSize) / 100);
ctx.font = `${calculatedFontSize}px Bangers, cursive, Impact, sans-serif`;
ctx.fillStyle = textColor;
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = calculatedFontSize / 12;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.save();
// Place text at a random-ish central location
const textX = canvas.width / 2 + (Math.random() - 0.5) * (canvas.width * 0.1);
const textY = canvas.height / 2 + (Math.random() - 0.5) * (canvas.height * 0.2);
ctx.translate(textX, textY);
ctx.rotate((Math.random() - 0.5) * 0.3); // Slight random rotation for the text
// Add a strong shadow for readability
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.strokeText(screamText.toUpperCase(), 0, 0);
ctx.fillText(screamText.toUpperCase(), 0, 0);
ctx.restore();
return canvas;
} catch (error) {
console.error("Failed to process image for Raving Rabbids theme:", error);
// Fallback: return the original image drawn on a canvas
const fallbackCanvas = document.createElement('canvas');
fallbackCanvas.width = originalImg.naturalWidth;
fallbackCanvas.height = originalImg.naturalHeight;
fallbackCanvas.getContext('2d').drawImage(originalImg, 0, 0);
return fallbackCanvas;
}
}
Apply Changes