You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, text = "С Днем Рождения, Тучка!", themeColor = "#64b5f6", balloonsCount = 10) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// 1. Draw the base original image
ctx.drawImage(originalImg, 0, 0, width, height);
// Dynamic scale based on image dimensions to keep decorations proportional
const scale = Math.max(0.5, Math.min(width, height) / 800);
// 2. Draw a decorative thematic frame
ctx.lineWidth = 30 * scale;
ctx.strokeStyle = themeColor;
ctx.strokeRect(0, 0, width, height);
// Inner thin line for the frame
ctx.lineWidth = 5 * scale;
ctx.strokeStyle = '#ffffff';
ctx.strokeRect(15 * scale, 15 * scale, width - 30 * scale, height - 30 * scale);
// Defined party colors
const colors = ['#fce18a', '#ff726d', '#b48def', '#f4306d', '#42b983', '#ffffff', '#00b8ff'];
// 3. Draw Confetti (concentrated near top and bottom)
ctx.globalAlpha = 0.8;
for (let i = 0; i < 150; i++) {
let cx = Math.random() * width;
let cy = (Math.random() < 0.6)
? Math.random() * (height * 0.35)
: height - Math.random() * (height * 0.35);
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(Math.random() * Math.PI * 2);
let s = (Math.random() * 0.6 + 0.4) * scale;
ctx.scale(s, s);
ctx.fillStyle = colors[Math.floor(Math.random() * colors.length)];
if (Math.random() < 0.5) {
ctx.beginPath();
ctx.arc(0, 0, 8, 0, Math.PI * 2);
ctx.fill();
} else {
ctx.fillRect(-6, -10, 12, 20);
}
ctx.restore();
}
ctx.globalAlpha = 1.0;
// 4. Utility: Draw Cloud
function drawCloud(cx, cy, s) {
ctx.save();
ctx.translate(cx, cy);
ctx.scale(s, s);
ctx.shadowColor = 'rgba(0,0,0,0.15)';
ctx.shadowBlur = 15;
ctx.shadowOffsetY = 5;
ctx.fillStyle = '#ffffff';
ctx.beginPath();
// A standard cloud shape made of overlapping arcs
ctx.arc(0, 0, 20, Math.PI * 0.5, Math.PI * 1.5);
ctx.arc(25, -15, 25, Math.PI * 1, Math.PI * 1.8);
ctx.arc(55, -20, 30, Math.PI * 1.2, Math.PI * 1.9);
ctx.arc(85, -10, 20, Math.PI * 1.5, Math.PI * 2);
ctx.arc(100, 10, 15, Math.PI * 1.5, Math.PI * 0.5);
ctx.lineTo(0, 25);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// Add some cute white clouds floating on top part
drawCloud(width * 0.05, 80 * scale, scale * 1.5);
drawCloud(width * 0.65, 50 * scale, scale * 2.0);
drawCloud(width * 0.35, 25 * scale, scale * 1.2);
// 5. Utility: Draw Party Balloons
function drawBalloon(cx, cy, color, s) {
ctx.save();
ctx.translate(cx, cy);
ctx.scale(s, s);
// String
ctx.beginPath();
ctx.moveTo(0, 25);
ctx.bezierCurveTo(10, 50, -10, 80, 0, 120);
ctx.strokeStyle = 'rgba(255,255,255,0.8)';
ctx.lineWidth = 3;
ctx.stroke();
// Balloon body
ctx.beginPath();
ctx.ellipse(0, 0, 25, 35, 0, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
// Shiny Highlight
ctx.beginPath();
ctx.ellipse(-10, -12, 6, 12, Math.PI/8, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.4)';
ctx.fill();
// Bottom knot
ctx.beginPath();
ctx.moveTo(-6, 33);
ctx.lineTo(6, 33);
ctx.lineTo(0, 25);
ctx.fill();
ctx.restore();
}
// Distribute balloons randomly across the top third
const parsedBalloonCount = Number(balloonsCount) || 10;
for (let i = 0; i < parsedBalloonCount; i++) {
let cx = width * (0.05 + Math.random() * 0.9);
let cy = height * (0.05 + Math.random() * 0.25);
let color = colors[Math.floor(Math.random() * (colors.length - 1))]; // avoid white balloon
let s = scale * (0.8 + Math.random() * 0.7);
drawBalloon(cx, cy, color, s);
}
// 6. Utility: "Tuchka" - Little Cloud White Bear illustration
function drawTuchkaBear(cx, cy, s) {
ctx.save();
ctx.translate(cx, cy);
ctx.scale(s, s);
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.shadowBlur = 15;
ctx.shadowOffsetY = 8;
// Ears
ctx.fillStyle = '#fefefe';
ctx.beginPath(); ctx.arc(-35, -35, 18, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(35, -35, 18, 0, Math.PI*2); ctx.fill();
// Inner Ears
ctx.shadowColor = 'transparent';
ctx.fillStyle = '#e1e8f0';
ctx.beginPath(); ctx.arc(-35, -35, 9, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(35, -35, 9, 0, Math.PI*2); ctx.fill();
// Head Base
ctx.shadowColor = 'rgba(0,0,0,0.3)';
ctx.fillStyle = '#fefefe';
ctx.beginPath();
ctx.ellipse(0, 0, 55, 48, 0, 0, Math.PI*2);
ctx.fill();
// Muzzle
ctx.shadowColor = 'transparent';
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.ellipse(0, 14, 28, 20, 0, 0, Math.PI*2);
ctx.fill();
// Nose
ctx.fillStyle = '#1a1a1a';
ctx.beginPath();
ctx.ellipse(0, 6, 12, 8, 0, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.beginPath(); ctx.arc(-4, 4, 3, 0, Math.PI*2); ctx.fill(); // Nose shine
// Eyes
ctx.fillStyle = '#1a1a1a';
ctx.beginPath(); ctx.arc(-20, -10, 6, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(20, -10, 6, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.beginPath(); ctx.arc(-21, -11, 2.5, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(19, -11, 2.5, 0, Math.PI*2); ctx.fill();
// Blush
ctx.fillStyle = 'rgba(255, 100, 100, 0.3)';
ctx.beginPath(); ctx.ellipse(-35, 5, 8, 5, 0, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.ellipse(35, 5, 8, 5, 0, 0, Math.PI*2); ctx.fill();
// Cute Mouth
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.moveTo(0, 14);
ctx.lineTo(0, 22);
ctx.moveTo(0, 22);
ctx.arc(-8, 22, 8, 0, Math.PI, false);
ctx.moveTo(0, 22);
ctx.arc(8, 22, 8, Math.PI, 0, true);
ctx.stroke();
// Party Hat
ctx.fillStyle = '#ff4b4b';
ctx.beginPath();
ctx.moveTo(-22, -40);
ctx.lineTo(22, -40);
ctx.lineTo(0, -90);
ctx.closePath();
ctx.fill();
// Hat Pom-Pom & Details
ctx.fillStyle = '#fff';
ctx.beginPath(); ctx.arc(0, -90, 10, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(0, -50, 4, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(-10, -65, 4, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(10, -75, 4, 0, Math.PI*2); ctx.fill();
ctx.restore();
}
// Place "Tuchka" Bear at the bottom right
drawTuchkaBear(width - 150 * scale, height - 120 * scale, scale * 2.2);
// 7. Draw Birthday Message Text Banner
const fontSize = Math.max(26, Math.floor(width / (text.length * 0.5)));
ctx.font = `bold ${fontSize}px 'Comic Sans MS', 'Arial Rounded MT Bold', 'Trebuchet MS', sans-serif`;
const bannerWidth = ctx.measureText(text).width + 120 * scale;
const bannerHeight = fontSize * 2.2;
const bx = width / 2;
const by = height - bannerHeight * 1.5;
ctx.save();
ctx.shadowColor = 'rgba(0,0,0,0.4)';
ctx.shadowBlur = 12;
ctx.shadowOffsetY = 6;
ctx.fillStyle = themeColor;
// Draw pill-shaped banner behind text
ctx.beginPath();
const r = bannerHeight / 2;
ctx.moveTo(bx - bannerWidth / 2 + r, by - bannerHeight / 2);
ctx.lineTo(bx + bannerWidth / 2 - r, by - bannerHeight / 2);
ctx.arc(bx + bannerWidth / 2 - r, by, r, -Math.PI / 2, Math.PI / 2);
ctx.lineTo(bx - bannerWidth / 2 + r, by + bannerHeight / 2);
ctx.arc(bx - bannerWidth / 2 + r, by, r, Math.PI / 2, -Math.PI / 2);
ctx.fill();
ctx.restore();
// Render Text
ctx.save();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ffffff';
// Text Stroke to make it pop
ctx.strokeStyle = 'rgba(0,0,0,0.15)';
ctx.lineWidth = 5 * scale;
ctx.strokeText(text, bx, by);
ctx.fillText(text, bx, by);
ctx.restore();
return canvas;
}
Apply Changes