Haz clic en la imagen para acercar


Tutorial: Cómo Dibujar el Fractal de Mandelbrot en JavaScript Puro

Introducción

El conjunto de Mandelbrot es uno de los fractales más famosos y hermosos de las matemáticas. En este tutorial, aprenderás a dibujarlo usando JavaScript puro y el elemento Canvas de HTML.

¿Qué es el Conjunto de Mandelbrot?

El conjunto de Mandelbrot es un conjunto de números complejos que produce un patrón fractal infinitamente complejo. Para cada punto en el plano complejo, aplicamos repetidamente la fórmula:

z(n+1) = z(n)² + c

Donde:

Paso 1: Configurar el HTML Básico

Primero, necesitamos un elemento canvas donde dibujar:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Fractal de Mandelbrot</title>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: #000;
        }
        canvas {
            border: 2px solid #333;
        }
    </style>
</head>
<body>
    <canvas id="mandelbrot"></canvas>
    <script src="mandelbrot.js"></script>
</body>
</html>

Paso 2: Implementar la Función del Conjunto de Mandelbrot

Esta función determina si un punto pertenece al conjunto:

function mandelbrot(c_re, c_im, maxIterations) {
    let z_re = 0;
    let z_im = 0;

    for (let i = 0; i < maxIterations; i++) {
        // Calcular z² + c
        const z_re_temp = z_re * z_re - z_im * z_im + c_re;
        z_im = 2 * z_re * z_im + c_im;
        z_re = z_re_temp;

        // Verificar si diverge (si el módulo es mayor que 2)
        if (z_re * z_re + z_im * z_im > 4) {
            return i; // Retorna el número de iteraciones
        }
    }

    return maxIterations; // El punto está en el conjunto
}

Explicación:

Paso 3: Mapear Píxeles a Coordenadas Complejas

Necesitamos convertir las coordenadas de píxeles del canvas a números complejos:

function pixelToComplex(x, y, width, height, zoom, centerX, centerY) {
    // Rango típico: [-2.5, 1] en x, [-1, 1] en y
    const minRe = centerX - (2.0 / zoom);
    const maxRe = centerX + (1.0 / zoom);
    const minIm = centerY - (1.5 / zoom);
    const maxIm = centerY + (1.5 / zoom);

    const re = minRe + (x / width) * (maxRe - minRe);
    const im = minIm + (y / height) * (maxIm - minIm);

    return { re, im };
}

Paso 4: Crear la Función de Color

Asignamos colores basados en el número de iteraciones:

function getColor(iterations, maxIterations) {
    if (iterations === maxIterations) {
        return [0, 0, 0]; // Negro para puntos en el conjunto
    }

    // Crear un gradiente de color
    const t = iterations / maxIterations;
    const r = Math.floor(9 * (1 - t) * t * t * t * 255);
    const g = Math.floor(15 * (1 - t) * (1 - t) * t * t * 255);
    const b = Math.floor(8.5 * (1 - t) * (1 - t) * (1 - t) * t * 255);

    return [r, g, b];
}

Paso 5: Dibujar el Fractal Completo

Ahora juntamos todo:

const canvas = document.getElementById('mandelbrot');
const ctx = canvas.getContext('2d');

// Configuración
const width = canvas.width = 800;
const height = canvas.height = 600;
const maxIterations = 100;
let zoom = 1;
let centerX = -0.5;
let centerY = 0;

function drawMandelbrot() {
    const imageData = ctx.createImageData(width, height);
    const data = imageData.data;

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            // Convertir píxel a número complejo
            const { re, im } = pixelToComplex(x, y, width, height, zoom, centerX, centerY);

            // Calcular iteraciones
            const iterations = mandelbrot(re, im, maxIterations);

            // Obtener color
            const [r, g, b] = getColor(iterations, maxIterations);

            // Establecer color del píxel
            const index = (y * width + x) * 4;
            data[index] = r;
            data[index + 1] = g;
            data[index + 2] = b;
            data[index + 3] = 255; // Alpha
        }
    }

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

// Dibujar el fractal
drawMandelbrot();

Paso 6: Añadir Interactividad (Opcional)

Permite hacer zoom con clics:

canvas.addEventListener('click', (e) => {
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    // Actualizar centro al punto clicado
    const { re, im } = pixelToComplex(x, y, width, height, zoom, centerX, centerY);
    centerX = re;
    centerY = im;

    // Aumentar zoom
    zoom *= 2;

    // Redibujar
    drawMandelbrot();
});

Optimizaciones

1. Renderizado Progresivo

Para fractales grandes, dibuja en pasadas múltiples:

function drawProgressive(step = 4) {
    // Primera pasada: cada 4 píxeles
    for (let y = 0; y < height; y += step) {
        for (let x = 0; x < width; x += step) {
            // Calcular y dibujar...
        }
    }

    // Luego reducir el paso progresivamente
    if (step > 1) {
        setTimeout(() => drawProgressive(step / 2), 0);
    }
}

2. Web Workers

Para evitar bloquear la interfaz, usa Web Workers para cálculos pesados.

3. Aumentar Iteraciones con Zoom

const maxIterations = Math.min(100 + Math.floor(zoom * 50), 1000);

Resultado Final

¡Ahora tienes un renderizador completo del fractal de Mandelbrot! Experimenta con:

Puntos Interesantes para Explorar

¡Disfruta explorando el infinito mundo del conjunto de Mandelbrot!