You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, text = "А как же торт?", bodyColor = "#F5DEB3", icingColor = "#FFF0F5", numCandles = 3) {
numCandles = parseInt(numCandles) || 0;
const canvas = document.createElement('canvas');
const width = 800;
const height = 800;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 1. Draw festive background
ctx.fillStyle = "#e0f7fa";
ctx.fillRect(0, 0, width, height);
// Add confetti/sprinkles in background
const confettiColors = ["#FF69B4", "#33CC33", "#6666FF", "#FFCC00", "#FF9900", "#FF3333"];
for (let i = 0; i < 200; i++) {
let spX = Math.random() * width;
let spY = Math.random() * height;
let spW = Math.random() * 8 + 4;
let spH = Math.random() * 4 + 2;
ctx.save();
ctx.translate(spX, spY);
ctx.rotate(Math.random() * Math.PI);
ctx.fillStyle = confettiColors[Math.floor(Math.random() * confettiColors.length)];
ctx.fillRect(-spW / 2, -spH / 2, spW, spH);
ctx.restore();
}
// Cake geometrical setups
const cx = 400; // Center X
const cy = 550; // Center Y at bottom of cake
const rx = 260; // Radius X (Width)
const ry = 100; // Radius Y (Perspective Depth)
const h = 200; // Height of the cake
const ty = cy - h;// Top Y position
// 2. Draw cake plate
ctx.fillStyle = "#fcfcfc";
ctx.beginPath();
ctx.ellipse(cx, cy + 15, rx + 60, ry + 30, 0, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = "#cccccc";
ctx.lineWidth = 4;
ctx.stroke();
// 3. Draw cake base (cylinder body)
ctx.fillStyle = bodyColor;
ctx.beginPath();
// Bottom ellipse
ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
ctx.fill();
// Body rectangle
ctx.fillRect(cx - rx, ty, rx * 2, h);
// 4. Draw cake filling/layer (a dark stripe in the middle)
ctx.fillStyle = "#8b4513"; // Chocolate filling
ctx.beginPath();
ctx.ellipse(cx, cy - h / 2, rx, ry, 0, 0, Math.PI, false);
ctx.lineTo(cx - rx, cy - h / 2 - 12);
ctx.ellipse(cx, cy - h / 2 - 12, rx, ry, 0, Math.PI, 0, true);
ctx.fill();
// 5. Draw Image on top as photo icing (clipped to top ellipse)
ctx.save();
ctx.beginPath();
ctx.ellipse(cx, ty, rx, ry, 0, 0, Math.PI * 2);
ctx.clip(); // Clip everything drawn next to this top surface
// Calculate object-fit: cover logic for the ellipse constraints
const imgRatio = originalImg.width / originalImg.height;
const areaRatio = rx / ry;
let sw = originalImg.width, sh = originalImg.height;
let sx = 0, sy = 0;
if (imgRatio > areaRatio) {
sw = originalImg.height * areaRatio;
sx = (originalImg.width - sw) / 2;
} else {
sh = originalImg.width / areaRatio;
sy = (originalImg.height - sh) / 2;
}
ctx.drawImage(originalImg, sx, sy, sw, sh, cx - rx, ty - ry, rx * 2, ry * 2);
// Add a slight lightening overlay to blend it like icing
ctx.fillStyle = "rgba(255,255,255,0.15)";
ctx.fillRect(cx - rx, ty - ry, rx * 2, ry * 2);
ctx.restore();
// 6. Draw icing decorations (drips around the top edge)
ctx.fillStyle = icingColor;
// Front half drips
for (let i = 0; i <= 120; i++) {
let t = (i / 120) * Math.PI; // 0 to PI
let scX = cx + rx * Math.cos(t);
let scY = ty + ry * Math.sin(t);
// Random but deterministic drip lengths
let dripLength = 25 + Math.sin(i * 12345) * 20;
ctx.beginPath();
ctx.arc(scX, scY, 18, 0, Math.PI * 2);
ctx.fill();
// Drip going down
ctx.fillRect(scX - 10, scY, 20, dripLength);
ctx.beginPath();
ctx.arc(scX, scY + dripLength, 10, 0, Math.PI * 2);
ctx.fill();
}
// Top border piping framing the photo
for (let i = 0; i <= 200; i++) {
let t = (i / 200) * Math.PI * 2;
let scX = cx + (rx - 10) * Math.cos(t);
let scY = ty + (ry - 10) * Math.sin(t);
ctx.beginPath();
ctx.arc(scX, scY, 12, 0, Math.PI * 2);
ctx.fill();
}
// Bottom border piping
for (let i = 0; i <= 100; i++) {
let t = (i / 100) * Math.PI;
let scX = cx + rx * Math.cos(t);
let scY = cy + ry * Math.sin(t);
ctx.beginPath();
ctx.arc(scX, scY, 16, 0, Math.PI * 2);
ctx.fill();
}
// 7. Draw candles
if (numCandles > 0) {
for (let k = 0; k < numCandles; k++) {
let cRx = rx * 0.7; // Place along an inner arc
// Distribute evenly
let candleX = cx;
if (numCandles > 1) {
candleX = cx - cRx + (2 * cRx * k) / (numCandles - 1);
}
let candleY = ty - 15 + Math.sin(k * 7) * 15; // slight staggered Y effect
// Candle Base
ctx.fillStyle = (k % 2 === 0) ? "#FF4500" : "#00CED1"; // Alternating red & cyan
ctx.fillRect(candleX - 8, candleY - 60, 16, 60);
// Candle Stripes
ctx.strokeStyle = "#FFFFFF";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(candleX - 8, candleY - 45);
ctx.lineTo(candleX + 8, candleY - 55);
ctx.moveTo(candleX - 8, candleY - 25);
ctx.lineTo(candleX + 8, candleY - 35);
ctx.moveTo(candleX - 8, candleY - 5);
ctx.lineTo(candleX + 8, candleY - 15);
ctx.stroke();
// Wick
ctx.fillStyle = "#333333";
ctx.fillRect(candleX - 1.5, candleY - 70, 3, 10);
// Flame (outer)
ctx.fillStyle = "#FFD700";
ctx.beginPath();
ctx.moveTo(candleX, candleY - 90);
ctx.quadraticCurveTo(candleX + 12, candleY - 70, candleX, candleY - 70);
ctx.quadraticCurveTo(candleX - 12, candleY - 70, candleX, candleY - 90);
ctx.fill();
// Flame (inner)
ctx.fillStyle = "#FFA500";
ctx.beginPath();
ctx.moveTo(candleX, candleY - 83);
ctx.quadraticCurveTo(candleX + 6, candleY - 70, candleX, candleY - 70);
ctx.quadraticCurveTo(candleX - 6, candleY - 70, candleX, candleY - 83);
ctx.fill();
}
}
// 8. Draw "А как же торт?" Text
if (text) {
ctx.font = "bold 56px 'Trebuchet MS', 'Arial Black', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
let textY = cy + ry + 85;
// Shadow/Border
ctx.fillStyle = "#881144";
ctx.fillText(text, (width / 2) + 4, textY + 4);
// Fill
ctx.fillStyle = "#ff5588";
ctx.fillText(text, width / 2, textY);
}
return canvas;
}
Apply Changes