You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, threshold = 30, lineColorStr = "173,216,230", bgColorStr = "0,0,0") {
const canvas = document.createElement('canvas');
// Use { willReadFrequently: true } for potential performance gains when using getImageData repeatedly,
// though for a single pass filter its impact might be minimal.
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Use naturalWidth/Height for HTMLImageElement to get original dimensions
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// If the image has no dimensions, return an empty canvas.
if (imgWidth === 0 || imgHeight === 0) {
return canvas;
}
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
const imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
const data = imageData.data; // This is a Uint8ClampedArray
// Create a copy of the pixel data to read from. This is crucial because
// the convolution operation needs original pixel values from the neighborhood,
// and we should not read from the data array that we are simultaneously modifying.
const srcData = new Uint8ClampedArray(data);
const width = imgWidth;
const height = imgHeight;
// Parse color strings into [R, G, B] arrays
let parsedLineColor = lineColorStr.split(',').map(s => parseInt(s.trim(), 10));
let parsedBgColor = bgColorStr.split(',').map(s => parseInt(s.trim(), 10));
// Validate and set default colors if parsing fails
if (parsedLineColor.length !== 3 || parsedLineColor.some(isNaN)) {
console.warn("Invalid lineColorStr. Using default light blue (173,216,230).");
parsedLineColor = [173, 216, 230];
}
if (parsedBgColor.length !== 3 || parsedBgColor.some(isNaN)) {
console.warn("Invalid bgColorStr. Using default black (0,0,0).");
parsedBgColor = [0, 0, 0];
}
// Laplacian kernel for edge detection. This kernel highlights regions of rapid intensity change.
const kernel = [
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
];
// Alternative Laplacian: [0, 1, 0, 1, -4, 1, 0, 1, 0]
// Iterate over each pixel, skipping a 1-pixel border (handled separately)
// This is because the 3x3 kernel needs neighbor pixels.
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let graySum = 0; // Accumulator for the convolution result on the grayscale image
// Apply the 3x3 kernel to the neighborhood of the current pixel (x,y)
for (let ky = -1; ky <= 1; ky++) { // Kernel y-offset (-1, 0, 1)
for (let kx = -1; kx <= 1; kx++) { // Kernel x-offset (-1, 0, 1)
// Calculate index for the 1D kernel array
const kernelIndex = (ky + 1) * 3 + (kx + 1);
const kernelValue = kernel[kernelIndex];
// Calculate coordinates of the neighbor pixel in the source image
const neighborX = x + kx;
const neighborY = y + ky;
// Calculate the index in the 1D source data array for the neighbor pixel
const srcPixelIndex = (neighborY * width + neighborX) * 4;
// Get RGB values of the neighbor pixel from the source data
const r = srcData[srcPixelIndex];
const g = srcData[srcPixelIndex + 1];
const b = srcData[srcPixelIndex + 2];
// Convert the neighbor pixel to grayscale (luminance standard)
const grayValue = 0.299 * r + 0.587 * g + 0.114 * b;
// Add the weighted grayscale value to the sum
graySum += grayValue * kernelValue;
}
}
// Calculate the index in the 1D destination data array for the current pixel (x,y)
const destPixelIndex = (y * width + x) * 4;
// If the absolute magnitude of the convolution result (edge strength)
// exceeds the threshold, color it as a "discharge line".
if (Math.abs(graySum) > threshold) {
data[destPixelIndex] = parsedLineColor[0]; // R
data[destPixelIndex + 1] = parsedLineColor[1]; // G
data[destPixelIndex + 2] = parsedLineColor[2]; // B
data[destPixelIndex + 3] = 255; // Alpha (opaque)
} else {
// Otherwise, color it as background.
data[destPixelIndex] = parsedBgColor[0]; // R
data[destPixelIndex + 1] = parsedBgColor[1]; // G
data[destPixelIndex + 2] = parsedBgColor[2]; // B
data[destPixelIndex + 3] = 255; // Alpha (opaque)
}
}
}
// Handle border pixels: fill them with the background color for a clean edge.
// This ensures all pixels in the output image are explicitly set.
for (let i = 0; i < width; i++) { // Top and bottom rows
const topRowIdx = i * 4;
const bottomRowIdx = ((height - 1) * width + i) * 4;
data[topRowIdx] = data[bottomRowIdx] = parsedBgColor[0];
data[topRowIdx + 1] = data[bottomRowIdx + 1] = parsedBgColor[1];
data[topRowIdx + 2] = data[bottomRowIdx + 2] = parsedBgColor[2];
data[topRowIdx + 3] = data[bottomRowIdx + 3] = 255;
}
for (let j = 1; j < height - 1; j++) { // Left and right columns (excluding corners already done)
const leftColIdx = (j * width) * 4;
const rightColIdx = (j * width + (width - 1)) * 4;
data[leftColIdx] = data[rightColIdx] = parsedBgColor[0];
data[leftColIdx + 1] = data[rightColIdx + 1] = parsedBgColor[1];
data[leftColIdx + 2] = data[rightColIdx + 2] = parsedBgColor[2];
data[leftColIdx + 3] = data[rightColIdx + 3] = 255;
}
// Put the modified pixel data back onto the canvas
ctx.putImageData(imageData, 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 Electrical Discharge Filter Effect Applicator is an online tool that enables users to apply a specialized visual effect to images, emulating the appearance of electrical discharges. This tool utilizes a convolution-based edge detection algorithm to highlight areas of significant intensity change in images, allowing users to emphasize edges by coloring them in a customizable line color while setting the background to a distinct color. It is ideal for enhancing digital art, creating visually striking graphics for presentations, or adding unique styles to photographs, making it a versatile resource for designers, artists, and hobbyists alike.