E.05 · Código abiertoOpen sourceAtractor de LorenzLorenz attractor
ẋ = σ(y − x) · ẏ = x(ρ − z) − y · ż = xy − βz
Más de un millón de partículas que recorren el atractor de Lorenz, la forma del caos determinista.Over a million particles flowing along the Lorenz attractor, the shape of deterministic chaos.
Escena del teaserScene from the teaser
// 01La cienciaThe science
En 1963 el meteorólogo Edward Lorenz redujo un modelo de convección atmosférica a tres ecuaciones. Al repetir una simulación con un dato redondeado, el resultado cambió por completo: dos estados casi iguales terminan muy lejos. Es el efecto mariposa.
Aun así, el sistema no es azar: todas las trayectorias quedan atrapadas en la misma figura de dos alas, el atractor, y la recorren sin repetirse nunca. Es caos determinista.
In 1963 meteorologist Edward Lorenz reduced a model of atmospheric convection to three equations. Rerunning a simulation with one rounded value changed the result completely: two almost identical states end up far apart. That is the butterfly effect.
Still, the system is not random: every trajectory gets trapped in the same two-winged shape, the attractor, and travels it without ever repeating. That is deterministic chaos.
// 02Cómo se calculaHow it is computed
- Se integra una trayectoria larga del sistema con RK4 (
dt = 0,005, σ = 10, ρ = 28, β = 8/3) y se descartan los primeros 1000 pasos, para empezar ya sobre el atractor. - Quedan 153 600 puntos, cada uno con su velocidad, guardados en una textura de punto flotante en la GPU.
- Se dibujan 1,5 millones de partículas. Cada una se sienta en un punto de la trayectoria y avanza por ella a su propio ritmo, con un leve desorden para que la nube tenga volumen.
- El color sale de la velocidad del flujo: azul donde es lento y ámbar donde es rápido.
- Cada partícula tiene profundidad de campo (su tamaño es el círculo de confusión) y la luz se acumula en punto flotante antes de revelarse.
- One long trajectory is integrated with RK4 (
dt = 0.005, σ = 10, ρ = 28, β = 8/3), dropping the first 1000 steps so it starts on the attractor. - That leaves 153,600 points, each with its speed, stored in a floating-point texture on the GPU.
- 1.5 million particles are drawn. Each one sits on a point of the trajectory and moves along it at its own pace, with a little jitter so the cloud has volume.
- Color comes from the speed of the flow: blue where it is slow, amber where it is fast.
- Each particle has depth of field (its size is the circle of confusion) and light accumulates in floating point before being revealed.
// 03El códigoThe code
Este es el código que corre en el video, tal como está en el repositorio. Todo es función del tiempo: cualquier cuadro se puede calcular solo y sale igual.
This is the code that runs in the video, exactly as it is in the repository. Everything is a function of time: any frame can be computed on its own and comes out the same.
La trayectoria del sistema de Lorenz, integrada con RK4.The Lorenz trajectory, integrated with RK4.
export const TRAY_LORENZ: Float32Array = (() => { const out = new Float32Array(N_TRAY * 4); const f = (x: number, y: number, z: number) => [10 * (y - x), x * (28 - z) - y, x * y - (8 / 3) * z]; let s = [0.1, 0, 0]; const dt = 0.005; for (let i = -1000; i < N_TRAY; i++) { const k1 = f(s[0], s[1], s[2]); const k2 = f(s[0] + (dt / 2) * k1[0], s[1] + (dt / 2) * k1[1], s[2] + (dt / 2) * k1[2]); const k3 = f(s[0] + (dt / 2) * k2[0], s[1] + (dt / 2) * k2[1], s[2] + (dt / 2) * k2[2]); const k4 = f(s[0] + dt * k3[0], s[1] + dt * k3[1], s[2] + dt * k3[2]); s = s.map((v, j) => v + (dt / 6) * (k1[j] + 2 * k2[j] + 2 * k3[j] + k4[j])); if (i >= 0) { out[i * 4] = s[0]; out[i * 4 + 1] = s[1]; out[i * 4 + 2] = s[2]; out[i * 4 + 3] = Math.hypot(k1[0], k1[1], k1[2]); } } return out;})();
En la GPU: cada partícula avanza por la trayectoria y se colorea por su velocidad.On the GPU: each particle moves along the trajectory and is colored by its speed.
float fi = mod(floor(a.x * n) + uFlujo * (0.6 + 0.8 * a.w), n); int idx = int(fi); vec4 q = texelFetch(uTray, ivec2(idx % 512, idx / 512), 0); vec3 jit = (vec3(h11(a.y * 91.0), h11(a.z * 37.0), h11(a.w * 53.0)) - 0.5) * 0.9; // coordenadas de Lorenz → mundo: z hacia arriba, centrado en z = 25 vec3 p = vec3(q.x + jit.x, (q.z - 25.0) + jit.z, (q.y + jit.y) * (1.0 - uAplana)) * 0.06 * uEsc; float sp = clamp((q.w - 20.0) / 160.0, 0.0, 1.0); vec3 lento = vec3(0.12, 0.28, 0.95), medio = vec3(0.55, 0.78, 1.0), rapido = vec3(1.0, 0.86, 0.62); vCol = sp < 0.5 ? mix(lento, medio, sp * 2.0) : mix(medio, rapido, sp * 2.0 - 1.0);
// 04RecrearloRecreate it
Con Node 20 y Chrome alcanza para sacar los cuadros de esta escena. El video completo, con la canción y todas las simulaciones, se arma con los pasos del README.
Node 20 and Chrome are enough to render the frames of this scene. The full video, with the song and every simulation, is built with the steps in the README.
# 1 · clonar e instalar · clone and install $ git clone https://github.com/4mser/bn-lectures-teaser.git $ cd bn-lectures-teaser $ npm ci # 2 · cuadros sueltos de esta escena (16:9) · frames of this scene $ node herramientas/cuadros.mjs out/lorenz 1770,1830 ClaseYT # 3 · o recorrerla en Remotion Studio · or scrub it in Remotion Studio $ npm run dev