You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, effectStrength = 0.6, glowLevel = 0.5, tintColorStr = "255,230,200,0.15", lightSourceX = 0.5, lightSourceY = 0.2) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Ensure originalImg is fully loaded, though typically it should be when passed.
// Using naturalWidth/Height for intrinsic dimensions of the image.
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// 0. Parse tint color string "R,G,B,A"
const [tintR, tintG, tintB, tintA] = tintColorStr.split(',').map(s => parseFloat(s.trim()));
// 1. Draw original image to main canvas as the base
ctx.drawImage(originalImg, 0, 0, w, h);
// 2. Glow Layer for highlights
// Only apply if glowLevel is significant
if (glowLevel > 0.01) {
// Create a temporary canvas to get pixel data from the original image without modifications
const highlightSourceCanvas = document.createElement('canvas');
highlightSourceCanvas.width = w;
highlightSourceCanvas.height = h;
const highlightSourceCtx = highlightSourceCanvas.getContext('2d');
highlightSourceCtx.drawImage(originalImg, 0, 0, w, h);
const imageData = highlightSourceCtx.getImageData(0, 0, w, h);
const data = imageData.data;
// Create the highlight mask: bright areas become opaque, others transparent
// Adjust threshold: higher glowLevel means lower threshold (more areas glow)
const thresholdLuminance = 200 - glowLevel * 120;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i+1];
const b = data[i+2];
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
if (luminance > thresholdLuminance) {
// Make highlights brighter and slightly warmer for the glow source
data[i] = Math.min(255, r + 70 * glowLevel);
data[i+1] = Math.min(255, g + 60 * glowLevel);
data[i+2] = Math.min(255, b + 50 * glowLevel);
data[i+3] = 255; // Fully opaque for glow source
} else {
data[i+3] = 0; // Transparent for non-highlight areas
}
}
// Canvas to hold the sharp (unblurred) highlight mask
const sharpGlowCanvas = document.createElement('canvas');
sharpGlowCanvas.width = w;
sharpGlowCanvas.height = h;
const sharpGlowCtx = sharpGlowCanvas.getContext('2d');
sharpGlowCtx.putImageData(imageData, 0, 0);
// Canvas for the blurred glow effect
const blurredGlowCanvas = document.createElement('canvas');
blurredGlowCanvas.width = w;
blurredGlowCanvas.height = h;
const blurredGlowCtx = blurredGlowCanvas.getContext('2d');
// Blur radius scales with glowLevel
const blurRadius = Math.max(1, 3 + glowLevel * 22); // Min 3px blur, up to 25px
blurredGlowCtx.filter = `blur(${blurRadius}px)`;
blurredGlowCtx.drawImage(sharpGlowCanvas, 0, 0, w, h); // Draw sharp mask, filter applies blur
blurredGlowCtx.filter = 'none'; // Reset filter on this context
// Blend the blurred glow onto the main canvas
// Glow opacity scales with glowLevel
ctx.globalAlpha = Math.min(1, 0.3 + glowLevel * 0.7);
ctx.globalCompositeOperation = 'lighter'; // 'screen' or 'lighter' are good for glows
ctx.drawImage(blurredGlowCanvas, 0, 0);
// Reset global alpha and composite operation
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
}
// 3. Simulated Radial Light Source
// Only apply if effectStrength is significant
if (effectStrength > 0.01) {
const focusPxX = lightSourceX * w;
const focusPxY = lightSourceY * h;
const maxDim = Math.max(w, h);
// Gradient radius scales with image size and effect strength
const gradientRadius = maxDim * (0.35 + effectStrength * 0.5);
const gradient = ctx.createRadialGradient(focusPxX, focusPxY, 0, focusPxX, focusPxY, gradientRadius);
// Opacity of the light's center scales with effectStrength
const centerAlpha = Math.min(1, 0.1 + 0.5 * effectStrength);
gradient.addColorStop(0, `rgba(255, 255, 240, ${centerAlpha})`); // Bright, slightly warm center
gradient.addColorStop(0.25, `rgba(255, 250, 230, ${centerAlpha * 0.7})`);
gradient.addColorStop(0.5, `rgba(255, 245, 220, ${centerAlpha * 0.3})`);
gradient.addColorStop(1, `rgba(255, 255, 255, 0)`); // Fades to transparent
ctx.globalCompositeOperation = 'soft-light'; // 'overlay' or 'color-dodge' could also work
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over'; // Reset
}
// 4. Global Color Tint
// Apply if tint has alpha and effectStrength is significant
if (tintA > 0 && effectStrength > 0.01) {
ctx.globalCompositeOperation = 'overlay'; // 'soft-light' or 'color' could work well too
// Tint's final alpha scales with its provided alpha and effectStrength
const currentTintAlpha = Math.min(1, tintA * (0.2 + effectStrength * 0.8));
ctx.fillStyle = `rgba(${tintR}, ${tintG}, ${tintB}, ${currentTintAlpha})`;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over'; // Reset
}
// 5. Final Overall Adjustments (Brightness, Contrast, Saturation)
// Apply if effectStrength is significant
if (effectStrength > 0.01) {
// Create a temporary canvas to hold the current state of the main canvas
const finalAdjustCanvas = document.createElement('canvas');
finalAdjustCanvas.width = w;
finalAdjustCanvas.height = h;
const finalAdjustCtx = finalAdjustCanvas.getContext('2d');
finalAdjustCtx.drawImage(canvas, 0, 0); // Copy current result from main canvas
ctx.clearRect(0, 0, w, h); // Clear main canvas before drawing the filtered version
// Adjustments scale with effectStrength
const brightnessValue = 1 + 0.15 * effectStrength; // Range: 1.0 to 1.15
const contrastValue = 1 + 0.15 * effectStrength; // Range: 1.0 to 1.15
// Desaturation increases with effectStrength
const saturationValue = Math.max(0, 1 - 0.4 * effectStrength); // Range: 1.0 to 0.6
ctx.filter = `brightness(${brightnessValue}) contrast(${contrastValue}) saturate(${saturationValue})`;
ctx.drawImage(finalAdjustCanvas, 0, 0); // Draw the copied image with filter applied
ctx.filter = 'none'; // Reset filter on the main context
}
return canvas;
}
Apply Changes