You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, centerX = 50, centerY = 50, radius = 40, strength = 30, twist = 45, glowRadius = 20, glowColor = "137, 207, 240", glowOpacity = 50) {
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Create a source canvas to read pixel data from the original image
const srcCanvas = document.createElement('canvas');
srcCanvas.width = width;
srcCanvas.height = height;
const srcCtx = srcCanvas.getContext('2d', { willReadFrequently: true });
srcCtx.drawImage(originalImg, 0, 0, width, height);
const srcData = srcCtx.getImageData(0, 0, width, height);
const srcPixels = srcData.data;
// Create the destination canvas to draw the result
const destCanvas = document.createElement('canvas');
destCanvas.width = width;
destCanvas.height = height;
const destCtx = destCanvas.getContext('2d');
const destData = destCtx.createImageData(width, height);
const destPixels = destData.data;
// Convert percentage-based parameters to absolute pixel values
const cx = width * (centerX / 100);
const cy = height * (centerY / 100);
const r = Math.min(width, height) * (radius / 100);
// Convert twist angle from degrees to radians
const twistAngle = twist * Math.PI / 180;
// Iterate over each pixel of the destination image
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const dx = x - cx;
const dy = y - cy;
const distance = Math.sqrt(dx * dx + dy * dy);
let srcX = x;
let srcY = y;
// Apply the effect only to pixels within the portal's radius
if (distance < r) {
// The effect is strongest at the center and fades towards the edge
const factor = Math.pow(1 - (distance / r), 2);
const angle = Math.atan2(dy, dx);
const newAngle = angle + factor * twistAngle;
const newDistance = distance - factor * strength;
// Calculate the source pixel's coordinates based on the distortion
srcX = cx + newDistance * Math.cos(newAngle);
srcY = cy + newDistance * Math.sin(newAngle);
}
// Clamp coordinates to stay within image bounds
const clampedX = Math.max(0, Math.min(width - 1, srcX));
const clampedY = Math.max(0, Math.min(height - 1, srcY));
// Use nearest-neighbor sampling for performance
const floorX = Math.floor(clampedX);
const floorY = Math.floor(clampedY);
const destIndex = (y * width + x) * 4;
const srcIndex = (floorY * width + floorX) * 4;
// Copy RGBA values from the calculated source pixel to the destination
destPixels[destIndex] = srcPixels[srcIndex];
destPixels[destIndex + 1] = srcPixels[srcIndex + 1];
destPixels[destIndex + 2] = srcPixels[srcIndex + 2];
destPixels[destIndex + 3] = srcPixels[srcIndex + 3];
}
}
// Put the manipulated pixel data onto the destination canvas
destCtx.putImageData(destData, 0, 0);
// Add an optional glow effect around the portal's edge
if (r > 0 && glowRadius > 0 && glowOpacity > 0) {
const opacity = Math.max(0, Math.min(1, glowOpacity / 100));
const color = `rgba(${glowColor}, ${opacity})`;
const transparentColor = `rgba(${glowColor}, 0)`;
const glowWidth = r * (glowRadius / 100);
if (glowWidth > 0) {
// Create a radial gradient for a soft glow ring
const gradient = destCtx.createRadialGradient(cx, cy, Math.max(0, r - glowWidth), cx, cy, r + glowWidth);
gradient.addColorStop(0, transparentColor);
gradient.addColorStop(0.5, color);
gradient.addColorStop(1, transparentColor);
// Use 'lighter' blend mode for a bright, additive glow
destCtx.globalCompositeOperation = 'lighter';
destCtx.beginPath();
destCtx.arc(cx, cy, r + glowWidth, 0, 2 * Math.PI);
destCtx.fillStyle = gradient;
destCtx.fill();
// Reset the blend mode to default
destCtx.globalCompositeOperation = 'source-over';
}
}
return destCanvas;
}
Apply Changes