You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, effectMode = "funhouse", intensity = 50) {
const canvas = document.createElement('canvas');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw original to get source pixels
ctx.drawImage(originalImg, 0, 0);
const srcData = ctx.getImageData(0, 0, width, height);
const dstData = ctx.createImageData(width, height);
const cx = width / 2;
const cy = height / 2;
// Base radius for circular displacements (bulge, pinch, swirl)
const R = Math.min(width, height) / 2;
// Normalize intensity (0 to 1 scale)
const normIntensity = Math.max(0, Math.min(100, Number(intensity))) / 100;
// Helper for bilinear interpolation to keep displaced pixels smooth
const fetchInterpolated = (srcX, srcY, dstArr, dstIdx) => {
// Strict boundary clamping
srcX = Math.max(0, Math.min(width - 1.001, srcX));
srcY = Math.max(0, Math.min(height - 1.001, srcY));
const x1 = Math.floor(srcX);
const y1 = Math.floor(srcY);
const x2 = x1 + 1;
const y2 = y1 + 1;
const weightX = srcX - x1;
const weightY = srcY - y1;
const idx1 = (y1 * width + x1) * 4;
const idx2 = (y1 * width + x2) * 4;
const idx3 = (y2 * width + x1) * 4;
const idx4 = (y2 * width + x2) * 4;
for (let i = 0; i < 4; i++) {
const p1 = srcData.data[idx1 + i];
const p2 = srcData.data[idx2 + i];
const p3 = srcData.data[idx3 + i];
const p4 = srcData.data[idx4 + i];
const top = p1 + (p2 - p1) * weightX;
const bottom = p3 + (p4 - p3) * weightX;
dstArr[dstIdx + i] = top + (bottom - top) * weightY;
}
};
let u, v, dx, dy, r, theta;
const effect = effectMode.toLowerCase();
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
u = x;
v = y;
dx = x - cx;
dy = y - cy;
r = Math.sqrt(dx * dx + dy * dy);
theta = Math.atan2(dy, dx);
switch (effect) {
case 'bulge':
if (r < R) {
// Exponent < 1 causes outward bulge
let amount = 1.0 - (normIntensity * 0.7);
let r_new = Math.pow(r / R, amount) * R;
u = cx + r_new * Math.cos(theta);
v = cy + r_new * Math.sin(theta);
}
break;
case 'pinch':
if (r < R) {
// Exponent > 1 causes inward pinch
let amount = 1.0 + (normIntensity * 1.5);
let r_new = Math.pow(r / R, amount) * R;
u = cx + r_new * Math.cos(theta);
v = cy + r_new * Math.sin(theta);
}
break;
case 'swirl':
if (r < R) {
// Angle increases towards center
let angle = normIntensity * Math.PI * 2 * ((R - r) / R);
u = cx + r * Math.cos(theta + angle);
v = cy + r * Math.sin(theta + angle);
}
break;
case 'wave':
let freq = 10 + (20 * (1 - normIntensity));
let amp = normIntensity * 40;
u = x + Math.sin(y / freq) * amp;
v = y + Math.cos(x / freq) * amp;
break;
case 'alien':
// Bulge on the top half (big brain), pinch on the bottom half (tiny chin)
if (r < R) {
let verticalWeight = (dy + R) / (2 * R); // Approx 0 at top, 1 at bottom
verticalWeight = Math.max(0, Math.min(1, verticalWeight));
let bulgeAmount = 1.0 - (normIntensity * 0.7);
let pinchAmount = 1.0 + (normIntensity * 1.5);
let amount = (bulgeAmount * (1 - verticalWeight)) + (pinchAmount * verticalWeight);
let r_new = Math.pow(r / R, amount) * R;
u = cx + r_new * Math.cos(theta);
v = cy + r_new * Math.sin(theta);
}
break;
case 'funhouse':
default:
// Mix of localized spherical bulge + sine waves (carnival mirror)
let fh_freq = Math.min(width, height) / 5;
let fh_amp = normIntensity * (Math.min(width, height) / 10);
u = x + Math.sin(y / fh_freq) * fh_amp;
let dx2 = u - cx;
let dy2 = v - cy;
let r2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
if (r2 < R * 0.8) {
let theta2 = Math.atan2(dy2, dx2);
let amount = 1.0 - (normIntensity * 0.6); // Bulge
let inner_R = R * 0.8;
let r_new = Math.pow(r2 / inner_R, amount) * inner_R;
u = cx + r_new * Math.cos(theta2);
v = cy + r_new * Math.sin(theta2);
}
break;
}
const dstIdx = (y * width + x) * 4;
fetchInterpolated(u, v, dstData.data, dstIdx);
}
}
ctx.putImageData(dstData, 0, 0);
return canvas;
}
Apply Changes