You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, impactX = 50, impactY = 50, shatterIntensity = 65, numRays = 24, numRings = 7) {
const W = originalImg.naturalWidth || originalImg.width;
const H = originalImg.naturalHeight || originalImg.height;
const canvas = document.createElement('canvas');
canvas.width = W;
canvas.height = H;
const ctx = canvas.getContext('2d');
// 1. Draw "The Fourth Wall" Background (A digital/matrix void behind the image)
ctx.fillStyle = "#050505";
ctx.fillRect(0, 0, W, H);
const minDim = Math.min(W, H);
const gap = Math.max(12, Math.floor(minDim / 45));
ctx.font = `bold ${Math.floor(gap * 0.9)}px monospace`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$+-*/=%\"'#&_(),.;:?!\\|{}<>[]^~";
for (let x = gap / 2; x < W; x += gap) {
for (let y = gap / 2; y < H; y += gap) {
if (Math.random() > 0.65) {
const r = Math.random();
if (r > 0.92) ctx.fillStyle = "#00FF41"; // Bright Green
else if (r > 0.4) ctx.fillStyle = "#008F11"; // Mid Green
else ctx.fillStyle = "#003B00"; // Dark Green
ctx.fillText(chars[Math.floor(Math.random() * chars.length)], x, y);
}
}
}
// A subtle warning text hidden deep in the void
ctx.fillStyle = "rgba(255, 0, 0, 0.45)";
ctx.font = `bold ${Math.floor(minDim * 0.1)}px Courier New, monospace`;
ctx.fillText("SYSTEM BREACH", W / 2, H / 2);
// 2. Setup Shatter Math Parameters
const ix = W * (Math.max(0, Math.min(100, parseFloat(impactX))) / 100);
const iy = H * (Math.max(0, Math.min(100, parseFloat(impactY))) / 100);
const intensity = Math.max(0, Math.min(100, parseFloat(shatterIntensity))) / 100;
const N = Math.max(5, parseInt(numRays, 10));
const M = Math.max(2, parseInt(numRings, 10));
// Diagonal distance ensuring corners are fully covered
const max_dist = Math.hypot(Math.max(ix, W - ix), Math.max(iy, H - iy)) * 1.15;
// Generate web nodes
const pt = [];
for (let i = 0; i < N; i++) {
pt[i] = [];
let base_angle = (i / N) * 2 * Math.PI;
let ray_angle = base_angle + (Math.random() - 0.5) * (Math.PI / N * 0.9);
for (let j = 0; j <= M; j++) {
if (j === 0) {
pt[i][j] = { x: ix, y: iy };
} else {
let r_ratio = Math.pow(j / M, 1.8);
let r = max_dist * r_ratio;
// Radius and transverse random jitter
let r_jitter = r + (Math.random() - 0.5) * (max_dist / M * 0.6) * r_ratio;
let t_jitter = (Math.random() - 0.5) * (max_dist / M * 0.5) * r_ratio;
let px = ix + r_jitter * Math.cos(ray_angle) - t_jitter * Math.sin(ray_angle);
let py = iy + r_jitter * Math.sin(ray_angle) + t_jitter * Math.cos(ray_angle);
pt[i][j] = { x: px, y: py };
}
}
}
// Create polygons (shards)
const polygons = [];
for (let j = 0; j < M; j++) {
for (let i = 0; i < N; i++) {
let next_i = (i + 1) % N;
polygons.push({
ring: j,
points: [
pt[i][j],
pt[next_i][j],
pt[next_i][j + 1],
pt[i][j + 1]
]
});
}
}
// Sort to draw inner elements later (over the outer ones) if they pop out
polygons.sort((a, b) => b.ring - a.ring);
const scaleRef = Math.max(W, H);
// 3. Render Shattered Polygons
polygons.forEach(polyData => {
let poly = polyData.points;
let cx = 0, cy = 0;
poly.forEach(p => { cx += p.x; cy += p.y; });
cx /= poly.length;
cy /= poly.length;
let dx = cx - ix;
let dy = cy - iy;
let dist = Math.hypot(dx, dy);
let nd = Math.min(dist / max_dist, 1);
// Core removal: Pieces near the impact point fall completely into the void
let dropZone = 0.15 * intensity;
if (nd < dropZone && Math.random() < 0.65) return;
// Force dissipation mapping outwards
let force = Math.max(0, (1 - Math.pow(nd, 0.6))) * intensity;
// Stabilize outer edges strictly
if (nd > 0.6 && Math.random() > 0.2) force = 0;
let move_x = (dx / (dist || 1)) * force * (W * 0.12);
let move_y = (dy / (dist || 1)) * force * (H * 0.12);
let rot = (Math.random() - 0.5) * force * 1.5;
let scale = 1;
if (force > 0) {
if (Math.random() > 0.6) scale = 1 + (Math.random() * force * 0.25); // Z-Pop Out
else scale = 1 - (Math.random() * force * 0.15); // Z-Shrink In
}
ctx.save();
// 3a. Transform Context relative to the polygon center
ctx.translate(cx + move_x, cy + move_y);
ctx.rotate(rot);
ctx.scale(scale, scale);
ctx.translate(-cx, -cy);
// 3b. Define Shard Path based on native untransformed coordinates
ctx.beginPath();
ctx.moveTo(poly[0].x, poly[0].y);
for (let k = 1; k < poly.length; k++) {
ctx.lineTo(poly[k].x, poly[k].y);
}
ctx.closePath();
// 3c. Drop Shadow (outside the shard)
if (force > 0) {
ctx.shadowColor = "rgba(0,0,0,0.9)";
ctx.shadowBlur = 25 * force * (scaleRef / 1000);
ctx.shadowOffsetX = 10 * force * (scaleRef / 1000) * (dx >= 0 ? 1 : -1);
ctx.shadowOffsetY = 10 * force * (scaleRef / 1000) * (dy >= 0 ? 1 : -1);
ctx.fillStyle = "#000";
ctx.fill();
}
ctx.clip();
ctx.shadowColor = "transparent";
// 3d. Draw Original Image Chunk
ctx.drawImage(originalImg, 0, 0, W, H);
// 3e. Refraction Glare / Glass Edge Highlighting
ctx.strokeStyle = `rgba(255, 255, 255, ${0.15 + force * 0.3})`;
ctx.lineWidth = Math.max(1, 2 * force * (scaleRef / 1000));
ctx.stroke();
// Cross fracture simulation randomly on displaced glass pieces
if (force > 0 && Math.random() > 0.75) {
ctx.beginPath();
ctx.moveTo(poly[0].x, poly[0].y);
ctx.lineTo(poly[2].x, poly[2].y);
ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
ctx.lineWidth = 1;
ctx.stroke();
}
// Sheen
if (force > 0 && Math.random() > 0.4) {
ctx.fillStyle = "rgba(255, 255, 255, 0.12)";
ctx.fill();
}
ctx.restore();
});
// 4. Glitch Flare at impact origin (to signify the breach intensity)
if (intensity > 0) {
ctx.save();
ctx.translate(ix, iy);
let coreRadius = minDim * 0.08 * intensity;
let coreGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, coreRadius);
coreGrad.addColorStop(0, "rgba(255, 255, 255, 0.95)");
coreGrad.addColorStop(0.3, "rgba(0, 255, 65, 0.4)");
coreGrad.addColorStop(1, "rgba(0, 255, 65, 0)");
ctx.fillStyle = coreGrad;
ctx.beginPath();
ctx.arc(0, 0, coreRadius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
return canvas;
}
Apply Changes