You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, intensity = 0.5) {
// Ensure intensity is not negative; users can provide intensity > 1 for stronger effects.
intensity = Math.max(0, intensity);
const w = originalImg.naturalWidth || originalImg.width;
const h = originalImg.naturalHeight || originalImg.height;
const outputCanvas = document.createElement('canvas');
outputCanvas.width = w;
outputCanvas.height = h;
const outputCtx = outputCanvas.getContext('2d');
// Draw the original image as the base. All operations will be applied to this canvas.
outputCtx.drawImage(originalImg, 0, 0, w, h);
// If intensity is negligible, the effect is minimal. Return the canvas with the original image.
if (intensity <= 0.001) {
return outputCanvas;
}
// --- Calculate effect parameters based on intensity ---
// Halation (glow around highlights) parameters
const halationRadius = intensity * 20; // Blur radius for the glow. Increases with intensity.
const highlightLuminanceThreshold = 240 - intensity * 80; // Pixels brighter than this are considered highlights. Threshold decreases with intensity.
const halationBlendOpacity = Math.min(1, intensity * 0.7); // Opacity for blending the halation. Max 1.0.
// Contrast and Saturation adjustment parameters for CSS-like filters
// A value < 1.0 reduces contrast/saturation.
const cssFilterContrast = Math.max(0, 1.0 - intensity * 0.25); // Reduces contrast as intensity increases.
const cssFilterSaturation = Math.max(0, 1.0 - intensity * 0.20); // Reduces saturation as intensity increases.
// --- 1. Create Highlight Layer ---
// This layer will contain only the highlight parts of the image.
const highlightCanvas = document.createElement('canvas');
highlightCanvas.width = w;
highlightCanvas.height = h;
const highlightCtx = highlightCanvas.getContext('2d');
// Draw original image to extract its pixels for C
highlightCtx.drawImage(originalImg, 0, 0, w, h);
const imageDataForHighlights = highlightCtx.getImageData(0, 0, w, h);
const pixels = imageDataForHighlights.data;
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
// Using a common luminance calculation: L = 0.299*R + 0.587*G + 0.114*B
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
if (luminance <= highlightLuminanceThreshold) {
pixels[i + 3] = 0; // Make non-highlight pixels transparent
} else {
pixels[i + 3] = 255; // Keep highlight pixels fully opaque for blurring
}
}
highlightCtx.putImageData(imageDataForHighlights, 0, 0);
// --- 2. Blur Highlight Layer ---
// The extracted highlights are blurred to create the soft glow.
const blurredHighlightCanvas = document.createElement('canvas');
blurredHighlightCanvas.width = w;
blurredHighlightCanvas.height = h;
const blurredHighlightCtx = blurredHighlightCanvas.getContext('2d');
if (halationRadius > 0.001) { // Apply blur only if radius is significant
blurredHighlightCtx.filter = `blur(${halationRadius}px)`;
blurredHighlightCtx.drawImage(highlightCanvas, 0, 0, w, h);
blurredHighlightCtx.filter = 'none'; // Reset filter
} else {
// If no blur is needed, just copy the (unblurred) highlights
blurredHighlightCtx.drawImage(highlightCanvas, 0, 0, w, h);
}
// --- 3. Composite Blurred Highlights onto Output Canvas ---
// The blurred highlights are blended back onto the main image.
if (halationBlendOpacity > 0.001) { // Apply blend only if opacity is significant
outputCtx.globalAlpha = halationBlendOpacity;
// 'screen' composite mode is often preferred for a softer bloom/halation effect.
outputCtx.globalCompositeOperation = 'screen';
outputCtx.drawImage(blurredHighlightCanvas, 0, 0, w, h);
outputCtx.globalAlpha = 1.0; // Reset globalAlpha
outputCtx.globalCompositeOperation = 'source-over'; // Reset composite operation
}
// --- 4. Apply final Contrast and Saturation adjustments ---
// These adjustments are applied to the image that now includes the halation effect.
const needsContrastAdj = Math.abs(cssFilterContrast - 1.0) > 0.001;
const needsSaturationAdj = Math.abs(cssFilterSaturation - 1.0) > 0.001;
if (needsContrastAdj || needsSaturationAdj) {
// Use a temporary canvas to apply filters, as canvas filters affect subsequent draw calls.
const tempTransferCanvas = document.createElement('canvas');
tempTransferCanvas.width = w;
tempTransferCanvas.height = h;
const tempTransferCtx = tempTransferCanvas.getContext('2d');
let filterString = "";
if (needsContrastAdj) filterString += `contrast(${cssFilterContrast}) `;
if (needsSaturationAdj) filterString += `saturate(${cssFilterSaturation})`;
tempTransferCtx.filter = filterString.trim();
// Draw the current outputCanvas (original + halation) onto temp canvas, applying C/S filters.
tempTransferCtx.drawImage(outputCanvas, 0, 0, w, h);
// Clear outputCanvas and draw the fully processed image from tempTransferCanvas back.
outputCtx.clearRect(0, 0, w, h);
outputCtx.drawImage(tempTransferCanvas, 0, 0, w, h);
outputCtx.filter = 'none'; // Reset filter on outputCtx
}
return outputCanvas;
}
Apply Changes