You can edit the below JavaScript code to customize the image tool.
/**
* Applies a radial pinch or bulge distortion to an image.
* This function simulates a lens effect, magnifying (bulge) or minifying (pinch)
* the area within a specified circle.
*
* @param {Image} originalImg The original Image object to process.
* @param {number} [strength=0.5] The strength of the distortion. Positive values (0 to 1) create a bulge,
* while negative values (0 to -1) create a pinch. 0 means no effect. Values are clamped to [-1, 1].
* @param {number} [radius=-1] The radius of the circular area of effect in pixels.
* If a non-positive value is provided, a default radius is calculated to cover the entire image from the center point.
* @param {number} [centerX=0.5] The horizontal center of the effect, as a ratio of the image's width (0.0 to 1.0).
* @param {number} [centerY=0.5] The vertical center of the effect, as a ratio of the image's height (0.0 to 1.0).
* @returns {HTMLCanvasElement} A new canvas element with the distorted image.
*/
function processImage(originalImg, strength = 0.5, radius = -1, centerX = 0.5, centerY = 0.5) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize repeated calls to getImageData on some browsers
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
ctx.drawImage(originalImg, 0, 0, width, height);
// If image is not loaded or has no dimensions, return the canvas as is
if (width === 0 || height === 0) {
return canvas;
}
const srcDataObj = ctx.getImageData(0, 0, width, height);
const srcData = srcDataObj.data;
const dstDataObj = ctx.createImageData(width, height);
const dstData = dstDataObj.data;
const cx = width * centerX;
const cy = height * centerY;
let effectRadius = radius;
// If radius is not provided or non-positive, calculate a default
if (effectRadius <= 0) {
// Default radius is the distance from the center to the furthest corner
const dx_to_edge = Math.max(cx, width - cx);
const dy_to_edge = Math.max(cy, height - cy);
effectRadius = Math.sqrt(dx_to_edge ** 2 + dy_to_edge ** 2);
}
// A radius of 0 means no area is affected, so we can return early
if (effectRadius === 0) {
return canvas;
}
// Clamp strength to the range [-1, 1] for predictable behavior
const s = Math.max(-1, Math.min(1, strength));
// Bail out if strength is 0, as there's no effect to apply
if (s === 0) {
return canvas;
}
const PI_2 = Math.PI / 2;
// A helper function to sample a color value from the source data using bilinear interpolation.
// This produces smoother results than nearest-neighbor sampling.
const getInterpolatedValue = (data, w, h, x, y, channel) => {
const x_low = Math.floor(x);
const y_low = Math.floor(y);
const x_high = x_low + 1;
const y_high = y_low + 1;
const x_weight = x - x_low;
const y_weight = y - y_low;
// A nested helper to get a single pixel component, clamping coordinates to be within bounds.
const getClamped = (x_coord, y_coord) => {
const clampedX = Math.max(0, Math.min(w - 1, x_coord));
const clampedY = Math.max(0, Math.min(h - 1, y_coord));
return data[((clampedY * w) + clampedX) * 4 + channel];
};
const tl = getClamped(x_low, y_low); // top-left
const tr = getClamped(x_high, y_low); // top-right
const bl = getClamped(x_low, y_high); // bottom-left
const br = getClamped(x_high, y_high); // bottom-right
const top = tl * (1 - x_weight) + tr * x_weight;
const bottom = bl * (1 - x_weight) + br * x_weight;
return top * (1 - y_weight) + bottom * y_weight;
};
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;
// Only apply the effect within the radius
if (distance < effectRadius) {
const angle = Math.atan2(dy, dx);
let newDist;
const rd = distance; // current radius in destination
if (s > 0) { // Bulge effect
// Map destination radius to source radius using an arcsin curve for a fisheye effect
const rs_bulge = effectRadius * Math.asin(rd / effectRadius) / PI_2;
// Interpolate between no effect (rd) and full effect (rs_bulge)
newDist = rd * (1 - s) + rs_bulge * s;
} else { // Pinch effect (s < 0)
// Map destination radius to source radius using a sin curve
const rs_pinch = effectRadius * Math.sin(rd * PI_2 / effectRadius);
const strength_pinch = -s; // Make strength positive for interpolation [0, 1]
// Interpolate between no effect (rd) and full effect (rs_pinch)
newDist = rd * (1 - strength_pinch) + rs_pinch * strength_pinch;
}
// Convert polar coordinates back to cartesian for the source pixel
srcX = cx + newDist * Math.cos(angle);
srcY = cy + newDist * Math.sin(angle);
}
const dstIdx = (y * width + x) * 4;
// Get the interpolated color from the source and apply it to the destination
dstData[dstIdx] = getInterpolatedValue(srcData, width, height, srcX, srcY, 0); // R
dstData[dstIdx + 1] = getInterpolatedValue(srcData, width, height, srcX, srcY, 1); // G
dstData[dstIdx + 2] = getInterpolatedValue(srcData, width, height, srcX, srcY, 2); // B
dstData[dstIdx + 3] = getInterpolatedValue(srcData, width, height, srcX, srcY, 3); // A
}
}
ctx.putImageData(dstDataObj, 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 Rashed Pinch and Bulge Strength Analyzer is a tool that applies radial pinch or bulge distortions to images, simulating a lens effect. Users can control the strength and radius of the distortion, allowing for creative manipulation of the image’s appearance. This tool can be used for artistic photography, graphic design, and to create unique visual effects for images, making it beneficial for artists, designers, and content creators looking to enhance their visual projects.