You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, intensity = "50", posX = "0.5", posY = "0.5") {
// Parse and clamp parameters
const valIntensity = Math.max(1, Math.min(200, parseFloat(intensity) || 50));
const valPosX = Math.max(0, Math.min(1, parseFloat(posX) || 0.5));
const valPosY = Math.max(0, Math.min(1, parseFloat(posY) || 0.5));
// Setup main canvas
const canvas = document.createElement('canvas');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Explosion center
const cenX = width * valPosX;
const cenY = height * valPosY;
const maxDim = Math.max(width, height);
// 1. Draw base image
ctx.drawImage(originalImg, 0, 0);
// 2. Apocalyptic Vignette (Darkens edges, adds a gritty smoky look)
ctx.globalCompositeOperation = 'multiply';
const vigGrad = ctx.createRadialGradient(cenX, cenY, 0, cenX, cenY, maxDim * 0.8);
vigGrad.addColorStop(0, 'rgba(255, 220, 200, 0.1)'); // Minimal darken in center
vigGrad.addColorStop(0.3, 'rgba(120, 40, 10, 0.6)'); // Smoky orange/brown mid
vigGrad.addColorStop(0.7, 'rgba(30, 5, 0, 0.9)'); // Deep dark red ruins
vigGrad.addColorStop(1, 'rgba(0, 0, 0, 1)'); // Pitch black edge
ctx.fillStyle = vigGrad;
ctx.fillRect(0, 0, width, height);
// 3. Prepare fiery off-screen version for explosive radial motion blur
const offCanvas = document.createElement('canvas');
offCanvas.width = width;
offCanvas.height = height;
const offCtx = offCanvas.getContext('2d');
// Draw image and tint it heavily red/orange
offCtx.drawImage(originalImg, 0, 0);
offCtx.globalCompositeOperation = 'multiply';
offCtx.fillStyle = '#ff4400';
offCtx.fillRect(0, 0, width, height);
// 4. Zoom blur (Explosive Shockwave) using the fiery off-screen image
const maxScale = 1 + (valIntensity * 0.01);
const steps = Math.max(5, Math.floor(valIntensity / 2));
ctx.globalCompositeOperation = 'screen';
ctx.globalAlpha = Math.max(0.01, 1.0 / steps); // Accumulate brightness smoothly
for (let i = 1; i <= steps; i++) {
const s = 1 + (maxScale - 1) * (i / steps);
ctx.save();
ctx.translate(cenX, cenY);
ctx.scale(s, s);
ctx.translate(-cenX, -cenY);
ctx.drawImage(offCanvas, 0, 0);
ctx.restore();
}
// 5. Intense blast core
ctx.globalCompositeOperation = 'color-dodge';
ctx.globalAlpha = 1.0;
const coreGrad = ctx.createRadialGradient(cenX, cenY, 0, cenX, cenY, maxDim * (valIntensity / 150));
coreGrad.addColorStop(0, 'rgba(255, 255, 255, 1)');
coreGrad.addColorStop(0.1, 'rgba(255, 200, 50, 0.9)');
coreGrad.addColorStop(0.4, 'rgba(255, 50, 0, 0.5)');
coreGrad.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = coreGrad;
ctx.fillRect(0, 0, width, height);
// 6. Expanding shockwave ring
ctx.globalCompositeOperation = 'screen';
const ringRadius = maxDim * (valIntensity / 200);
if (ringRadius > 5) {
const innerR = Math.max(1, ringRadius * 0.7);
const outerR = ringRadius * 1.3;
const ringGrad = ctx.createRadialGradient(cenX, cenY, innerR, cenX, cenY, outerR);
ringGrad.addColorStop(0, 'rgba(0,0,0,0)');
ringGrad.addColorStop(0.5, 'rgba(255, 180, 50, 0.4)');
ringGrad.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = ringGrad;
ctx.beginPath();
ctx.arc(cenX, cenY, outerR, 0, Math.PI * 2);
ctx.fill();
}
// 7. Flying glowing embers
const numEmbers = Math.floor(valIntensity * 4);
for (let i = 0; i < numEmbers; i++) {
const angle = Math.random() * Math.PI * 2;
// Exponential distribution to cluster particles near the center
const dist = (Math.random() ** 1.5) * maxDim * 0.6;
const x = cenX + Math.cos(angle) * dist;
const y = cenY + Math.sin(angle) * dist;
const size = (Math.random() * (maxDim * 0.003) + 1);
const stretch = Math.random() * 3 + 1.5; // Motion stretch in the direction of the blast
ctx.beginPath();
// ctx.ellipse applies a directional stretch to the particle based on its trajectory angle
ctx.ellipse(x, y, size * stretch, size, angle, 0, Math.PI * 2);
// Heat color logic: center is hot (white/yellow), edges are cooler (orange/red)
const heat = Math.max(0, 1 - (dist / (maxDim * 0.5)));
const r = 255;
const g = Math.floor(Math.max(50, heat * 255));
const b = Math.floor(Math.max(0, heat * 200 - 50));
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${Math.random() * 0.5 + 0.5})`;
ctx.fill();
}
// 8. Cinematic Film Grain / Noise Overlay (Apocalyptic Grit)
const noiseCanvas = document.createElement('canvas');
noiseCanvas.width = 64;
noiseCanvas.height = 64;
const nCtx = noiseCanvas.getContext('2d');
for (let x = 0; x < 64; x += 2) {
for (let y = 0; y < 64; y += 2) {
nCtx.fillStyle = Math.random() > 0.5 ? 'rgba(0,0,0,0.15)' : 'rgba(255,255,255,0.1)';
nCtx.fillRect(x, y, 2, 2);
}
}
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = ctx.createPattern(noiseCanvas, 'repeat');
ctx.fillRect(0, 0, width, height);
return canvas;
}
Apply Changes