You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Visualizes "monsters around" an image by drawing procedurally generated monster features
* like eyes, tentacles, or slimes on top of it.
*
* @param {Image} originalImg The original image object to process.
* @param {string} monsterType The type of monster to draw. Accepts 'eyes', 'tentacles', or 'slimes'. Defaults to 'eyes'.
* @param {number} monsterCount The number of monsters to draw on the image. Defaults to 10.
* @param {number} monsterSize The base size of each monster in pixels. Defaults to 50.
* @param {number} monsterOpacity The opacity of the monsters, from 0 (transparent) to 1 (opaque). Defaults to 0.8.
* @param {string} monsterColor The color of the monsters in a CSS-compatible format (e.g., '#ff0000', 'red'). Defaults to '#ff0000'.
* @returns {HTMLCanvasElement} A canvas element with the monsters drawn over the original image.
*/
function processImage(originalImg, monsterType = 'eyes', monsterCount = 10, monsterSize = 50, monsterOpacity = 0.8, monsterColor = '#ff0000') {
// Parameter validation and coercion
const count = parseInt(monsterCount, 10) || 10;
const size = Math.max(5, parseFloat(monsterSize)) || 50;
const opacity = Math.max(0, Math.min(1, parseFloat(monsterOpacity) || 0.8));
// Canvas setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// Draw original image
ctx.drawImage(originalImg, 0, 0, w, h);
// Set monster drawing style
ctx.globalAlpha = opacity;
// --- Helper drawing functions ---
const drawEyes = (x, y) => {
ctx.fillStyle = monsterColor;
const eyeRadius = size / 5;
const eyeSpacing = size / 4;
// Draw the two eye circles
ctx.beginPath();
ctx.arc(x - eyeSpacing, y, eyeRadius, 0, 2 * Math.PI);
ctx.arc(x + eyeSpacing, y, eyeRadius, 0, 2 * Math.PI);
ctx.fill();
// Draw pupils looking towards the center of the image
const dx = w / 2 - x;
const dy = h / 2 - y;
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
const pupilOffsetX = (dx / dist) * (eyeRadius / 2);
const pupilOffsetY = (dy / dist) * (eyeRadius / 2);
const pupilRadius = eyeRadius / 2;
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(x - eyeSpacing + pupilOffsetX, y + pupilOffsetY, pupilRadius, 0, 2 * Math.PI);
ctx.arc(x + eyeSpacing + pupilOffsetX, y + pupilOffsetY, pupilRadius, 0, 2 * Math.PI);
ctx.fill();
};
const drawTentacle = (x, y, edge) => {
ctx.strokeStyle = monsterColor;
ctx.lineWidth = Math.max(2, size / 8);
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(x, y);
let endX, endY, cp1x, cp1y, cp2x, cp2y;
const length = size * 1.5;
const spread = size;
// Determine control points based on which edge the tentacle originates from
if (edge === 0) { // Top
endY = y + length;
endX = x + (Math.random() - 0.5) * spread;
cp1y = y + length / 3;
cp1x = x + (Math.random() - 0.5) * spread;
cp2y = y + (length * 2) / 3;
cp2x = x + (Math.random() - 0.5) * spread;
} else if (edge === 1) { // Right
endX = x - length;
endY = y + (Math.random() - 0.5) * spread;
cp1x = x - length / 3;
cp1y = y + (Math.random() - 0.5) * spread;
cp2x = x - (length * 2) / 3;
cp2y = y + (Math.random() - 0.5) * spread;
} else if (edge === 2) { // Bottom
endY = y - length;
endX = x + (Math.random() - 0.5) * spread;
cp1y = y - length / 3;
cp1x = x + (Math.random() - 0.5) * spread;
cp2y = y - (length * 2) / 3;
cp2x = x + (Math.random() - 0.5) * spread;
} else { // Left
endX = x + length;
endY = y + (Math.random() - 0.5) * spread;
cp1x = x + length / 3;
cp1y = y + (Math.random() - 0.5) * spread;
cp2x = x + (length * 2) / 3;
cp2y = y + (Math.random() - 0.5) * spread;
}
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
ctx.stroke();
};
const drawSlime = (x, y, edge) => {
ctx.fillStyle = monsterColor;
let centerX = x;
let centerY = y;
const radius = size / 2;
if (edge === 0) centerY += radius;
if (edge === 1) centerX -= radius;
if (edge === 2) centerY -= radius;
if (edge === 3) centerX += radius;
ctx.beginPath();
const points = 6 + Math.floor(Math.random() * 5);
const angleStep = (Math.PI * 2) / points;
const startAngle = Math.random() * Math.PI * 2;
const getPoint = (angle) => {
const r = radius * (0.8 + Math.random() * 0.4);
return {
x: centerX + Math.cos(angle) * r,
y: centerY + Math.sin(angle) * r,
};
};
let p1 = getPoint(startAngle);
ctx.moveTo(p1.x, p1.y);
for (let i = 1; i <= points; i++) {
const p2 = getPoint(startAngle + i * angleStep);
const midPoint = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };
ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = p2;
}
ctx.closePath();
ctx.fill();
// Draw eyes on the slime
const tempAlpha = ctx.globalAlpha;
ctx.globalAlpha = Math.min(1.0, opacity + 0.3);
ctx.fillStyle = '#000';
const eyeRadius = size / 10;
const eyeSpacing = size / 8;
ctx.beginPath();
ctx.arc(centerX - eyeSpacing, centerY, eyeRadius, 0, 2 * Math.PI);
ctx.arc(centerX + eyeSpacing, centerY, eyeRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.globalAlpha = tempAlpha;
};
// --- Main loop to draw monsters ---
for (let i = 0; i < count; i++) {
if (monsterType.toLowerCase() === 'eyes') {
// Eyes can appear anywhere in the image for a spooky effect
const randomX = Math.random() * w;
const randomY = Math.random() * h;
drawEyes(randomX, randomY);
continue;
}
// Other monsters peek from the edges
const edge = Math.floor(Math.random() * 4); // 0:top, 1:right, 2:bottom, 3:left
let x, y;
switch (edge) {
case 0: x = Math.random() * w; y = 0; break;
case 1: x = w; y = Math.random() * h; break;
case 2: x = Math.random() * w; y = h; break;
case 3: x = 0; y = Math.random() * h; break;
}
switch (monsterType.toLowerCase()) {
case 'tentacles':
drawTentacle(x, y, edge);
break;
case 'slimes':
drawSlime(x, y, edge);
break;
}
}
return canvas;
}
Apply Changes