You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg) {
const canvas = document.createElement('canvas');
const width = 800;
const height = 600;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 1. Draw Background: Underwater scene
const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
bgGradient.addColorStop(0, '#5D9CEC'); // Lighter blue
bgGradient.addColorStop(1, '#004E92'); // Darker blue
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, width, height);
// Light rays from the surface
const lightGradient = ctx.createRadialGradient(width / 2, -100, 50, width / 2, 0, 400);
lightGradient.addColorStop(0, 'rgba(255, 255, 255, 0.25)');
lightGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.fillStyle = lightGradient;
ctx.fillRect(0, 0, width, height);
// Sandy bottom
ctx.fillStyle = '#F2D492';
ctx.beginPath();
ctx.moveTo(0, height);
ctx.lineTo(0, height - 80);
for (let i = 0; i < width; i += 40) {
ctx.quadraticCurveTo(i + 20, height - 80 + Math.random() * 20 - 10, i + 40, height - 80 + Math.random() * 10 - 5);
}
ctx.lineTo(width, height);
ctx.closePath();
ctx.fill();
// 2. Draw Seaweed
function drawSeaweed(x, h, sway) {
ctx.beginPath();
ctx.moveTo(x, height);
let controlX = x;
for (let y = height; y > height - h; y -= 10) {
controlX = x + Math.sin(y / 40) * sway;
ctx.lineTo(controlX, y);
}
ctx.lineWidth = 10;
ctx.strokeStyle = '#5B8C5A';
ctx.lineCap = 'round';
ctx.stroke();
}
drawSeaweed(width * 0.1, 150, 15);
drawSeaweed(width * 0.15, 100, 10);
drawSeaweed(width * 0.85, 180, 20);
// 3. Draw a broken net
ctx.strokeStyle = 'rgba(200, 200, 200, 0.5)';
ctx.lineWidth = 3;
const netSpacing = 60;
const netCenter = { x: width / 2, y: height / 2 + 50 };
const netSize = 250;
for (let i = -netSize; i <= netSize; i += netSpacing) {
// Vertical lines (some are broken)
if (Math.random() > 0.25) {
ctx.beginPath();
ctx.moveTo(netCenter.x + i, netCenter.y - netSize * (Math.random() * 0.5 + 0.5));
ctx.quadraticCurveTo(
netCenter.x + i + (Math.random() - 0.5) * 30, netCenter.y,
netCenter.x + i + (Math.random() - 0.5) * 30, netCenter.y + netSize * (Math.random() * 0.5 + 0.5)
);
ctx.stroke();
}
// Horizontal lines
if (Math.random() > 0.25) {
ctx.beginPath();
ctx.moveTo(netCenter.x - netSize * (Math.random() * 0.5 + 0.5), netCenter.y + i);
ctx.quadraticCurveTo(
netCenter.x, netCenter.y + i + (Math.random() - 0.5) * 30,
netCenter.x + netSize * (Math.random() * 0.5 + 0.5), netCenter.y + i
);
ctx.stroke();
}
}
// 4. Draw the Octopus
const octopusX = width / 2;
const octopusY = height / 2 + 50;
const octopusColor = '#FF6F61';
const octopusShadowColor = '#D95B5B';
// Tentacles (drawn first, to be behind the head)
ctx.lineCap = 'round';
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2 + Math.PI / 16;
const length = 100 + Math.random() * 50;
const curveFactor = (Math.random() - 0.5) * 1.8;
const startX = octopusX + Math.cos(angle) * 30;
const startY = octopusY - 30 + Math.sin(angle) * 20;
const endX = startX + Math.cos(angle + curveFactor / 2) * length;
const endY = startY + Math.sin(angle + curveFactor / 2) * length;
const cpX = startX + Math.cos(angle + curveFactor) * length * 0.5;
const cpY = startY + Math.sin(angle + curveFactor) * length * 0.5;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.quadraticCurveTo(cpX, cpY, endX, endY);
// Tentacle arm
ctx.strokeStyle = octopusShadowColor;
ctx.lineWidth = 16;
ctx.setLineDash([]);
ctx.stroke();
// Suckers underneath (on some tentacles for perspective)
if (i % 2 !== 0) {
ctx.strokeStyle = 'rgba(255, 229, 220, 0.8)';
ctx.lineWidth = 12;
ctx.setLineDash([1, 15]); // 1px dot, 15px gap
ctx.stroke();
}
}
ctx.setLineDash([]);
// Head (Mantle)
ctx.fillStyle = octopusColor;
ctx.strokeStyle = octopusShadowColor;
ctx.lineWidth = 4;
ctx.beginPath();
ctx.ellipse(octopusX, octopusY - 45, 55, 65, 0, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Eyes
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(octopusX - 20, octopusY - 55, 12, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(octopusX + 20, octopusY - 55, 12, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(octopusX - 18, octopusY - 52, 6, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(octopusX + 22, octopusY - 52, 6, 0, Math.PI * 2);
ctx.fill();
// Sad eyebrows
ctx.strokeStyle = 'black';
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.moveTo(octopusX - 30, octopusY - 72);
ctx.lineTo(octopusX - 17, octopusY - 67);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(octopusX + 30, octopusY - 72);
ctx.lineTo(octopusX + 17, octopusY - 67);
ctx.stroke();
// 5. Draw Rescuer's Hands (stylized)
function drawHand(x, y, scale = 1) {
ctx.fillStyle = '#E0AC69'; // Skin tone
ctx.strokeStyle = '#B58453'; // Shadow
ctx.lineWidth = 2;
ctx.lineJoin = 'round';
ctx.beginPath(); // Palm
ctx.moveTo(x, y);
ctx.bezierCurveTo(x + 30 * scale, y - 60, x + 90 * scale, y - 50, x + 100 * scale, y);
ctx.lineTo(x + 80 * scale, y + 50);
ctx.lineTo(x, y + 40);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath(); // Thumb
ctx.moveTo(x + 20 * scale, y + 35);
ctx.quadraticCurveTo(x - 20 * scale, y + 60, x - 10 * scale, y + 80);
ctx.stroke();
}
drawHand(octopusX - 150, octopusY + 30, 1); // Left hand
drawHand(octopusX + 150, octopusY, -1); // Right hand
// 6. Draw Bubbles rising
for (let i = 0; i < 40; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const r = Math.random() * 8 + 2;
ctx.fillStyle = `rgba(215, 235, 255, ${Math.random() * 0.3 + 0.1})`;
ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.3 + 0.2})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
return canvas;
}
Apply Changes