Please bookmark this page to avoid losing your image tool!

Image Lens Flare Filter Application

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(
    originalImg,
    flareX_percent_param = 25,
    flareY_percent_param = 25,
    flareRadius_percent_param = 15,
    flareColor_param = "255,255,200", // Default: pale yellow
    flareOpacity_param = 0.6,
    addStreaks_param = "true",
    addGhosts_param = "true"
) {
    // --- Parameter parsing and validation ---
    const P_flareX_percent = parseFloat(flareX_percent_param);
    const P_flareY_percent = parseFloat(flareY_percent_param);
    const P_flareRadius_percent = parseFloat(flareRadius_percent_param);
    const P_flareOpacity = parseFloat(flareOpacity_param);

    // Apply defaults if parsing failed (resulted in NaN)
    const validatedFlareXPercent = isNaN(P_flareX_percent) ? 25 : P_flareX_percent;
    const validatedFlareYPercent = isNaN(P_flareY_percent) ? 25 : P_flareY_percent;
    const validatedFlareRadiusPercent = isNaN(P_flareRadius_percent) ? 15 : P_flareRadius_percent;
    const validatedFlareOpacity = isNaN(P_flareOpacity) ? 0.6 : Math.max(0, Math.min(1, P_flareOpacity));

    let validatedFlareColorRGB = (flareColor_param || "255,255,200").split(',').map(c => parseInt(c.trim(), 10));
    if (validatedFlareColorRGB.length !== 3 || validatedFlareColorRGB.some(isNaN)) {
        validatedFlareColorRGB = [255, 255, 200]; // Default color if parsing fails
    }

    const validatedAddStreaks = String(addStreaks_param).toLowerCase() === 'true';
    const validatedAddGhosts = String(addGhosts_param).toLowerCase() === 'true';

    // --- Canvas setup ---
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;
    
    if (canvas.width === 0 || canvas.height === 0) {
        console.warn("Image has zero width or height. Returning original image.");
        // Create a 0x0 canvas or return original image
        // To be displayable, a new empty 0x0 canvas is still an element.
        // However, instruction says "return a single canvas (preferred), or an Image ... "
        // If it cannot be processed returning originalImg seems safer if caller expects an Image.
        // Let's be consistent and return a 0x0 canvas.
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = 0;
        emptyCanvas.height = 0;
        return emptyCanvas; 
    }

    const ctx = canvas.getContext('2d');
    if (!ctx) {
        console.error("Could not get 2D context. Returning original image.");
        // As above, return an empty canvas.
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = canvas.width; // keep dimensions if available
        emptyCanvas.height = canvas.height;
        return emptyCanvas;
    }

    // Draw original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // --- Calculate flare pixel values ---
    const flareX = canvas.width * (validatedFlareXPercent / 100);
    const flareY = canvas.height * (validatedFlareYPercent / 100);
    const baseRadius = Math.min(canvas.width, canvas.height) * (validatedFlareRadiusPercent / 100);

    // Set composite operation for additive light effect (makes colors brighter)
    ctx.globalCompositeOperation = 'lighter';

    // --- 1. Main Flare Core (Brightest Point) ---
    const coreOpacity = validatedFlareOpacity * 0.9;
    ctx.fillStyle = `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, ${coreOpacity})`;
    ctx.beginPath();
    ctx.arc(flareX, flareY, baseRadius * 0.15, 0, 2 * Math.PI);
    ctx.fill();

    // --- 2. Main Glow (Radial Gradient) ---
    const glowOpacityStart = validatedFlareOpacity * 0.6;
    const glowOpacityMid = validatedFlareOpacity * 0.3;
    const glowGradient = ctx.createRadialGradient(flareX, flareY, baseRadius * 0.1, flareX, flareY, baseRadius);
    glowGradient.addColorStop(0, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, ${glowOpacityStart})`);
    glowGradient.addColorStop(0.5, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, ${glowOpacityMid})`);
    glowGradient.addColorStop(1, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, 0)`);
    ctx.fillStyle = glowGradient;
    ctx.beginPath();
    ctx.arc(flareX, flareY, baseRadius, 0, 2 * Math.PI);
    ctx.fill();

    // --- 3. Streaks ---
    if (validatedAddStreaks && baseRadius > 0) {
        const numStreaks = 12;
        const streakBaseOpacity = validatedFlareOpacity * 0.25;
        for (let i = 0; i < numStreaks; i++) {
            const angle = (i / numStreaks) * 2 * Math.PI + (Math.random() - 0.5) * 0.1; // Full circle with slight randomness
            const streakLength = baseRadius * (1.5 + Math.random() * 2.5); // Vary length
            const streakOpacity = streakBaseOpacity * (0.5 + Math.random() * 0.5); // Vary opacity
            const streakWidth = Math.max(1, baseRadius * 0.015 * (0.7 + Math.random() * 0.6)); // Vary width, min 1px

            const endX = flareX + Math.cos(angle) * streakLength;
            const endY = flareY + Math.sin(angle) * streakLength;

            const streakGrad = ctx.createLinearGradient(flareX, flareY, endX, endY);
            streakGrad.addColorStop(0, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, ${streakOpacity})`);
            streakGrad.addColorStop(0.3, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, ${streakOpacity * 0.7})`);
            streakGrad.addColorStop(1, `rgba(${validatedFlareColorRGB[0]}, ${validatedFlareColorRGB[1]}, ${validatedFlareColorRGB[2]}, 0)`);
            
            ctx.strokeStyle = streakGrad;
            ctx.lineWidth = streakWidth;
            ctx.beginPath();
            ctx.moveTo(flareX, flareY);
            ctx.lineTo(endX, endY);
            ctx.stroke();
        }
    }

    // --- 4. Ghost Flares ---
    if (validatedAddGhosts && baseRadius > 0) {
        const imgCenterX = canvas.width / 2;
        const imgCenterY = canvas.height / 2;
        const vecX = imgCenterX - flareX; // Vector from flare to image center
        const vecY = imgCenterY - flareY;

        // Predefined characteristics for a few ghost flares
        const ghostData = [
            { k: -0.5, sizeFactor: 0.25, colorShift: [30, -10, -20], opacityFactor: 0.15, sides: 6 }, // Reddish tint
            { k: 0.3, sizeFactor: 0.15, colorShift: [-10, 30, -10], opacityFactor: 0.20, sides: 5 },  // Greenish tint
            { k: 0.7, sizeFactor: 0.20, colorShift: [-20, -10, 30], opacityFactor: 0.18, sides: 7 },  // Bluish tint
            { k: 1.2, sizeFactor: 0.10, colorShift: [10, 10, -20], opacityFactor: 0.12, sides: 6 }, // Yellowish tint
            { k: 1.6, sizeFactor: 0.18, colorShift: [25, 0, -5], opacityFactor: 0.10, sides: 5 }    // Reddish tint again
        ];

        ghostData.forEach(g => {
            const ghostX = flareX + g.k * vecX;
            const ghostY = flareY + g.k * vecY;
            
            // Basic check to avoid drawing ghosts too far off-canvas (simple optimization)
            // This helps prevent excessive drawing outside the visible area.
            const safetyMargin = baseRadius * Math.max(g.sizeFactor, 0.5) * 2; 
            if (ghostX < -safetyMargin || ghostX > canvas.width + safetyMargin || 
                ghostY < -safetyMargin || ghostY > canvas.height + safetyMargin) {
                return;
            }

            const ghostRadius = baseRadius * g.sizeFactor * (0.8 + Math.random() * 0.4); // Randomize size slightly
            if (ghostRadius < 1) return; // Skip rendering if ghost is too small to be visible

            const r = Math.max(0, Math.min(255, validatedFlareColorRGB[0] + g.colorShift[0]));
            const gr = Math.max(0, Math.min(255, validatedFlareColorRGB[1] + g.colorShift[1]));
            const b = Math.max(0, Math.min(255, validatedFlareColorRGB[2] + g.colorShift[2]));
            const ghostOpacity = validatedFlareOpacity * g.opacityFactor * (0.8 + Math.random() * 0.4); // Randomize opacity slightly

            const ghostGradient = ctx.createRadialGradient(ghostX, ghostY, 0, ghostX, ghostY, ghostRadius);
            ghostGradient.addColorStop(0, `rgba(${r},${gr},${b},${ghostOpacity})`);
            ghostGradient.addColorStop(0.6, `rgba(${r},${gr},${b},${ghostOpacity * 0.5})`);
            ghostGradient.addColorStop(1, `rgba(${r},${gr},${b},0)`);
            ctx.fillStyle = ghostGradient;

            // Draw polygon for ghost (e.g., hexagon, pentagon to simulate lens iris shape)
            const sides = g.sides;
            ctx.beginPath();
            const angleOffset = (Math.PI / sides); // Offset for polygon orientation
            for (let i = 0; i < sides; i++) { 
                const angle = (i / sides) * 2 * Math.PI + angleOffset;
                const px = ghostX + ghostRadius * Math.cos(angle);
                const py = ghostY + ghostRadius * Math.sin(angle);
                if (i === 0) {
                    ctx.moveTo(px, py);
                } else {
                    ctx.lineTo(px, py);
                }
            }
            ctx.closePath();
            ctx.fill();
        });
    }

    // Reset composite operation to default
    ctx.globalCompositeOperation = 'source-over';

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Lens Flare Filter Application is a tool designed to enhance images by adding customizable lens flare effects. Users can specify the position, radius, color, and opacity of the flare, allowing for a variety of visual styles. The tool also includes options to add streaks of light and ghost flares for a more dynamic appearance. This application is ideal for photographers, graphic designers, and anyone looking to create visually striking images with a professional touch.

Leave a Reply

Your email address will not be published. Required fields are marked *