You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, numBubbles = 25, dogEmojis = "🐶,🐕,🦮,🐩", bubbleColor = "rgba(200, 230, 255, 0.3)") {
// Create the output canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas dimensions to match the input image
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Draw the original image as the background
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Parse parameters
let count = parseInt(numBubbles, 10);
if (isNaN(count) || count <= 0) {
count = 25;
}
const dogs = dogEmojis.split(',').map(d => d.trim()).filter(d => Boolean(d));
if (dogs.length === 0) {
dogs.push("🐶");
}
// Function to draw a realistic soap bubble
function drawBubbleAndDog(x, y, radius, dogEmoji) {
// 1. Draw the dog inside the bubble (drawn first so it's "inside")
ctx.font = `${radius * 0.9}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowBlur = Math.max(2, radius * 0.1);
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = radius * 0.05;
ctx.fillText(dogEmoji, x, y + radius * 0.1); // slight offset for visual balance
// Reset shadow for the bubble
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 2. Bubble Fill
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
// Use a radial gradient to give a 3D spherical feel
const gradient = ctx.createRadialGradient(
x - radius * 0.3, y - radius * 0.3, radius * 0.1,
x, y, radius
);
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.6)');
gradient.addColorStop(0.4, 'rgba(255, 255, 255, 0.1)');
gradient.addColorStop(0.8, bubbleColor);
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.4)');
ctx.fillStyle = gradient;
ctx.fill();
// 3. Bubble Outline
ctx.lineWidth = Math.max(1, radius * 0.02);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
ctx.stroke();
// 4. Specular Highlight (top-left arc)
ctx.beginPath();
ctx.arc(x, y, radius * 0.75, Math.PI * 1.05, Math.PI * 1.45);
ctx.lineWidth = Math.max(2, radius * 0.08);
ctx.lineCap = 'round';
ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
ctx.stroke();
// 5. Secondary Reflection (bottom-right arc)
ctx.beginPath();
ctx.arc(x, y, radius * 0.85, Math.PI * 0.1, Math.PI * 0.4);
ctx.lineWidth = Math.max(1, radius * 0.03);
ctx.lineCap = 'round';
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.stroke();
}
// Generate random bubbles with dogs
for (let i = 0; i < count; i++) {
// Randomize bubble size (between 5% and 15% of the shortest side)
const minSize = Math.min(canvas.width, canvas.height);
const radius = Math.random() * (minSize * 0.1) + (minSize * 0.05);
// Randomize position (keep entirely within the canvas constraints optionally, but partial overlap is fine)
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
// Pick a random dog emoji
const dog = dogs[Math.floor(Math.random() * dogs.length)];
drawBubbleAndDog(x, y, radius, dog);
}
return canvas;
}
Apply Changes