You can edit the below JavaScript code to customize the image tool.
function processImage(
originalImg,
centerXRatio = 0.5,
centerYRatio = 0.5,
frequency = 10,
phaseShift = 0, // Radians
strength = 0.5,
zoneColor1Str = "rgba(0,0,0,1)",
zoneColor2Str = "rgba(255,255,255,0)"
) {
const width = originalImg.width;
const height = originalImg.height;
const outputCanvas = document.createElement('canvas');
outputCanvas.width = width;
outputCanvas.height = height;
const ctx = outputCanvas.getContext('2d');
if (!ctx) { // Should not happen in a browser environment
console.error("Could not get 2D context");
return outputCanvas; // Return an empty canvas
}
if (width === 0 || height === 0) {
// For a 0-dimension image (e.g., not loaded or genuinely 0x0),
// draw nothing and return an empty canvas of that size.
return outputCanvas;
}
// Helper function to parse CSS color strings to an {r, g, b, a} object.
// Alpha 'a' will be in the range [0, 1].
function _parseColorToRGBA(colorStr) {
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 1;
tempCanvas.height = 1;
// {willReadFrequently: true} is an optimization hint for contexts heavily used with getImageData.
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
if (!tempCtx) return { r: 0, g: 0, b: 0, a: 0 }; // Fallback, though unlikely in browsers.
tempCtx.fillStyle = colorStr; // Assign "invalid-color" will make fillStyle default to black
tempCtx.fillRect(0, 0, 1, 1);
const imageData = tempCtx.getImageData(0, 0, 1, 1).data;
return {
r: imageData[0],
g: imageData[1],
b: imageData[2],
a: imageData[3] / 255.0
};
}
// Draw the original image onto the output canvas first
ctx.drawImage(originalImg, 0, 0, width, height);
// Create a temporary canvas for the zone plate pattern
const zonePlateCanvas = document.createElement('canvas');
zonePlateCanvas.width = width;
zonePlateCanvas.height = height;
const zoneCtx = zonePlateCanvas.getContext('2d', { willReadFrequently: true });
if (!zoneCtx) { // Should not happen
console.error("Could not get 2D context for zone plate canvas");
return outputCanvas; // Return canvas with original image only
}
const zoneImageData = zoneCtx.createImageData(width, height);
const data = zoneImageData.data;
const actualCenterX = width * centerXRatio;
const actualCenterY = height * centerYRatio;
// Calculate the maximum possible distance from the (actualCenterX, actualCenterY)
// to any of the four corners of the image. This normalizes the radial distance 'r'
// so that 'frequency' consistently refers to the number of full cycles
// from the center to the furthest point in the image bounds from that center.
const distSqToCorners = [
actualCenterX * actualCenterX + actualCenterY * actualCenterY, // dist to (0,0) squared
(width - actualCenterX) * (width - actualCenterX) + actualCenterY * actualCenterY, // dist to (width,0) squared
actualCenterX * actualCenterX + (height - actualCenterY) * (height - actualCenterY), // dist to (0,height) squared
(width - actualCenterX) * (width - actualCenterX) + (height - actualCenterY) * (height - actualCenterY) // dist to (width,height) squared
];
const maxDistSq = Math.max(...distSqToCorners);
const maxPossibleRadius = Math.sqrt(maxDistSq);
const parsedColor1 = _parseColorToRGBA(zoneColor1Str);
const parsedColor2 = _parseColorToRGBA(zoneColor2Str);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const dx = x - actualCenterX;
const dy = y - actualCenterY;
const r = Math.sqrt(dx * dx + dy * dy);
let waveArg;
// maxPossibleRadius will be 0 only if width and height are 0, which is handled by the initial check.
// If it were possible for maxPossibleRadius to be 0 here, we'd need to handle division by zero.
// However, for any image with width > 0 or height > 0, maxPossibleRadius > 0.
waveArg = (r / maxPossibleRadius) * frequency * 2 * Math.PI + phaseShift;
const zoneValue = Math.cos(waveArg); // Ranges from -1 to 1
const idx = (y * width + x) * 4; // Index for the pixel's R channel
let R, G, B, A;
if (zoneValue > 0) { // One part of the cycle
R = parsedColor1.r;
G = parsedColor1.g;
B = parsedColor1.b;
A = parsedColor1.a;
} else { // The other part of the cycle
R = parsedColor2.r;
G = parsedColor2.g;
B = parsedColor2.b;
A = parsedColor2.a;
}
data[idx] = R;
data[idx + 1] = G;
data[idx + 2] = B;
// Apply overall strength to the alpha of the selected zone color
// Ensure alpha is clamped between 0 and 255
data[idx + 3] = Math.max(0, Math.min(255, Math.round(A * strength * 255)));
}
}
zoneCtx.putImageData(zoneImageData, 0, 0);
// Blend the generated zone plate pattern onto the original image
// Default globalCompositeOperation 'source-over' will draw zonePlateCanvas on top.
// The alpha of the zone plate pixels (controlled by parsedColor.a and strength)
// will determine how it blends with the underlying original image.
ctx.drawImage(zonePlateCanvas, 0, 0);
return outputCanvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Zone Plate Lens Effect Creator allows users to apply a creative zone plate effect to images. By adjusting parameters like center position, frequency, phase shift, and color choices, users can generate artistic variations of their images. This tool can be particularly useful for graphic designers, photographers, and artists looking to add unique visual effects to their work, create abstract designs, or enhance images for presentations and social media sharing.