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.
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:
c
es el punto que estamos probandoz
comienza en 0z
no diverge al infinito, el punto c
pertenece al conjuntoPrimero, 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>
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:
c_re
y c_im
son las partes real e imaginaria del número complejo c
z_re
y z_im
representan el número complejo z
z
excede 2, sabemos que divergirá al infinitoNecesitamos 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 };
}
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];
}
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();
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();
});
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);
}
}
Para evitar bloquear la interfaz, usa Web Workers para cálculos pesados.
const maxIterations = Math.min(100 + Math.floor(zoom * 50), 1000);
¡Ahora tienes un renderizador completo del fractal de Mandelbrot! Experimenta con:
¡Disfruta explorando el infinito mundo del conjunto de Mandelbrot!