Please bookmark this page to avoid losing your image tool!

Image Northern Lights Filter Effect Tool

(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.
async function processImage(originalImg, numBands = 5, intensity = 0.6, colorString = "30,180,100;70,60,180;200,80,150") {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Ensure correct dimensions are used, prefer natural dimensions
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    canvas.width = imgWidth;
    canvas.height = imgHeight;

    if (imgWidth === 0 || imgHeight === 0) {
        // Handle cases where image dimensions are not available (e.g., image not loaded or invalid)
        console.error("Image dimensions are zero. Ensure the image is loaded and valid.");
        // Return an empty (or small error indicating) canvas
        ctx.fillStyle = "grey";
        ctx.fillRect(0,0, canvas.width || 100, canvas.height || 30);
        ctx.fillStyle = "red";
        ctx.font = "12px Arial";
        ctx.textAlign = "center";
        ctx.fillText("Error: Image not loaded or invalid.", (canvas.width || 100)/2, (canvas.height || 30)/2 + 4);
        return canvas; 
    }

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

    // Parse colors: "R1,G1,B1;R2,G2,B2;..."
    const parsedColors = colorString.split(';').map(cStr => {
        const parts = cStr.split(',').map(s => parseInt(s.trim(), 10));
        return { 
            r: (Number.isFinite(parts[0]) ? Math.min(255, Math.max(0,parts[0])) : 0), 
            g: (Number.isFinite(parts[1]) ? Math.min(255, Math.max(0,parts[1])) : 0), 
            b: (Number.isFinite(parts[2]) ? Math.min(255, Math.max(0,parts[2])) : 0)
        };
    }).filter(c => 
        // Filter out any entries that might have resulted from empty strings e.g. ";;"
        // Although the parsing above is robust and provides default 0,0,0
        // this check is just an extra safeguard if a color string was like "NaN,NaN,NaN"
        !(isNaN(c.r) || isNaN(c.g) || isNaN(c.b))
    );

    if (parsedColors.length === 0) {
        // Default fallback colors if colorString is invalid or results in no usable colors
        parsedColors.push({ r: 30, g: 180, b: 100 }); // Aurora Green
        parsedColors.push({ r: 70, g: 60, b: 180 }); // Aurora Purple/Blue
        parsedColors.push({ r: 200, g: 80, b: 150 });// Aurora Pink
    }

    // Use 'lighter' composite operation for glowing effects
    ctx.globalCompositeOperation = 'lighter'; 

    for (let i = 0; i < numBands; i++) {
        const color = parsedColors[i % parsedColors.length];
        
        // Band properties
        const bandHeight = Math.max(20, canvas.height * (0.15 + Math.random() * 0.35)); 
        const yPosition = Math.random() * Math.max(0, canvas.height - bandHeight); 
        
        const bandAlpha = Math.max(0.01, Math.min(1, intensity * (0.25 + Math.random() * 0.4))); // Alpha for band core

        // Vertical gradient for each band to make it softer at edges
        const gradient = ctx.createLinearGradient(0, yPosition, 0, yPosition + bandHeight);
        gradient.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, ${bandAlpha * 0.1})`);
        gradient.addColorStop(0.2 + Math.random() * 0.1, `rgba(${color.r}, ${color.g}, ${color.b}, ${bandAlpha * 0.7})`);
        gradient.addColorStop(0.5, `rgba(${color.r}, ${color.g}, ${color.b}, ${bandAlpha})`); // Core color intensity
        gradient.addColorStop(0.8 - Math.random() * 0.1, `rgba(${color.r}, ${color.g}, ${color.b}, ${bandAlpha * 0.7})`);
        gradient.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, ${bandAlpha * 0.1})`);
        
        ctx.fillStyle = gradient;

        // Draw a wavy band
        ctx.beginPath();
        
        const segments = Math.max(20, Math.floor(canvas.width / 30)); // More segments for wider images
        const baseAmplitude = bandHeight * 0.10; // Base for wave height variation
        
        // Top edge of the band
        // Start y for the top edge with some random displacement
        let currentY = yPosition + (Math.random() - 0.5) * baseAmplitude * 2; 
        ctx.moveTo(0, Math.max(0, Math.min(canvas.height, currentY)));

        for (let j_seg = 0; j_seg <= segments; j_seg++) {
            const x = (canvas.width / segments) * j_seg;
            // Sum of two sine waves for a more natural, irregular wave pattern
            const wave1Freq = (Math.PI * 2) / (canvas.width / (1 + Math.random() * 1.5)); // 1 to 2.5 cycles
            const wave1Phase = Math.random() * Math.PI * 2;
            const wave1Amp = baseAmplitude * (0.6 + Math.random() * 0.8); // Varies per segment slightly
            
            const wave2Freq = (Math.PI * 2) / (canvas.width / (0.4 + Math.random() * 1.0)); // 0.4 to 1.4 cycles
            const wave2Phase = Math.random() * Math.PI * 2;
            const wave2Amp = baseAmplitude * (0.4 + Math.random() * 0.6);

            const yOffset = Math.sin(x * wave1Freq + wave1Phase) * wave1Amp + 
                            Math.sin(x * wave2Freq + wave2Phase) * wave2Amp;
            
            currentY = yPosition + yOffset;
            ctx.lineTo(x, Math.max(0, Math.min(canvas.height, currentY))); // Clamp Y to canvas bounds
        }

        // Bottom edge of the band (offset by bandHeight)
        // Start y for the bottom edge (right side) with random displacement
        currentY = yPosition + bandHeight + (Math.random() - 0.5) * baseAmplitude * 2; 
        ctx.lineTo(canvas.width, Math.max(0, Math.min(canvas.height, currentY))); // Connect to bottom-right general area

        for (let j_seg = segments; j_seg >= 0; j_seg--) {
            const x = (canvas.width / segments) * j_seg;
            // Similar wave calculation for the bottom edge for consistency but still dynamic
            const wave1Freq = (Math.PI * 2) / (canvas.width / (1 + Math.random() * 1.5));
            const wave1Phase = Math.random() * Math.PI * 2; // Different phase helps
            const wave1Amp = baseAmplitude * (0.6 + Math.random() * 0.8);
            
            const wave2Freq = (Math.PI * 2) / (canvas.width / (0.4 + Math.random() * 1.0));
            const wave2Phase = Math.random() * Math.PI * 2;
            const wave2Amp = baseAmplitude * (0.4 + Math.random() * 0.6);

            const yOffset = Math.sin(x * wave1Freq + wave1Phase) * wave1Amp + 
                            Math.sin(x * wave2Freq + wave2Phase) * wave2Amp;
            
            currentY = yPosition + bandHeight + yOffset; // Add bandHeight for bottom edge
            ctx.lineTo(x,  Math.max(0, Math.min(canvas.height, currentY))); // Clamp Y
        }
        
        ctx.closePath();
        ctx.fill();
    }

    // Reset composite operation to default for any subsequent drawing (if any)
    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 Northern Lights Filter Effect Tool allows users to apply a stunning aurora-like effect to their images. By adjusting parameters such as the number of color bands, intensity, and specific color choices, users can create mesmerizing visuals that mimic the natural beauty of the Northern Lights. This tool is ideal for photographers, graphic designers, and anyone looking to enhance their images with vibrant, dynamic color effects. Common use cases include creating artistic photo edits, enhancing social media images, or adding unique backgrounds to digital projects.

Leave a Reply

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