You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, strength = 0.4, radius = 15, threshold = 190, softness = 0.3) {
// Ensure originalImg is loaded and has dimensions
const w = originalImg.naturalWidth || originalImg.width;
const h = originalImg.naturalHeight || originalImg.height;
if (!w || !h) {
// Image not loaded or has no dimensions. Return an empty 1x1 canvas.
const emptyCanvas = document.createElement('canvas');
emptyCanvas.width = 1;
emptyCanvas.height = 1;
return emptyCanvas;
}
// Clamp and validate parameters
strength = Math.min(1, Math.max(0, strength));
radius = Math.max(0, radius); // blur radius cannot be negative
threshold = Math.min(255, Math.max(0, threshold)); // luminance threshold
softness = Math.min(1, Math.max(0, softness));
const mainCanvas = document.createElement('canvas');
mainCanvas.width = w;
mainCanvas.height = h;
const ctx = mainCanvas.getContext('2d');
// 1. Draw original image to main canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// ---- BLOOM EFFECT ----
// Apply bloom if strength and radius are effective (greater than a small epsilon)
if (strength > 0.001 && radius > 0.001) {
// 2. Extract highlights
// Create an offscreen canvas for highlight extraction.
const highlightCanvas = document.createElement('canvas');
highlightCanvas.width = w;
highlightCanvas.height = h;
const hCtx = highlightCanvas.getContext('2d');
// Get image data from the main canvas (where originalImg is currently drawn)
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
// Create new ImageData to store the highlight mask.
const highlightImageData = ctx.createImageData(w, h);
const hData = highlightImageData.data;
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]; // Original alpha of the pixel
// Calculate luminance (standard Rec. 709)
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
if (luminance >= threshold) {
// If_luma is above threshold, copy pixel to highlight mask
hData[i] = r;
hData[i + 1] = g;
hData[i + 2] = b;
hData[i + 3] = a; // Preserve original alpha
} else {
// If_luma is below threshold, make pixel transparent black in highlight mask
hData[i] = 0;
hData[i + 1] = 0;
hData[i + 2] = 0;
hData[i + 3] = 0;
}
}
hCtx.putImageData(highlightImageData, 0, 0); // Draw highlight mask to highlightCanvas
// 3. Blur the highlights
// Create an offscreen canvas for blurred highlights
const blurredHighlightsCanvas = document.createElement('canvas');
blurredHighlightsCanvas.width = w;
blurredHighlightsCanvas.height = h;
const bhCtx = blurredHighlightsCanvas.getContext('2d');
bhCtx.filter = `blur(${radius}px)`; // Apply blur filter
bhCtx.drawImage(highlightCanvas, 0, 0, w, h); // Draw highlight mask through the blur filter
bhCtx.filter = 'none'; // Reset filter for this context
// 4. Blend blurred highlights back to the main canvas
ctx.globalAlpha = strength; // Control intensity of the bloom
ctx.globalCompositeOperation = 'screen'; // 'screen' or 'lighter' are common for bloom
ctx.drawImage(blurredHighlightsCanvas, 0, 0, w, h);
// Reset global alpha and composite operation for main context
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
}
// ---- OVERALL SOFTNESS AND ADJUSTMENTS ----
// Apply if softness factor is effective (greater than a small epsilon)
if (softness > 0.001) {
// Calculate effect magnitudes based on softness parameter
const softBlurAmount = softness * 0.5; // Max 0.5px blur at softness = 1
const contrastValue = 100 - Math.floor(softness * 10); // Max 10% reduction (e.g., 90% contrast)
const saturateValue = 100 - Math.floor(softness * 10); // Max 10% desaturation (e.g., 90% saturation)
let filterArray = [];
// Add blur if it's significant enough (avoids `blur(0.00px)`)
if (softBlurAmount > 0.01) {
filterArray.push(`blur(${softBlurAmount.toFixed(2)}px)`);
}
// Add contrast adjustment if it's not effectively 100%
if (contrastValue < 99.9) {
filterArray.push(`contrast(${contrastValue}%)`);
}
// Add saturation adjustment if it's not effectively 100%
if (saturateValue < 99.9) {
filterArray.push(`saturate(${saturateValue}%)`);
}
const filterString = filterArray.join(' ');
if (filterString) { // Only apply if there's any filter to apply
// To apply filter to mainCanvas's current content:
// 1. Copy mainCanvas content to a temporary canvas.
// 2. Clear mainCanvas (optional as drawImage overwrites, but good for clarity).
// 3. Set filter on mainCanvas's context.
// 4. Draw temporary canvas content back to mainCanvas (filter applies on this draw).
// 5. Reset filter on mainCanvas's context.
const tempTransferCanvas = document.createElement('canvas');
tempTransferCanvas.width = w;
tempTransferCanvas.height = h;
const tempCtx = tempTransferCanvas.getContext('2d');
tempCtx.drawImage(mainCanvas, 0, 0); // 1. Copy current state of mainCanvas
ctx.clearRect(0, 0, w, h); // 2. Clear mainCanvas
ctx.filter = filterString; // 3. Set filter
ctx.drawImage(tempTransferCanvas, 0, 0); // 4. Draw back with filter
ctx.filter = 'none'; // 5. Reset filter
}
}
return mainCanvas;
}
Apply Changes