Please bookmark this page to avoid losing your image tool!

Image Galaxy Background Adder

(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, galaxyCount = 10, minGalaxySize = 50, maxGalaxySize = 150, fieldStarCount = 500) {

    // Ensure parameters are numbers
    const numGalaxyCount = Number(galaxyCount);
    const numMinGalaxySize = Number(minGalaxySize);
    const numMaxGalaxySize = Number(maxGalaxySize);
    const numFieldStarCount = Number(fieldStarCount);

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

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

    // --- Helper function to draw a single galaxy ---
    const drawGalaxy = (x, y, size) => {
        const coreRadius = size * (0.05 + Math.random() * 0.05);
        const starCount = Math.floor(size * 1.5);
        const armCount = 2 + Math.floor(Math.random() * 3); // 2 to 4 arms
        const spiralTightness = 5 + Math.random() * 5;

        // 1. Draw core glow
        const coreGradient = ctx.createRadialGradient(x, y, 0, x, y, coreRadius * 2);
        coreGradient.addColorStop(0, `rgba(255, 255, 230, 0.95)`);
        coreGradient.addColorStop(0.5, `rgba(220, 220, 255, 0.5)`);
        coreGradient.addColorStop(1, `rgba(200, 200, 255, 0)`);
        ctx.fillStyle = coreGradient;
        ctx.beginPath();
        ctx.arc(x, y, coreRadius * 2.5, 0, Math.PI * 2);
        ctx.fill();

        // 2. Draw stars in spiral arms
        for (let i = 0; i < starCount; i++) {
            // Use a power distribution to cluster stars near the center
            const distance = Math.pow(Math.random(), 2) * (size / 2);
            const angle = Math.random() * Math.PI * 2;
            
            // Define spiral arms
            const arm = Math.floor(angle * armCount / (Math.PI * 2));
            const armAngle = (arm / armCount) * Math.PI * 2;
            const spiralOffset = distance / spiralTightness;
            const fuzziness = (Math.random() - 0.5) * (Math.PI / armCount) * 0.7;

            const finalAngle = armAngle + spiralOffset + fuzziness;

            const starX = x + Math.cos(finalAngle) * distance;
            const starY = y + Math.sin(finalAngle) * distance;
            
            const starSize = Math.random() * 1.5 + 0.5;
            const starOpacity = 0.5 + Math.random() * 0.5;
            
            const r = 220 + Math.random() * 35;
            const g = 220 + Math.random() * 35;
            const b = 240 + Math.random() * 15;
            
            ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${starOpacity})`;
            ctx.beginPath();
            ctx.arc(starX, starY, starSize, 0, Math.PI * 2);
            ctx.fill();
        }
    };

    // --- Step 1: Create a dark space background ---
    const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
    bgGradient.addColorStop(0, '#000010');
    bgGradient.addColorStop(1, '#0c0c24');
    ctx.fillStyle = bgGradient;
    ctx.fillRect(0, 0, width, height);

    // --- Step 2: Draw distant field stars ---
    for (let i = 0; i < numFieldStarCount; i++) {
        const x = Math.random() * width;
        const y = Math.random() * height;
        const size = Math.random() * 1.5 + 0.2;
        const alpha = Math.random() * 0.5 + 0.1;

        ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
        ctx.beginPath();
        ctx.arc(x, y, size, 0, Math.PI * 2);
        ctx.fill();
    }

    // --- Step 3: Analyze original image to find empty space ---
    const MASK_GRID_SCALE = 20;
    const maskWidth = Math.ceil(width / MASK_GRID_SCALE);
    const maskHeight = Math.ceil(height / MASK_GRID_SCALE);
    const occupiedMask = new Array(maskWidth * maskHeight).fill(false);

    const maskCanvas = document.createElement('canvas');
    maskCanvas.width = width;
    maskCanvas.height = height;
    const maskCtx = maskCanvas.getContext('2d');
    maskCtx.drawImage(originalImg, 0, 0);
    const imageData = maskCtx.getImageData(0, 0, width, height);
    const data = imageData.data;

    for (let i = 0; i < data.length; i += 4) {
        const alpha = data[i + 3];
        if (alpha > 50) { // Threshold for considering a pixel "occupied"
            const pixelIndex = i / 4;
            const x = pixelIndex % width;
            const y = Math.floor(pixelIndex / width);
            const maskX = Math.floor(x / MASK_GRID_SCALE);
            const maskY = Math.floor(y / MASK_GRID_SCALE);
            occupiedMask[maskY * maskWidth + maskX] = true;
        }
    }
    
    const isOccupied = (x, y, radius) => {
        const startX = Math.floor((x - radius) / MASK_GRID_SCALE);
        const endX = Math.ceil((x + radius) / MASK_GRID_SCALE);
        const startY = Math.floor((y - radius) / MASK_GRID_SCALE);
        const endY = Math.ceil((y + radius) / MASK_GRID_SCALE);

        for (let my = Math.max(0, startY); my < Math.min(maskHeight, endY); my++) {
            for (let mx = Math.max(0, startX); mx < Math.min(maskWidth, endX); mx++) {
                if (occupiedMask[my * maskWidth + mx]) {
                    return true;
                }
            }
        }
        return false;
    };

    // --- Step 4: Place galaxies in non-occupied, non-overlapping spaces ---
    const galaxies = [];
    for (let i = 0; i < numGalaxyCount; i++) {
        let tries = 0;
        const maxTries = 200;
        while (tries < maxTries) {
            tries++;
            const size = numMinGalaxySize + Math.random() * (numMaxGalaxySize - numMinGalaxySize);
            const radius = size / 2;
            const x = Math.random() * width;
            const y = Math.random() * height;

            if (isOccupied(x, y, radius)) {
                continue;
            }

            let overlapsOtherGalaxy = false;
            for (const g of galaxies) {
                const dx = x - g.x;
                const dy = y - g.y;
                const dist = Math.sqrt(dx * dx + dy * dy);
                if (dist < radius + g.radius) {
                    overlapsOtherGalaxy = true;
                    break;
                }
            }
            if (overlapsOtherGalaxy) {
                 continue;
            }

            // If we've reached here, it's a valid spot
            drawGalaxy(x, y, size);
            galaxies.push({ x, y, radius });
            break;
        }
    }

    // --- Step 5: Draw the original image on top of the generated background ---
    ctx.drawImage(originalImg, 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 Galaxy Background Adder is a tool that allows users to enhance their images by adding a stunning galaxy-themed background. Users can specify the number and size of galaxies, as well as the quantity of field stars to be integrated into the image. This tool is particularly useful for graphic designers, photographers, or anyone looking to create visually striking images that incorporate a cosmic aesthetic. Utilize it to create artistic portraits, themed promotional materials, or personalized artwork that captivates viewers with celestial visuals.

Leave a Reply

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