Please bookmark this page to avoid losing your image tool!

Image Fractal Mandelbrot Filter Application

(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, maxIterations = 100, zoom = 1.0, centerX = -0.7, centerY = 0.0, colorPaletteStr = "hsv_cycle", blendMode = "multiply") {

    const W = originalImg.width;
    const H = originalImg.height;

    // Ensure width and height are valid
    if (W === 0 || H === 0) {
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = W;
        emptyCanvas.height = H;
        console.warn("Original image has zero width or height.");
        return emptyCanvas;
    }
    
    // Ensure zoom is positive
    if (zoom <= 0) {
        zoom = 1.0; // Default to 1.0 if zoom is invalid
        console.warn("Zoom must be positive. Resetting to 1.0.");
    }
    if (maxIterations <= 0) {
        maxIterations = 1; // Ensure at least one iteration
    }


    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = W;
    outputCanvas.height = H;
    const outCtx = outputCanvas.getContext('2d');

    // Draw the original image first onto the output canvas
    outCtx.drawImage(originalImg, 0, 0, W, H);

    // Create ImageData for the fractal
    const fractalImageData = outCtx.createImageData(W, H);
    
    // Complex plane parameters:
    // Standard Mandelbrot view is roughly Real:[-2.5, 1.0], Imaginary:[-1.0, 1.0] (Width 3.5, Height 2.0)
    // Or more tightly, Real:[-2.0, 1.0], Imaginary:[-1.0, 1.0] (Width 3.0, Height 2.0)
    // We'll use the 3.0x2.0 base view.
    // Adjust the view window to match canvas aspect ratio, centered at (centerX, centerY) and scaled by zoom.
    let realRange, imaginaryRange;
    const canvasAspectRatio = W / H;
    const fractalBaseAspectRatio = 3.0 / 2.0; 

    if (canvasAspectRatio > fractalBaseAspectRatio) {
        // Canvas is wider than the base fractal view. Fit height, expand width.
        imaginaryRange = 2.0 / zoom;
        realRange = imaginaryRange * canvasAspectRatio;
    } else {
        // Canvas is taller or has the same aspect ratio. Fit width, expand height.
        realRange = 3.0 / zoom;
        imaginaryRange = realRange / canvasAspectRatio;
    }
    
    const realMin = centerX - realRange / 2.0;
    // Canvas Y=0 is top row. Complex Im values usually increase upwards.
    // So, map canvas row 0 to top of complex window (imaginaryMax).
    const imaginaryMax = centerY + imaginaryRange / 2.0; 

    // Helper function for HSV to RGB conversion (defined inside to keep the function self-contained)
    function HSVtoRGB(h, s, v) {
        let r, g, b, i, f, p, q, t;
        if (s === 0) {
            r = g = b = v; // achromatic
        } else {
            i = Math.floor(h * 6);
            f = h * 6 - i;
            p = v * (1 - s);
            q = v * (1 - f * s);
            t = v * (1 - (1 - f) * s);
            switch (i % 6) {
                case 0: r = v; g = t; b = p; break;
                case 1: r = q; g = v; b = p; break;
                case 2: r = p; g = v; b = t; break;
                case 3: r = p; g = q; b = v; break;
                case 4: r = t; g = p; b = v; break;
                case 5: r = v; g = p; b = q; break;
                default: r = 0; g = 0; b = 0; // Should not happen
            }
        }
        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }

    // Mandelbrot calculation loop
    for (let r_px = 0; r_px < H; r_px++) { // Current row in pixels
        for (let c_px = 0; c_px < W; c_px++) { // Current column in pixels
            // Map pixel coordinate to complex number c = cr + i * ci
            const cr = realMin + (c_px / W) * realRange;
            const ci = imaginaryMax - (r_px / H) * imaginaryRange; // Y-axis inversion for complex plane

            let zr = 0; // Real part of z
            let zi = 0; // Imaginary part of z
            let zr2 = 0; // zr^2
            let zi2 = 0; // zi^2
            let n = 0; // Iteration count

            // Iterate z_{k+1} = z_k^2 + c
            for (; n < maxIterations; n++) {
                // z_i_new = 2*z_r*z_i + c_i
                // z_r_new = z_r^2 - z_i^2 + c_r
                // Important: use z_r from _this_ iteration for z_i calculation before z_r is updated.
                // zr2 and zi2 are from the *previous* iteration state (z_k)^2
                zi = 2 * zr * zi + ci; 
                zr = zr2 - zi2 + cr;   
                
                // Update squares for next iteration's escape check and calculation
                zr2 = zr * zr;
                zi2 = zi * zi;

                // Check escape condition: |z|^2 > 4 (escape radius 2)
                if (zr2 + zi2 > 4) {
                    break;
                }
            }

            const pixelIdx = (r_px * W + c_px) * 4;
            let r_col = 0, g_col = 0, b_col = 0; // Default to black (inside the set)

            if (n < maxIterations) { // Point escaped (outside the set)
                // Smooth coloring based on n and final |z|
                // nu = log_2(log_escape_radius(|Z_n|))
                const log_zn = Math.log(Math.sqrt(zr2 + zi2)); // log(|Z_n|)
                const nu = Math.log(log_zn / Math.log(2)) / Math.log(2); // log_2(log_base_2(|Z_n|))
                
                let smooth_n = n + 1 - nu;
                if (smooth_n < 0 || !isFinite(smooth_n)) { // handles NaN/Infinity from log args
                     smooth_n = 0; 
                }


                switch (colorPaletteStr.toLowerCase()) {
                    case "hsv_cycle":
                        const hue_factor = 0.05; // Controls frequency of color cycling
                        const hue = (smooth_n * hue_factor) % 1.0;
                        [r_col, g_col, b_col] = HSVtoRGB(hue, 1.0, 1.0);
                        break;
                    case "grayscale":
                        // Map smooth_n to a grayscale value. Normalize by maxIterations or a fraction of it.
                        let gray_val = (smooth_n / (maxIterations * 0.3)) % 1.0; // Modulo for cycling effect
                        gray_val = Math.min(1.0, gray_val); // Clamp
                        const gray = Math.floor(gray_val * 255);
                        r_col = g_col = b_col = gray;
                        break;
                    default: // Fallback to hsv_cycle if palette name is unknown
                        const default_hue_factor = 0.05;
                        const default_hue = (smooth_n * default_hue_factor) % 1.0;
                        [r_col, g_col, b_col] = HSVtoRGB(default_hue, 1.0, 1.0);
                        break;
                }
            }
            // Else (n == maxIterations), point is in the set. Color remains black.

            fractalImageData.data[pixelIdx]     = r_col;
            fractalImageData.data[pixelIdx + 1] = g_col;
            fractalImageData.data[pixelIdx + 2] = b_col;
            fractalImageData.data[pixelIdx + 3] = 255; // Fully opaque
        }
    }

    // To blend, draw the fractalData onto a temporary canvas, then draw that canvas onto the outputCanvas.
    const fractalCanvas = document.createElement('canvas');
    fractalCanvas.width = W;
    fractalCanvas.height = H;
    const fractalCtx = fractalCanvas.getContext('2d');
    fractalCtx.putImageData(fractalImageData, 0, 0);

    // Apply blend mode
    outCtx.globalCompositeOperation = blendMode;
    outCtx.drawImage(fractalCanvas, 0, 0);
    
    // Optional: Reset globalCompositeOperation if outCtx might be used further by caller
    // outCtx.globalCompositeOperation = 'source-over'; 

    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 Fractal Mandelbrot Filter Application allows users to generate stunning Mandelbrot fractal visuals applied to existing images. This tool enables customization such as adjusting the zoom level, changing the center point of the fractal, and selecting from various color palettes. It is particularly useful for artists, designers, and digital creators looking to enhance their images with unique fractal effects, making it ideal for digital art projects, graphic design, and creative visual experimentation.

Leave a Reply

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