You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg) {
// Helper function to create a minimal canvas indicating an error or inability to process
const createErrorCanvas = () => {
const errCanvas = document.createElement('canvas');
errCanvas.width = 1;
errCanvas.height = 1;
const errCtx = errCanvas.getContext('2d');
if (errCtx) {
errCtx.fillStyle = 'lightgray'; // A light gray 1x1 pixel
errCtx.fillRect(0, 0, 1, 1);
}
return errCanvas;
};
// Step 1: Ensure the image is fully loaded and has valid dimensions
let imageReady = false;
if (originalImg.complete) {
// Image is reported as complete by the browser
if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
// Complete but bad dimensions usually means broken image or non-image type
console.error("Image is complete but has zero natural width or height.");
} else {
imageReady = true;
}
} else {
// Image is not yet complete, so we'll wait for it to load or error.
try {
await new Promise((resolve, reject) => {
originalImg.onload = () => {
// Once onload fires, check dimensions again, as it can fire for broken images.
if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
reject(new Error('Image loaded, but naturalWidth or naturalHeight is 0.'));
} else {
resolve();
}
};
originalImg.onerror = () => reject(new Error('Image failed to load (onerror event).'));
// A common edge case: new Image() without src set, or an <img> tag not yet configured.
// The Promise might hang if src is never set. However, for a provided Image object,
// we assume its src attribute is either set or will be set externally.
// If originalImg.src is "" and it's not in DOM, it typically errors out quickly.
});
imageReady = true; // If promise resolved, image is ready
} catch (error) {
console.error(error.message);
// imageReady remains false
}
}
if (!imageReady) {
return createErrorCanvas(); // Return placeholder if image isn't usable
}
// Step 2: Create a canvas and get its 2D rendering context
const canvas = document.createElement('canvas');
// Use the natural dimensions of the image for the canvas
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error("Failed to get 2D rendering context from canvas.");
return createErrorCanvas(); // Return placeholder if context cannot be obtained
}
// Step 3: Apply desaturation
// Method 1: Use the canvas context's 'filter' property (modern, preferred)
if (typeof ctx.filter !== 'undefined') {
ctx.filter = 'grayscale(100%)';
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
ctx.filter = 'none'; // Reset filter to avoid side effects if context is reused elsewhere
} else {
// Method 2: Fallback to manual pixel manipulation (for older browsers/environments)
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height); // Draw the original image first
try {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // This is a Uint8ClampedArray: [R,G,B,A, R,G,B,A, ...]
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// const a = data[i+3]; // Alpha channel, preserved
// Calculate grayscale value using the luminosity method (standard formula)
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
// Set Red, Green, and Blue components to the calculated gray value
data[i] = Math.round(gray); // Red
data[i + 1] = Math.round(gray); // Green
data[i + 2] = Math.round(gray); // Blue
}
ctx.putImageData(imageData, 0, 0); // Write the modified pixel data back to the canvas
} catch (e) {
// This catch block usually handles SecurityError if the canvas is tainted
// (e.g., by drawing a cross-origin image without CORS headers).
console.error("Failed to desaturate image using pixel manipulation. This can occur with cross-origin images if CORS is not properly configured. The original image is returned.", e);
// If this error occurs, the canvas already contains the original (colored) image
// drawn by `ctx.drawImage` before the `try` block. This acts as a graceful fallback,
// returning the colored image instead of a broken state or an error canvas.
}
}
// Step 4: Return the processed canvas
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 Desaturation Filter tool allows users to convert colorful images into grayscale. This can be particularly useful for artists, designers, and photographers who want to emphasize texture or composition without the distraction of color. It can be applied for various purposes such as creating black-and-white art, enhancing visual contrast, or preparing images for printing in monochrome. The tool processes images efficiently, ensuring a smooth transition from color to grayscale.