You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, textTop = "", textBottom = "", intensity = 100) {
intensity = Number(intensity) || 100;
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw the underlying image
ctx.drawImage(originalImg, 0, 0);
// Apply darkening overlay to make the overlay white text pop
ctx.fillStyle = "rgba(0, 0, 0, 0.35)";
ctx.fillRect(0, 0, w, h);
const baseSize = Math.max(10, Math.min(w, h) / 1000);
// Rotation helper
function drawRotated(x, y, angle, drawFn) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
drawFn(ctx);
ctx.restore();
}
// Fraction drawing helper
function drawFraction(c, x, y, leftText, topText, bottomText, fontSize) {
let oldFont = c.font;
c.font = `italic ${fontSize}px "Times New Roman", serif`;
let leftWidth = c.measureText(leftText).width;
c.fillText(leftText, x, y + fontSize/3);
let topWidth = c.measureText(topText).width;
let bottomWidth = c.measureText(bottomText).width;
let fractionWidth = Math.max(topWidth, bottomWidth);
let fractionStartX = x + leftWidth + 5;
c.fillText(topText, fractionStartX + (fractionWidth - topWidth)/2, y - fontSize*0.05);
c.fillText(bottomText, fractionStartX + (fractionWidth - bottomWidth)/2, y + fontSize*1.05);
c.beginPath();
c.lineCap = "round";
c.moveTo(fractionStartX, y + fontSize * 0.35);
c.lineTo(fractionStartX + fractionWidth, y + fontSize * 0.35);
c.lineWidth = Math.max(2, fontSize/15);
c.strokeStyle = c.fillStyle;
c.stroke();
c.font = oldFont;
}
// Graph helper
function drawGraph(c, x, y, s) {
c.lineWidth = Math.max(2, s/30);
c.strokeStyle = c.fillStyle;
c.lineCap = "round";
c.lineJoin = "round";
c.beginPath();
c.moveTo(x, y - s*0.2);
c.lineTo(x, y + s);
c.lineTo(x + s*1.2, y + s);
c.stroke();
c.beginPath();
c.moveTo(x + s*0.1, y + s*0.1);
c.quadraticCurveTo(x + s*0.5, y + s*1.3, x + s*0.9, y + s*0.1);
c.stroke();
}
// Geometry triangle helper
function drawTriangle(c, x, y, s) {
c.lineWidth = Math.max(2, s/40);
c.strokeStyle = c.fillStyle;
c.lineJoin = "round";
c.beginPath();
c.moveTo(x, y);
c.lineTo(x + s, y + s/2);
c.lineTo(x + s*0.2, y + s*0.8);
c.closePath();
c.stroke();
let oldFont = c.font;
c.font = `italic ${s/4}px "Times New Roman", serif`;
c.fillText("a", x + s/2 + s/20, y + s/4);
c.fillText("b", x + s*0.6, y + s*0.75);
c.fillText("c", x + s*0.05, y + s*0.5);
c.font = oldFont;
}
// Geometry circle helper
function drawCircleRadius(c, x, y, r) {
c.lineWidth = Math.max(2, r/20);
c.strokeStyle = c.fillStyle;
c.beginPath();
c.arc(x, y, r, 0, Math.PI * 2);
c.stroke();
c.beginPath();
c.moveTo(x, y);
c.lineTo(x + r*0.8, y - r*0.6);
c.stroke();
c.beginPath();
c.arc(x, y, r/15, 0, Math.PI * 2);
c.fillStyle = c.strokeStyle;
c.fill();
let oldFont = c.font;
c.font = `italic ${r/2.5}px "Times New Roman", serif`;
c.fillText("r", x + r*0.4, y - r*0.1);
c.font = oldFont;
}
// Tear drop drawing
function drawTearDrop(c, x, y, size) {
c.save();
c.translate(x, y);
c.scale(size, size);
c.beginPath();
c.moveTo(0, 0); // Top sharp point
// Smoothly round at the bottom point horizontally forming a tear shape
c.bezierCurveTo(0.5, 1.5, 1.5, 3.5, 0, 3.5);
c.bezierCurveTo(-1.5, 3.5, -0.5, 1.5, 0, 0);
c.fillStyle = "rgba(100, 180, 255, 0.4)";
c.shadowColor = "rgba(0, 0, 0, 0.3)";
c.shadowBlur = 5;
c.shadowOffsetX = 2;
c.shadowOffsetY = 5;
c.fill();
c.lineWidth = 0.1;
c.strokeStyle = "rgba(255, 255, 255, 0.6)";
c.shadowBlur = 0;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.stroke();
// Little white reflection/highlight
c.beginPath();
c.moveTo(-0.4, 2.0);
c.bezierCurveTo(-0.7, 2.4, -0.6, 3.0, -0.1, 3.3);
c.strokeStyle = "rgba(255, 255, 255, 0.9)";
c.lineWidth = 0.2;
c.stroke();
c.restore();
}
// Universal Math Styling
let alpha = Math.min(1, Math.max(0, (intensity / 100) * 0.95));
ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
ctx.shadowColor = "rgba(0, 0, 0, 0.9)";
ctx.shadowBlur = 5 * baseSize;
ctx.shadowOffsetX = 2 * baseSize;
ctx.shadowOffsetY = 2 * baseSize;
// --- DRAW BACKGROUND MATH (Out of Focus) ---
ctx.save();
ctx.globalAlpha = 0.3 * alpha;
ctx.shadowBlur = 12 * baseSize;
drawRotated(w*0.1, h*0.2, -0.2, (c) => {
c.font = `bold italic ${180 * baseSize}px "Times New Roman", serif`;
c.fillText("Σ x²", 0, 0);
});
drawRotated(w*0.7, h*0.7, 0.2, (c) => {
c.font = `bold italic ${150 * baseSize}px "Times New Roman", serif`;
c.fillText("σ², μ", 0, 0);
});
drawRotated(w*0.8, h*0.2, -0.1, (c) => {
c.font = `bold italic ${160 * baseSize}px "Times New Roman", serif`;
c.fillText("f'(x)", 0, 0);
});
ctx.restore();
// --- DRAW FOREGROUND MATH ---
const sFont = 40 * baseSize;
const mFont = 60 * baseSize;
const lFont = 80 * baseSize;
// 1. Quadratic (Top Left)
drawRotated(w*0.05, h*0.15, -0.05, (c) => {
drawFraction(c, 0, 0, "x = ", "-b ± √(b² - 4ac)", "2a", lFont);
});
// 2. Pythagoras (Bottom Left)
drawRotated(w*0.1, h*0.65, 0.08, (c) => {
c.font = `italic ${mFont}px "Times New Roman", serif`;
c.fillText("a² + b² = c²", 0, 0);
});
// 3. Triangle (Bottom Left)
drawRotated(w*0.15, h*0.78, -0.05, (c) => {
drawTriangle(c, 0, 0, 160 * baseSize);
});
// 4. Graph (Top Right)
drawRotated(w*0.7, h*0.05, 0.1, (c) => {
drawGraph(c, 0, 0, 140 * baseSize);
c.font = `italic ${sFont}px "Times New Roman", serif`;
c.fillText("y = ax² + bx + c", -20 * baseSize, 180 * baseSize);
});
// 5. Integral (Center Right)
drawRotated(w*0.6, h*0.45, -0.07, (c) => {
c.font = `italic ${lFont}px "Times New Roman", serif`;
c.fillText("∫ eˣ dx = eˣ + C", 0, 0);
});
// 6. Trigonometry (Bottom Right)
drawRotated(w*0.62, h*0.8, -0.12, (c) => {
drawFraction(c, 0, 0, "tan(θ) = ", "sin(θ)", "cos(θ)", mFont);
});
// 7. Circle (Center left)
drawRotated(w*0.25, h*0.4, 0.1, (c) => {
drawCircleRadius(c, 0, 0, 100 * baseSize);
c.font = `italic ${sFont}px "Times New Roman", serif`;
c.fillText("A = πr²", -30*baseSize, 140 * baseSize);
});
// 8. Physics (Top Center)
drawRotated(w*0.42, h*0.08, -0.03, (c) => {
c.font = `italic ${mFont}px "Times New Roman", serif`;
c.fillText("E = mc²", 0, 0);
});
// 9. Matrix (Center Below)
drawRotated(w*0.35, h*0.88, 0.05, (c) => {
c.font = `${mFont}px "Times New Roman", serif`;
c.fillText("1 0", 0, 0);
c.fillText("0 1", 0, mFont*1.1);
let tw = c.measureText("1 0").width;
c.lineWidth = Math.max(2, mFont/15);
c.strokeStyle = c.fillStyle;
c.lineCap = "square";
c.beginPath();
// Left bracket: [
c.moveTo(-10, -mFont*0.8);
c.lineTo(-20, -mFont*0.8);
c.lineTo(-20, mFont*1.3);
c.lineTo(-10, mFont*1.3);
c.stroke();
// Right bracket: ]
c.beginPath();
c.moveTo(tw + 10, -mFont*0.8);
c.lineTo(tw + 20, -mFont*0.8);
c.lineTo(tw + 20, mFont*1.3);
c.lineTo(tw + 10, mFont*1.3);
c.stroke();
});
// 10. Calculus limit Eq (Center top area)
drawRotated(w*0.4, h*0.25, -0.03, (c) => {
c.font = `italic ${mFont}px "Times New Roman", serif`;
c.fillText("lim (1 + 1/n)ⁿ = e", 0, 0);
c.font = `italic ${mFont*0.6}px "Times New Roman", serif`;
c.fillText("n→∞", mFont*0.3, mFont*0.65);
});
// --- DRAW TEARS ---
let numTears = Math.floor(25 * (intensity/100));
for (let i = 0; i < numTears; i++) {
// Semi-random deterministic positioning
let tx = ((i * 37) % 100) / 100 * w;
let ty = ((i * 53) % 100) / 100 * h;
let ts = (10 + ((i * 17) % 15)) * baseSize;
drawTearDrop(ctx, tx, ty, ts);
}
// --- DRAW MEME TEXT (Impact Font) ---
function drawMemeText(c, w, h, text, isBottom) {
if (!text) return;
text = String(text).toUpperCase();
c.font = `bold ${Math.max(25, h / 10)}px Impact, sans-serif`;
c.textAlign = "center";
c.lineWidth = Math.max(4, h / 160);
c.strokeStyle = "black";
c.fillStyle = "white";
// Erase any generic math shadow
c.shadowBlur = 0;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
let margin = h * 0.05;
let x = w / 2;
let lineHeight = Math.max(30, h / 9);
let maxWidth = w * 0.9;
// Multi-line word wrap
let words = text.split(" ");
let line = "";
let lines = [];
for(let n = 0; n < words.length; n++) {
let testLine = line + (line === "" ? "" : " ") + words[n];
let metrics = c.measureText(testLine);
if (metrics.width > maxWidth && n > 0) {
lines.push(line);
line = words[n];
} else {
line = testLine;
}
}
lines.push(line);
let y = isBottom ? h - margin - (lines.length - 1) * lineHeight : margin + lineHeight;
for (let i = 0; i < lines.length; i++) {
c.strokeText(lines[i], x, y + i * lineHeight);
c.fillText(lines[i], x, y + i * lineHeight);
}
}
drawMemeText(ctx, w, h, textTop, false);
drawMemeText(ctx, w, h, textBottom, true);
return canvas;
}
Apply Changes