Please bookmark this page to avoid losing your image tool!

Image Star Map 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.
function processImage(originalImg, thresholdParam = 200, starColorStr = "white", skyColorStr = "black", starSizeParam = 1) {
    // Sanitize and validate parameters
    let numThreshold = Number(thresholdParam);
    if (isNaN(numThreshold)) {
        numThreshold = 200; // Default threshold if input is not a valid number
    }
    const threshold = Math.max(0, Math.min(255, numThreshold)); // Clamp threshold to 0-255

    let numStarSize = Number(starSizeParam);
    if (isNaN(numStarSize)) {
        numStarSize = 1; // Default star size if input is not a valid number
    }
    // Ensure star size is at least 1. If 0.5 is given, it becomes 1.
    // If a size like 2.5 is given, it remains 2.5 for calculations in the 'else' branch.
    const finalStarSize = Math.max(1, numStarSize);

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

    if (!outputCtx) {
        // Fallback if canvas context cannot be created, though highly unlikely for '2d'
        console.error("Failed to get 2D context");
        return document.createElement('div').appendChild(document.createTextNode("Error: Canvas not supported or context creation failed."));
    }

    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Handle 0-dimension images gracefully
    if (width === 0 || height === 0) {
        outputCanvas.width = 0;
        outputCanvas.height = 0;
        return outputCanvas;
    }

    outputCanvas.width = width;
    outputCanvas.height = height;

    // Use a temporary canvas to draw the original image and get its pixel data.
    // This avoids issues if originalImg is already on a live canvas or tainted.
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = width;
    tempCanvas.height = height;
    const tempCtx = tempCanvas.getContext('2d');

    if (!tempCtx) {
         console.error("Failed to get 2D context for temporary canvas");
         // Draw an error message on the output canvas
         outputCtx.fillStyle = "grey";
         outputCtx.fillRect(0,0,width,height);
         outputCtx.fillStyle = "black";
         outputCtx.font = "16px Arial";
         outputCtx.textAlign = "center";
         outputCtx.textBaseline = "middle";
         outputCtx.fillText("Error: Could not create temp canvas context.", width/2, height/2);
         return outputCanvas;
    }

    tempCtx.drawImage(originalImg, 0, 0, width, height);
    
    let imageData;
    try {
        imageData = tempCtx.getImageData(0, 0, width, height);
    } catch (e) {
        // This commonly happens due to cross-origin issues with the image source
        console.error("Error getting image data (cross-origin issue?): ", e);
        outputCtx.fillStyle = "lightgray";
        outputCtx.fillRect(0, 0, width, height);
        outputCtx.fillStyle = "black";
        outputCtx.font = "16px Arial";
        outputCtx.textAlign = "center";
        outputCtx.textBaseline = "middle";
        const message = "Error: Could not process image.";
        const message2 = "(May be a cross-origin security restriction)";
        outputCtx.fillText(message, width / 2, height / 2 - 10);
        outputCtx.fillText(message2, width / 2, height / 2 + 10);
        return outputCanvas;
    }
    const data = imageData.data;

    // Branch logic:
    // finalStarSize === 1: Pixel manipulation optimized for 1x1 stars.
    // finalStarSize > 1: Drawing method for larger, potentially multi-pixel stars.
    if (finalStarSize === 1) {
        // Helper function to parse CSS color strings (e.g., "white", "#FFF", "rgb(255,0,0)") into RGB components
        function getColorComponents(colorString) {
            const parseCanvas = document.createElement('canvas');
            parseCanvas.width = 1;
            parseCanvas.height = 1;
            const parseCtx = parseCanvas.getContext('2d');
            if (!parseCtx) throw new Error("Could not create 2D context for color parsing");
            
            parseCtx.fillStyle = colorString;
            parseCtx.fillRect(0, 0, 1, 1);
            const [r, g, b] = parseCtx.getImageData(0, 0, 1, 1).data;
            return { r, g, b };
        }

        let starRgb, skyRgb;
        try {
            starRgb = getColorComponents(starColorStr);
            skyRgb = getColorComponents(skyColorStr);
        } catch (e) {
            console.error("Error parsing colors: ", e);
            // Fallback to default styling if color parsing fails
            outputCtx.fillStyle = "grey";
            outputCtx.fillRect(0,0,width,height);
            outputCtx.fillStyle = "black";
            outputCtx.font = "16px Arial";
            outputCtx.textAlign = "center";
            outputCtx.textBaseline = "middle";
            outputCtx.fillText("Error: Color parsing failed.", width/2, height/2);
            return outputCanvas;
        }


        const outputImageData = outputCtx.createImageData(width, height);
        const outputData = outputImageData.data;

        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]; // Original alpha, not used by default in this filter

            // Simple brightness calculation (average of RGB)
            const brightness = (r + g + b) / 3;

            if (brightness > threshold) {
                // This is a "star" pixel
                outputData[i] = starRgb.r;
                outputData[i+1] = starRgb.g;
                outputData[i+2] = starRgb.b;
                outputData[i+3] = 255; // Opaque star
            } else {
                // This is a "sky" pixel
                outputData[i] = skyRgb.r;
                outputData[i+1] = skyRgb.g;
                outputData[i+2] = skyRgb.b;
                outputData[i+3] = 255; // Opaque sky
            }
        }
        outputCtx.putImageData(outputImageData, 0, 0);

    } else { // Drawing method for stars larger than 1 pixel (finalStarSize > 1)
        // Fill background with sky color
        outputCtx.fillStyle = skyColorStr;
        outputCtx.fillRect(0, 0, width, height);

        // Set fill style for stars
        outputCtx.fillStyle = starColorStr;
        
        // For drawn shapes, Math.round() can make them visually crisper if subpixel sizes are not desired.
        const drawSize = Math.round(finalStarSize);
        const halfDrawSize = drawSize / 2;

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                const i = (y * width + x) * 4;
                const r = data[i];
                const g = data[i+1];
                const b = data[i+2];

                const brightness = (r + g + b) / 3;

                if (brightness > threshold) {
                    // Draw a star (square shape) centered at pixel (x, y)
                    // The -0.5 offset for x and y can help center rects on pixels if not using Math.round for coords
                    outputCtx.fillRect(x - halfDrawSize, y - halfDrawSize, drawSize, drawSize);
                }
            }
        }
    }

    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 Star Map Filter Effect Tool is a web-based utility that transforms images into stylized star maps. Users can upload an image and apply a star map effect by adjusting parameters such as brightness threshold, star color, sky color, and the size of the stars. This tool is ideal for creating artistic interpretations of existing photos, enhancing night sky images, or generating unique graphics for personal projects and social media. It offers a fun and creative way to visualize images with celestial aesthetics.

Leave a Reply

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