You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, centerXStr = "0.5", centerYStr = "0.5", angleStr = "1.0", radiusStr = "0.5") {
let centerXPercent = parseFloat(centerXStr);
if (isNaN(centerXPercent)) centerXPercent = 0.5;
let centerYPercent = parseFloat(centerYStr);
if (isNaN(centerYPercent)) centerYPercent = 0.5;
let swirlAngle = parseFloat(angleStr); // In radians
if (isNaN(swirlAngle)) swirlAngle = 1.0;
let swirlRadiusPercent = parseFloat(radiusStr);
if (isNaN(swirlRadiusPercent)) swirlRadiusPercent = 0.5;
// Ensure radius percent is not negative.
if (swirlRadiusPercent < 0) swirlRadiusPercent = 0;
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Create a temporary canvas to draw the original image and access its pixel data.
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imgWidth;
tempCanvas.height = imgHeight;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
let srcImageData;
try {
srcImageData = tempCtx.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
// This can happen if the image is tainted (e.g., cross-origin issues)
console.error("Error getting image data for swirl: ", e);
// Fallback: return the original image drawn on the main canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
return canvas;
}
const srcData = srcImageData.data;
const dstImageData = ctx.createImageData(imgWidth, imgHeight);
const dstData = dstImageData.data;
const actualCenterX = imgWidth * centerXPercent;
const actualCenterY = imgHeight * centerYPercent;
// Base the swirl radius on the smaller dimension of the image.
const baseRadiusDimension = Math.min(imgWidth, imgHeight);
const actualSwirlRadius = baseRadiusDimension * swirlRadiusPercent;
// If radius is zero or negative, or angle is zero, no swirl effect is applied.
if (actualSwirlRadius <= 0 || swirlAngle === 0) {
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
return canvas;
}
for (let y = 0; y < imgHeight; y++) {
for (let x = 0; x < imgWidth; x++) {
const dx = x - actualCenterX; // Distance from center X
const dy = y - actualCenterY; // Distance from center Y
const distance = Math.sqrt(dx * dx + dy * dy); // Overall distance from center
let sourceX, sourceY;
if (distance < actualSwirlRadius) {
const originalAngle = Math.atan2(dy, dx); // Angle of the pixel relative to the center
// The swirl factor decreases linearly from 1 (at the center) to 0 (at the radius edge).
// This means the swirl is strongest at the center and fades out.
const swirlFactor = 1 - (distance / actualSwirlRadius);
// For a different falloff, one could use: e.g. Math.pow(swirlFactor, 2) for sharper falloff.
const rotation = swirlAngle * swirlFactor; // Amount of rotation for this pixel
const newAngle = originalAngle + rotation; // New angle after applying swirl
// Convert polar coordinates (newAngle, distance) back to Cartesian for source pixel
sourceX = actualCenterX + distance * Math.cos(newAngle);
sourceY = actualCenterY + distance * Math.sin(newAngle);
} else {
// Pixels outside the swirl radius are not affected
sourceX = x;
sourceY = y;
}
const dstIndex = (y * imgWidth + x) * 4; // Index for the destination pixel array
let r, g, b, a;
// Use bilinear interpolation for smoother results when mapping source pixels.
// Check if the source pixel is within bounds where 4-point interpolation is possible.
// (i.e., not too close to the right or bottom edges).
if (sourceX >= 0 && sourceX < imgWidth - 1 && sourceY >= 0 && sourceY < imgHeight - 1) {
const x_floor = Math.floor(sourceX);
const y_floor = Math.floor(sourceY);
const tx = sourceX - x_floor; // Fractional part of X
const ty = sourceY - y_floor; // Fractional part of Y
// Indices of the 4 neighboring pixels in the source data array
const idx00 = (y_floor * imgWidth + x_floor) * 4;
const idx10 = (y_floor * imgWidth + (x_floor + 1)) * 4;
const idx01 = ((y_floor + 1) * imgWidth + x_floor) * 4;
const idx11 = ((y_floor + 1) * imgWidth + (x_floor + 1)) * 4;
// Interpolate R, G, B, A channels
r = (srcData[idx00+0]*(1-tx) + srcData[idx10+0]*tx)*(1-ty) + (srcData[idx01+0]*(1-tx) + srcData[idx11+0]*tx)*ty;
g = (srcData[idx00+1]*(1-tx) + srcData[idx10+1]*tx)*(1-ty) + (srcData[idx01+1]*(1-tx) + srcData[idx11+1]*tx)*ty;
b = (srcData[idx00+2]*(1-tx) + srcData[idx10+2]*tx)*(1-ty) + (srcData[idx01+2]*(1-tx) + srcData[idx11+2]*tx)*ty;
a = (srcData[idx00+3]*(1-tx) + srcData[idx10+3]*tx)*(1-ty) + (srcData[idx01+3]*(1-tx) + srcData[idx11+3]*tx)*ty;
} else {
// For pixels near edges or outside, use nearest neighbor with clamping.
const clampedSX = Math.max(0, Math.min(imgWidth - 1, Math.floor(sourceX)));
const clampedSY = Math.max(0, Math.min(imgHeight - 1, Math.floor(sourceY)));
const srcIndex = (clampedSY * imgWidth + clampedSX) * 4;
r = srcData[srcIndex];
g = srcData[srcIndex+1];
b = srcData[srcIndex+2];
a = srcData[srcIndex+3];
}
// Assign computed color to destination pixel.
// ImageData.data is a Uint8ClampedArray, so values are automatically clamped to 0-255 and rounded.
dstData[dstIndex] = r;
dstData[dstIndex + 1] = g;
dstData[dstIndex + 2] = b;
dstData[dstIndex + 3] = a;
}
}
// Draw the manipulated pixel data onto the main canvas.
ctx.putImageData(dstImageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Swirl Filter Application allows users to apply a swirling effect to images. By specifying a center point, swirl angle, and radius, users can create visually dynamic images that distort and rotate parts of the image in a spiral manner. This tool is ideal for artists and designers looking to create unique visual effects for graphics, social media posts, or digital art. It can also be used in creative projects where a sense of motion or abstraction is desired, making images more engaging and artistic.