Please bookmark this page to avoid losing your image tool!

Image Cosmic Ray 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, numRays = 1000, raySize = 1, rayColorStr = "255,255,255") {

    // Sanitize numeric inputs
    // numRays: number of speckles. Must be non-negative integer. Default to 1000 if invalid.
    const DENSITY_NUM_RAYS = (typeof numRays === 'number' && isFinite(numRays) && numRays >= 0)
                           ? Math.floor(numRays)
                           : 1000;
    // raySize: size of each speckle in pixels. Must be positive integer (at least 1). Default to 1 if invalid.
    const PIXEL_RAY_SIZE = (typeof raySize === 'number' && isFinite(raySize) && raySize >= 1)
                         ? Math.floor(raySize)
                         : 1;

    // Ensure the image is loaded and has valid dimensions
    if (!originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        try {
            await new Promise((resolve, reject) => {
                const loadHandler = () => {
                    originalImg.removeEventListener('load', loadHandler);
                    originalImg.removeEventListener('error', errorHandler);
                    if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
                        reject(new Error("Image loaded but has zero dimensions."));
                    } else {
                        resolve();
                    }
                };
                const errorHandler = (errEvent) => {
                    originalImg.removeEventListener('load', loadHandler);
                    originalImg.removeEventListener('error', errorHandler);
                    // Try to provide a more specific error message if possible
                    const errorMsg = (typeof errEvent === 'string') ? errEvent :
                                     (errEvent && errEvent.message) ? errEvent.message :
                                     "Image failed to load for an unknown reason.";
                    reject(new Error(errorMsg));
                };

                originalImg.addEventListener('load', loadHandler);
                originalImg.addEventListener('error', errorHandler);

                // Handle cases where image might be 'complete' but errored or not yet processed by event listeners
                if (originalImg.complete) {
                    if (originalImg.naturalWidth > 0 && originalImg.naturalHeight > 0) {
                        loadHandler(); // Already loaded successfully
                    } else {
                        // Already complete but dimensions are invalid, likely an error
                        errorHandler("Image is 'complete' but has invalid dimensions or failed to load.");
                    }
                }
                // Note: If originalImg.src is not set, this Promise may never resolve or reject.
                // The caller is responsible for setting originalImg.src to initiate loading.
            });
        } catch (error) {
            console.error("Image loading failed:", error.message);
            const errorCanvas = document.createElement('canvas');
            errorCanvas.width = Math.max(250, (error.message.length * 7) + 20); // Adjust width based on message
            errorCanvas.height = 60;
            const errorCtx = errorCanvas.getContext('2d');
            if (errorCtx) {
                errorCtx.fillStyle = "#f0f0f0"; // Light gray background
                errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
                errorCtx.fillStyle = "red";
                errorCtx.font = "bold 14px Arial";
                errorCtx.textAlign = "center";
                errorCtx.fillText("Image Loading Error", errorCanvas.width / 2, 25);
                errorCtx.fillStyle = "black";
                errorCtx.font = "12px Arial";
                // Truncate long messages for display
                const displayMessage = error.message.length > 40 ? error.message.substring(0, 37) + "..." : error.message;
                errorCtx.fillText(displayMessage, errorCanvas.width / 2, 45);
            }
            return errorCanvas;
        }
    }

    const canvas = document.createElement('canvas');
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

    // This should technically be caught by the loader, but as a final safeguard:
    if (canvas.width === 0 || canvas.height === 0) {
        console.warn("Image has zero dimensions after loading. Returning an empty canvas.");
        return canvas; // Return empty 0x0 canvas
    }

    const ctx = canvas.getContext('2d');
    if (!ctx) {
        // Highly unlikely in a standard browser environment for '2d' context
        console.error("Could not get 2D context from canvas. Returning an empty canvas.");
        return canvas;
    }

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

    // If no rays are to be added, return the canvas with the original image drawn
    if (DENSITY_NUM_RAYS === 0) {
        return canvas;
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Could not get image data from canvas:", e.message);
        // This typically happens due to cross-origin security restrictions (tainted canvas)
        ctx.clearRect(0,0,canvas.width, canvas.height); // Clear original image if we can't process
        ctx.fillStyle = "rgba(100, 100, 100, 0.8)"; // Semi-transparent dark overlay
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        
        ctx.fillStyle = "white";
        ctx.textAlign = "center";
        const baseFontSize = Math.min(canvas.width, canvas.height) / 18; // Responsive font size
        ctx.font = `bold ${Math.max(12, baseFontSize)}px Arial`;
        ctx.fillText("Effect Application Error", canvas.width / 2, canvas.height / 2 - baseFontSize * 0.6);
        ctx.font = `${Math.max(10, baseFontSize * 0.7)}px Arial`;
        const errDetail = e.message.toLowerCase().includes("taint") ? "Cross-origin image security issue." : "Cannot access pixel data.";
        ctx.fillText(errDetail, canvas.width / 2, canvas.height / 2 + baseFontSize * 0.8);
        return canvas;
    }
    
    const data = imageData.data; // Uint8ClampedArray: [R, G, B, A, R, G, B, A, ...]

    // Parse rayColorStr (e.g., "255,0,128") into R, G, B components
    let parsedR = 255, parsedG = 255, parsedB = 255; // Default to white
    const colorString = String(rayColorStr || ""); // Ensure it's a string
    const colorParts = colorString.split(',');

    if (colorParts.length === 3) {
        const tempR = parseInt(colorParts[0].trim(), 10);
        const tempG = parseInt(colorParts[1].trim(), 10);
        const tempB = parseInt(colorParts[2].trim(), 10);
        if (!isNaN(tempR) && !isNaN(tempG) && !isNaN(tempB)) {
            // Values will be clamped by Uint8ClampedArray, but good to keep within 0-255 logic
            parsedR = Math.max(0, Math.min(255, tempR));
            parsedG = Math.max(0, Math.min(255, tempG));
            parsedB = Math.max(0, Math.min(255, tempB));
        } else {
            console.warn(`Invalid color components in rayColorStr: "${rayColorStr}". Using default white.`);
        }
    } else {
        console.warn(`Invalid rayColorStr format: "${rayColorStr}". Expected "R,G,B". Using default white.`);
    }

    // Determine maximum top-left coordinates for a speckle to fit fully or partially
    const maxX = Math.max(0, canvas.width - PIXEL_RAY_SIZE);
    const maxY = Math.max(0, canvas.height - PIXEL_RAY_SIZE);

    for (let i = 0; i < DENSITY_NUM_RAYS; i++) {
        // Generate random top-left coordinates for the speckle
        const x = (maxX > 0) ? Math.floor(Math.random() * (maxX + 1)) : 0; // Random int from 0 to maxX
        const y = (maxY > 0) ? Math.floor(Math.random() * (maxY + 1)) : 0; // Random int from 0 to maxY
      
        // Draw the speckle (a square of PIXEL_RAY_SIZE)
        for (let dy = 0; dy < PIXEL_RAY_SIZE; dy++) {
            const currentY = y + dy;
            if (currentY >= canvas.height) break; // Stop if speckle row goes beyond canvas bottom

            for (let dx = 0; dx < PIXEL_RAY_SIZE; dx++) {
                const currentX = x + dx;
                if (currentX >= canvas.width) break; // Stop if speckle col goes beyond canvas right

                const index = (currentY * canvas.width + currentX) * 4;
                data[index]     = parsedR;   // Red
                data[index + 1] = parsedG;   // Green
                data[index + 2] = parsedB;   // Blue
                data[index + 3] = 255;       // Alpha (fully opaque)
            }
        }
    }

    // Put the modified image data back onto the canvas
    ctx.putImageData(imageData, 0, 0);

    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 Cosmic Ray Filter Effect Tool allows users to apply a cosmic ray effect to images by adding randomly distributed speckles of specified colors and sizes. Users can customize the number of speckles, their size, and color to create unique visual effects. This tool can be useful for creating artistic images, enhancing digital artwork, or adding an interesting texture to photos, making it suitable for artists, designers, and social media enthusiasts looking to modify their visuals creatively.

Leave a Reply

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