You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, message = "На помощь!", assistantType = "robot", bubbleColor = "#ffeb3b") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Match dimensions of the original image
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
// Draw original picture
ctx.drawImage(originalImg, 0, 0, w, h);
// Calculate adaptive scaling so "assistant" looks proportionate
// Min scale of 0.8 to ensure text stays readable on smaller pictures
const scale = Math.max(0.8, Math.min(w, h) / 400);
const fontSize = Math.floor(22 * scale);
const padding = Math.floor(15 * scale);
const margin = Math.floor(20 * scale);
const lineHeight = fontSize * 1.4;
const maxTextWidth = Math.max(150 * scale, w * 0.45);
ctx.font = `bold ${fontSize}px "Comic Sans MS", "Segoe UI", Arial, sans-serif`;
// Figure out the preferred assistant mascot
let mascot = "🤖";
const typeLower = assistantType.toLowerCase();
if (typeLower.includes("doc")) mascot = "👨⚕️";
if (typeLower.includes("dog")) mascot = "🐶";
if (typeLower.includes("sos") || typeLower.includes("help")) mascot = "🛟";
if (typeLower.includes("clip")) mascot = "📎";
// Text Wrapping Logic
const words = message.split(' ');
let line = '';
const lines = [];
let longestLineWidth = 0;
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + ' ';
const metrics = ctx.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > maxTextWidth && n > 0) {
lines.push(line);
line = words[n] + ' ';
} else {
line = testLine;
}
}
lines.push(line);
// Verify sizes
lines.forEach(l => {
const lw = ctx.measureText(l).width;
if (lw > longestLineWidth) longestLineWidth = lw;
});
const textBlockWidth = longestLineWidth;
const textBlockHeight = lines.length * lineHeight;
// Calculate bubble dimensions
const bubbleW = textBlockWidth + padding * 2;
// Deduct slightly bounds since trailing space might exist on font baseline
const bubbleH = textBlockHeight + padding * 2 - (lineHeight - fontSize);
const emojiSize = Math.floor(55 * scale);
ctx.font = `${emojiSize}px sans-serif`;
const emojiWidth = ctx.measureText(mascot).width;
// Element positions
const emojiX = w - emojiWidth - margin;
const emojiY = h - margin;
let bubbleX = emojiX - bubbleW - margin / 2;
const bubbleY = Math.max(margin, h - margin - emojiSize / 2 - bubbleH / 2);
// Force bubble to stay within canvas bounds
if (bubbleX < margin) bubbleX = margin;
// Draw Speech Bubble Visuals
ctx.fillStyle = bubbleColor;
ctx.shadowColor = 'rgba(0,0,0,0.35)';
ctx.shadowBlur = 10 * scale;
ctx.shadowOffsetX = 3 * scale;
ctx.shadowOffsetY = 5 * scale;
const radius = 15 * scale;
ctx.beginPath();
ctx.moveTo(bubbleX + radius, bubbleY);
// Top Edge
ctx.lineTo(bubbleX + bubbleW - radius, bubbleY);
ctx.quadraticCurveTo(bubbleX + bubbleW, bubbleY, bubbleX + bubbleW, bubbleY + radius);
// Right Edge logic (Add tail if the bubble is left of the emoji)
if (bubbleX + bubbleW < emojiX) {
const tailY = bubbleY + Math.max(radius, bubbleH / 2 - 15 * scale);
ctx.lineTo(bubbleX + bubbleW, tailY);
// Triangle tail pointing to mascot
ctx.lineTo(emojiX + emojiWidth / 4, tailY + 15 * scale);
ctx.lineTo(bubbleX + bubbleW, tailY + 20 * scale);
}
ctx.lineTo(bubbleX + bubbleW, bubbleY + bubbleH - radius);
ctx.quadraticCurveTo(bubbleX + bubbleW, bubbleY + bubbleH, bubbleX + bubbleW - radius, bubbleY + bubbleH);
// Bottom Edge
ctx.lineTo(bubbleX + radius, bubbleY + bubbleH);
ctx.quadraticCurveTo(bubbleX, bubbleY + bubbleH, bubbleX, bubbleY + bubbleH - radius);
// Left Edge
ctx.lineTo(bubbleX, bubbleY + radius);
ctx.quadraticCurveTo(bubbleX, bubbleY, bubbleX + radius, bubbleY);
ctx.closePath();
// Render Bubble
ctx.fill();
ctx.shadowColor = 'transparent';
ctx.lineWidth = 2 * Math.max(1, scale);
ctx.strokeStyle = '#222222';
ctx.stroke();
// Draw Helper Message Text
ctx.fillStyle = '#111111';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `bold ${fontSize}px "Comic Sans MS", "Segoe UI", Arial, sans-serif`;
let textY = bubbleY + padding;
lines.forEach(l => {
ctx.fillText(l.trim(), bubbleX + padding, textY);
textY += lineHeight;
});
// Draw Assistant Mascot (Emoji)
ctx.font = `${emojiSize}px sans-serif`;
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
// Drop shadow for the emoji
ctx.shadowColor = 'rgba(0,0,0,0.5)';
ctx.shadowBlur = 8 * scale;
ctx.shadowOffsetX = 2 * scale;
ctx.shadowOffsetY = 4 * scale;
ctx.fillText(mascot, emojiX, emojiY);
return canvas;
}
Apply Changes