Please bookmark this page to avoid losing your image tool!

Image Astronomical Chart 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, starDetectionThreshold = 200, starColorInput = "rgb(255, 255, 230)", backgroundColorInput = "rgb(5, 5, 25)", nebulaTintColor = "rgb(40, 60, 130)", nebulaIntensity = 1.2) {

    // Helper to parse color strings (e.g., "rgb(255,0,0)", "#FF0000", "red") into [r, g, b]
    function parseColor(colorStr) {
        const canvas = document.createElement('canvas');
        canvas.width = 1;
        canvas.height = 1;
        // Use willReadFrequently for contexts where getImageData is called, even for small ones.
        const ctx = canvas.getContext('2d', { willReadFrequently: true }); 
        
        // Set a known transparent background to help detect if a color string fails to parse
        // and defaults to transparent black vs. an intentionally transparent color.
        ctx.fillStyle = 'rgba(0,0,0,0)'; 
        ctx.fillRect(0,0,1,1);

        ctx.fillStyle = colorStr; // Assign the color string
        ctx.fillRect(0, 0, 1, 1); // Draw the color
        const pixelData = ctx.getImageData(0, 0, 1, 1).data;

        // Basic check for parse failure: if the resulting color is transparent black,
        // and the input string didn't explicitly ask for transparency,
        // it might be an invalid color string.
        const isInputExplicitlyTransparent = /(?:rgba\(.+,\s*0\)|transparent)/i.test(colorStr);
        if (pixelData[3] === 0 && !isInputExplicitlyTransparent) {
            console.warn(`Failed to parse color "${colorStr}" or it resolved to fully transparent unexpectedly. Defaulting to opaque black.`);
            return [0, 0, 0]; 
        }
        return [pixelData[0], pixelData[1], pixelData[2]];
    }

    const starColor = parseColor(starColorInput);
    const backgroundColor = parseColor(backgroundColorInput);
    const nebulaColor = parseColor(nebulaTintColor);

    const outputCanvas = document.createElement('canvas');
    const ctx = outputCanvas.getContext('2d');

    // Ensure the image is loaded and has dimensions
    if (!originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        // Wait for the image to load if it's not already fully available
        // This situation typically means an Image object was created but its src hasn't finished loading
        // or the src is invalid.
        try {
            await new Promise((resolve, reject) => {
                if (originalImg.complete && originalImg.naturalWidth > 0) {
                    resolve(); // Already loaded
                    return;
                }
                originalImg.onload = () => resolve();
                originalImg.onerror = () => reject(new Error("Image failed to load."));
                // If src is not set or invalid, it might neither load nor error. Add timeout.
                setTimeout(() => reject(new Error("Image loading timed out.")), 5000);
            });
        } catch (e) {
            console.error("Error loading original image:", e.message);
            outputCanvas.width = 100; // Create a small error canvas
            outputCanvas.height = 30;
            ctx.fillStyle = "red";
            ctx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
            ctx.fillStyle = "white";
            ctx.font = "10px Arial";
            ctx.fillText("Image Error", 5, 20);
            return outputCanvas;
        }
    }

    outputCanvas.width = originalImg.naturalWidth;
    outputCanvas.height = originalImg.naturalHeight;

    ctx.drawImage(originalImg, 0, 0, outputCanvas.width, outputCanvas.height);

    const imageData = ctx.getImageData(0, 0, outputCanvas.width, outputCanvas.height);
    const data = imageData.data;

    const effectiveNebulaIntensity = Math.max(0, nebulaIntensity); // Ensure non-negative

    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]; // Alpha is preserved by default

        // Calculate luminance (perceived brightness)
        const luminance = 0.299 * r + 0.587 * g + 0.114 * b;

        if (luminance > starDetectionThreshold) {
            // This pixel is a "star"
            data[i]   = starColor[0]; // R
            data[i+1] = starColor[1]; // G
            data[i+2] = starColor[2]; // B
        } else {
            // This pixel is part of the "nebula" or background
            let normalizedLuminance;
            if (starDetectionThreshold > 0) {
                // Normalize luminance for the range 0 to starDetectionThreshold
                normalizedLuminance = Math.min(luminance / starDetectionThreshold, 1.0);
            } else {
                // If threshold is 0, this 'else' block is only reached if luminance is also 0.
                normalizedLuminance = 0;
            }
            
            // Calculate the mix amount for nebula color.
            // This determines how much of the nebulaColor is blended with backgroundColor.
            let mixAmount = normalizedLuminance * effectiveNebulaIntensity;
            mixAmount = Math.max(0, Math.min(mixAmount, 1.0)); // Clamp mixAmount to [0, 1]

            // Interpolate between backgroundColor and nebulaColor
            data[i]   = backgroundColor[0] * (1 - mixAmount) + nebulaColor[0] * mixAmount;
            data[i+1] = backgroundColor[1] * (1 - mixAmount) + nebulaColor[1] * mixAmount;
            data[i+2] = backgroundColor[2] * (1 - mixAmount) + nebulaColor[2] * mixAmount;

            // Clamp final color values to the valid 0-255 range
            data[i]   = Math.max(0, Math.min(255, Math.round(data[i])));
            data[i+1] = Math.max(0, Math.min(255, Math.round(data[i+1])));
            data[i+2] = Math.max(0, Math.min(255, Math.round(data[i+2])));
        }
    }

    ctx.putImageData(imageData, 0, 0);
    return outputCanvas;
}

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 Astronomical Chart Filter Effect Tool is designed to enhance images by applying an astronomical filter effect. Users can transform photos into celestial-themed visuals by detecting ‘stars’ based on brightness thresholds and altering colors to create nebulas and enhanced backgrounds. This tool is ideal for artists, astronomers, or anyone looking to add a galactic touch to their images for use in presentations, social media, or personal projects.

Leave a Reply

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