You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, strength = 1.0, grayLevel = 128) {
// Use naturalWidth and naturalHeight for intrinsic image dimensions if available.
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Create a temporary canvas to draw the image and get its pixel data.
// This is necessary because an Image object itself doesn't provide direct pixel access.
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
// Add { willReadFrequently: true } for potential performance optimization when using getImageData frequently.
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.drawImage(originalImg, 0, 0, width, height);
let imageData;
try {
// Getting image data can fail if the image is from a different origin (tainted canvas).
imageData = tempCtx.getImageData(0, 0, width, height);
} catch (e) {
console.error("Error getting image data for emboss filter: ", e);
// Create a canvas displaying an error message.
const errorCanvas = document.createElement('canvas');
errorCanvas.width = width;
errorCanvas.height = height;
const errorCtx = errorCanvas.getContext('2d');
errorCtx.fillStyle = "rgba(230, 230, 230, 1)"; // Solid light gray background
errorCtx.fillRect(0, 0, width, height);
errorCtx.fillStyle = "black";
errorCtx.font = "16px Arial";
errorCtx.textAlign = "center";
errorCtx.textBaseline = "middle";
errorCtx.fillText("Error: Could not process image.", width / 2, height / 2 - 10);
errorCtx.fillText("This may be due to cross-origin restrictions.", width / 2, height / 2 + 10);
return errorCanvas;
}
const data = imageData.data; // Pixel data array (flat, RGBA format).
// Create the output canvas where the filtered image will be drawn.
const outputCanvas = document.createElement('canvas');
outputCanvas.width = width;
outputCanvas.height = height;
const outputCtx = outputCanvas.getContext('2d');
const outputImageData = outputCtx.createImageData(width, height);
const outputData = outputImageData.data;
// Emboss convolution kernel. This specific kernel simulates light from the top-left.
// It effectively calculates: Pixel(top-left neighbor) - Pixel(bottom-right neighbor).
// For a flat area, this results in 0.
// The 'strength' parameter scales this difference, and 'grayLevel' adds a bias.
const kernel = [
[1, 0, 0],
[0, 0, 0], // The central pixel's original value is not used by this kernel.
[0, 0, -1]
];
const kernelSize = 3; // Kernel is 3x3.
const halfKernelSize = Math.floor(kernelSize / 2); // This will be 1 for a 3x3 kernel.
// Iterate over each pixel coordinate (x, y) of the image.
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let rSum = 0, gSum = 0, bSum = 0;
// Apply the convolution kernel to the current pixel.
for (let ky = 0; ky < kernelSize; ky++) { // Kernel y-index
for (let kx = 0; kx < kernelSize; kx++) { // Kernel x-index
const kernelVal = kernel[ky][kx];
// Skip calculations if kernel value is 0 (minor optimization).
if (kernelVal === 0) {
continue;
}
// Calculate the coordinates of the neighbor pixel to sample.
// The kernel's center (halfKernelSize, halfKernelSize) aligns conceptually with the current pixel (x,y).
// So, kernel cell (kx, ky) maps to image pixel (x + kx - halfKernelSize, y + ky - halfKernelSize).
let neighborX = x + kx - halfKernelSize;
let neighborY = y + ky - halfKernelSize;
// Handle image edges by clamping neighbor coordinates (also known as "clamp to edge" or "repeat border pixels").
neighborX = Math.max(0, Math.min(width - 1, neighborX));
neighborY = Math.max(0, Math.min(height - 1, neighborY));
// Get the index of the neighbor pixel in the flat data array. Each pixel is 4 bytes (R,G,B,A).
const neighborIndex = (neighborY * width + neighborX) * 4;
// Accumulate the weighted channel values.
rSum += data[neighborIndex] * kernelVal;
gSum += data[neighborIndex + 1] * kernelVal;
bSum += data[neighborIndex + 2] * kernelVal;
// Alpha channel is not typically convolved in emboss filters.
}
}
// Get the index of the current pixel in the output data array.
const currentIndex = (y * width + x) * 4;
// The convolved sums (rSum, gSum, bSum) represent the difference (e.g., -255 to 255).
// Apply the 'strength' factor to scale this difference, then add 'grayLevel' as an offset/bias.
// Finally, clamp the result to the [0, 255] range and convert to an integer for pixel data.
outputData[currentIndex] = Math.max(0, Math.min(255, Math.floor(rSum * strength + grayLevel)));
outputData[currentIndex + 1] = Math.max(0, Math.min(255, Math.floor(gSum * strength + grayLevel)));
outputData[currentIndex + 2] = Math.max(0, Math.min(255, Math.floor(bSum * strength + grayLevel)));
outputData[currentIndex + 3] = data[currentIndex + 3]; // Preserve the original alpha channel.
}
}
// Put the processed pixel data onto the output canvas.
outputCtx.putImageData(outputImageData, 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 Emboss Filter Application allows users to apply an emboss effect to their images, creating a three-dimensional, raised effect by using image convolution. Users can adjust the strength of the emboss effect and the gray level to customize the output. This tool is suitable for enhancing images in graphic design, creating artistic effects for photographs, or adding depth to images for presentations. It is especially useful for designers and artists looking to give a unique visual style to their work.