You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, flareX = "0.5", flareY = "0.5", radius = "100", brightness = "0.9", color = "255,255,220", numGhosts = "5", ghostSpread = "0.2", ghostRelativeSize = "0.5", strength = "0.7") {
// Ensure parameters are numbers
const pActualFlareX = parseFloat(flareX) * originalImg.width;
const pActualFlareY = parseFloat(flareY) * originalImg.height;
const pRadius = parseFloat(radius);
const pBrightness = parseFloat(brightness); // Main flare brightness component
const colorParts = color.split(',').map(s => parseInt(s.trim()));
const pR = colorParts[0] || 255;
const pG = colorParts[1] || 255;
const pB = colorParts[2] || 220;
const pNumGhosts = parseInt(numGhosts);
const pGhostSpread = parseFloat(ghostSpread); // Factor for how far ghosts spread from center
const pGhostRelativeSize = parseFloat(ghostRelativeSize); // Base size of ghosts relative to main radius
const pStrength = parseFloat(strength); // Overall effect strength/opacity alpha multiplier
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
const ctx = canvas.getContext('2d');
// Draw the original image first
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// If flare has no size or strength, return original image on canvas
if (pRadius <= 0 || pStrength <= 0) {
return canvas;
}
// Helper function to draw a flare glow element
// cx, cy: center coordinates of the glow
// rad: radius of the glow
// r, g, b: color components
// baseAlpha: an alpha multiplier for this specific glow component
// innerStop, midStop: gradient stop positions (0-1)
// midAlphaFactor: multiplier for alpha at midStop, relative to baseAlpha
function drawFlareGlow(context, cx, cy, rad, r, g, b, baseAlpha, innerStop = 0, midStop = 0.5, midAlphaFactor = 0.5) {
if (rad <= 0 || baseAlpha <= 0) return; // Don't draw if invisible or no size
const gradient = context.createRadialGradient(cx, cy, 0, cx, cy, rad);
gradient.addColorStop(innerStop, `rgba(${r},${g},${b},${baseAlpha})`);
if (midStop > innerStop && midStop < 1) { // Ensure midStop is valid and useful
gradient.addColorStop(midStop, `rgba(${r},${g},${b},${baseAlpha * midAlphaFactor})`);
}
gradient.addColorStop(1, `rgba(${r},${g},${b},0)`); // Fade to transparent
context.fillStyle = gradient;
context.beginPath();
context.arc(cx, cy, rad, 0, 2 * Math.PI);
context.fill();
}
// Set blending mode for light effects - 'lighter' adds pixel values
ctx.globalCompositeOperation = 'lighter';
// 1. Draw Main Flare (consisting of a soft glow and a brighter core)
// Soft Glow part of the main flare
drawFlareGlow(ctx, pActualFlareX, pActualFlareY, pRadius, pR, pG, pB, pBrightness * pStrength, 0, 0.6, 0.3);
// Brighter Core part of the main flare (typically white and very bright)
drawFlareGlow(ctx, pActualFlareX, pActualFlareY, pRadius * 0.3, 255, 255, 255, 1.0 * pStrength, 0, 0.5, 0.7);
// 2. Draw Ghosts (Secondary Lens Flare Artifacts)
if (pNumGhosts > 0) {
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// Vector from the flare's actual position to the image center
const vecFlareToCenterX = centerX - pActualFlareX;
const vecFlareToCenterY = centerY - pActualFlareY;
const distFlareToCenter = Math.sqrt(vecFlareToCenterX * vecFlareToCenterX + vecFlareToCenterY * vecFlareToCenterY);
// Predefined chromatic aberration colors for ghosts to cycle through
const ghostColors = [
[255, 150, 150], // Reddish
[150, 255, 150], // Greenish
[150, 150, 255], // Bluish
[255, 255, 150], // Yellowish
[150, 255, 255], // Cyanish
[255, 150, 255] // Magentaish
];
if (distFlareToCenter < 1.0) { // Case: Flare is at or very near the image center
for (let i = 0; i < pNumGhosts; i++) {
const angle = (i / pNumGhosts) * 2 * Math.PI;
// Ghosts spread radially outwards from the center
// Multiply pGhostSpread by a factor (e.g., 1.5) to make spread more apparent when flare is centered
const offsetMagnitude = pRadius * (0.5 + i * pGhostSpread * 1.5);
const ghostX = centerX + Math.cos(angle) * offsetMagnitude;
const ghostY = centerY + Math.sin(angle) * offsetMagnitude;
// Size variation for ghosts (can include some randomness or pattern)
let ghostRad = pRadius * pGhostRelativeSize * (0.7 - (i / pNumGhosts) * 0.4 + Math.sin(i * 0.7 + 0.5) * 0.15);
ghostRad = Math.max(1, ghostRad); // Ensure minimum radius of 1px
// Ghosts are typically dimmer than the main flare
const ghostAlpha = pBrightness * 0.4 * (1 - (i / pNumGhosts) * 0.6) * pStrength;
const [gcR, gcG, gcB] = ghostColors[i % ghostColors.length]; // Cycle through colors
drawFlareGlow(ctx, ghostX, ghostY, ghostRad, gcR, gcG, gcB, ghostAlpha);
}
} else { // Case: Flare is offset from the center
const normVecFlareToCenterX = vecFlareToCenterX / distFlareToCenter;
const normVecFlareToCenterY = vecFlareToCenterY / distFlareToCenter;
for (let i = 0; i < pNumGhosts; i++) {
// Ghosts are positioned along the line from image center, extending in the same direction as the vector from flare to center.
// This places them on the "opposite" side of the center from the flare source, a common optical effect.
const ghostOffsetMagnitude = (i + 1) * pGhostSpread * distFlareToCenter;
const ghostX = centerX + normVecFlareToCenterX * ghostOffsetMagnitude;
const ghostY = centerY + normVecFlareToCenterY * ghostOffsetMagnitude;
let ghostRad = pRadius * pGhostRelativeSize * (0.8 - (i / pNumGhosts) * 0.5 + Math.sin(i * 0.5 + 0.8) * 0.2);
ghostRad = Math.max(1, ghostRad);
const ghostAlpha = pBrightness * 0.5 * (1 - (i / pNumGhosts) * 0.5) * pStrength;
const [gcR, gcG, gcB] = ghostColors[i % ghostColors.length];
drawFlareGlow(ctx, ghostX, ghostY, ghostRad, gcR, gcG, gcB, ghostAlpha);
}
}
}
// Reset composite operation to default to avoid affecting subsequent drawings on this context (if any)
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes