chaos theory
LORENZ ATTRACTOR
Real-time 3D visualization of deterministic chaos.
Parameters
10.00
28.00
2.67
8000
1.0x
Lorenz Equations
dx/dt = σ(y − x)
dy/dt = x(ρ − z) − y
dz/dt = xy − βz
Live Stats
x:
0.0000
y:
0.0000
z:
0.0000
Status:
chaotic
Iterations:
0
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
// ===== STATE =====
const state = {
sigma: 10,
rho: 28,
beta: 8/3,
trailLength: 8000,
speed: 1,
paused: false,
iteration: 0,
x: 0.1,
y: 0,
z: 0,
};
// ===== PARAMETERS & CONTROLS =====
const sigmaSlider = document.getElementById('sigma-slider');
const rhoSlider = document.getElementById('rho-slider');
const betaSlider = document.getElementById('beta-slider');
const trailSlider = document.getElementById('trail-slider');
const speedSlider = document.getElementById('speed-slider');
sigmaSlider.addEventListener('input', (e) => {
state.sigma = parseFloat(e.target.value);
document.getElementById('sigma-value').textContent = state.sigma.toFixed(2);
});
rhoSlider.addEventListener('input', (e) => {
state.rho = parseFloat(e.target.value);
document.getElementById('rho-value').textContent = state.rho.toFixed(2);
updateStatusIndicator();
});
betaSlider.addEventListener('input', (e) => {
state.beta = parseFloat(e.target.value);
document.getElementById('beta-value').textContent = state.beta.toFixed(2);
});
trailSlider.addEventListener('input', (e) => {
state.trailLength = parseInt(e.target.value);
document.getElementById('trail-value').textContent = state.trailLength;
});
speedSlider.addEventListener('input', (e) => {
state.speed = parseFloat(e.target.value);
document.getElementById('speed-value').textContent = state.speed.toFixed(1) + 'x';
});
// ===== BUTTONS =====
document.getElementById('reset-btn').addEventListener('click', () => {
state.x = 0.1;
state.y = 0;
state.z = 0;
state.iteration = 0;
trailBuffer = [];
updateStats();
});
document.getElementById('pause-btn').addEventListener('click', (e) => {
state.paused = !state.paused;
e.target.textContent = state.paused ? 'Resume' : 'Pause';
});
document.getElementById('random-btn').addEventListener('click', () => {
state.x = (Math.random() - 0.5) * 10;
state.y = (Math.random() - 0.5) * 10;
state.z = Math.random() * 40;
state.iteration = 0;
trailBuffer = [];
updateStats();
});
function updateStatusIndicator() {
const indicator = document.getElementById('status-indicator');
const text = document.getElementById('status-text');
if (state.rho > 24.74) {
indicator.className = 'status-indicator status-chaotic';
text.textContent = 'chaotic';
} else {
indicator.className = 'status-indicator status-stable';
text.textContent = 'stable';
}
}
function updateStats() {
document.getElementById('stat-x').textContent = state.x.toFixed(4);
document.getElementById('stat-y').textContent = state.y.toFixed(4);
document.getElementById('stat-z').textContent = state.z.toFixed(4);
document.getElementById('stat-iter').textContent = state.iteration.toLocaleString();
}
// ===== THREE.JS SCENE =====
const canvas = document.getElementById('canvas');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(
75,
canvas.clientWidth / canvas.clientHeight,
0.1,
1000
);
camera.position.set(30, 30, 40);
camera.lookAt(0, 0, 20);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
// Add subtle lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
// Trail buffer (circular)
let trailBuffer = [];
let trailGeometry = null;
let trailLine = null;
let glowLine = null;
function createTrailGeometry() {
if (trailLine) scene.remove(trailLine);
if (glowLine) scene.remove(glowLine);
const positions = new Float32Array(state.trailLength * 3);
trailGeometry = new THREE.BufferGeometry();
trailGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
trailGeometry.setDrawRange(0, 0);
// Main trail with gradient
const trailMaterial = new THREE.LineBasicMaterial({
color: 0xff2200,
linewidth: 1,
vertexColors: true,
});
trailLine = new THREE.Line(trailGeometry, trailMaterial);
scene.add(trailLine);
// Glow effect (thicker, semi-transparent)
const glowGeometry = new THREE.BufferGeometry();
glowGeometry.setAttribute('position', new THREE.BufferAttribute(positions.slice(), 3));
const glowMaterial = new THREE.LineBasicMaterial({
color: 0xff2200,
linewidth: 3,
transparent: true,
opacity: 0.15,
vertexColors: false,
});
glowLine = new THREE.Line(glowGeometry, glowMaterial);
scene.add(glowLine);
}
createTrailGeometry();
// ===== RUNGE-KUTTA 4 INTEGRATION =====
function lorenzDerivative(x, y, z) {
const dx = state.sigma * (y - x);
const dy = x * (state.rho - z) - y;
const dz = x * y - state.beta * z;
return { dx, dy, dz };
}
function rk4Step(x, y, z, dt) {
// k1
let d1 = lorenzDerivative(x, y, z);
// k2
let d2 = lorenzDerivative(x + 0.5*dt*d1.dx, y + 0.5*dt*d1.dy, z + 0.5*dt*d1.dz);
// k3
let d3 = lorenzDerivative(x + 0.5*dt*d2.dx, y + 0.5*dt*d2.dy, z + 0.5*dt*d2.dz);
// k4
let d4 = lorenzDerivative(x + dt*d3.dx, y + dt*d3.dy, z + dt*d3.dz);
const newX = x + (dt/6) * (d1.dx + 2*d2.dx + 2*d3.dx + d4.dx);
const newY = y + (dt/6) * (d1.dy + 2*d2.dy + 2*d3.dy + d4.dy);
const newZ = z + (dt/6) * (d1.dz + 2*d2.dz + 2*d3.dz + d4.dz);
return { x: newX, y: newY, z: newZ };
}
// ===== ANIMATION LOOP =====
let mouseDown = false;
let mouseX = 0, mouseY = 0;
let cameraAzimuth = 0.927; // radians
let cameraElevation = 0.785; // radians
const cameraDistance = 50;
canvas.addEventListener('mousedown', (e) => {
mouseDown = true;
mouseX = e.clientX;
mouseY = e.clientY;
});
canvas.addEventListener('mousemove', (e) => {
if (mouseDown) {
const dx = e.clientX - mouseX;
const dy = e.clientY - mouseY;
cameraAzimuth += dx * 0.005;
cameraElevation += dy * 0.005;
cameraElevation = Math.max(-Math.PI/2 + 0.1, Math.min(Math.PI/2 - 0.1, cameraElevation));
mouseX = e.clientX;
mouseY = e.clientY;
}
});
canvas.addEventListener('mouseup', () => {
mouseDown = false;
});
canvas.addEventListener('mouseleave', () => {
mouseDown = false;
});
function animate() {
requestAnimationFrame(animate);
// Update simulation
if (!state.paused) {
const dt = 0.005;
for (let i = 0; i < state.speed; i++) {
const result = rk4Step(state.x, state.y, state.z, dt);
state.x = result.x;
state.y = result.y;
state.z = result.z;
state.iteration++;
// Add to trail
trailBuffer.push([state.x, state.y, state.z]);
if (trailBuffer.length > state.trailLength) {
trailBuffer.shift();
}
}
}
// Update trail geometry
const positions = trailGeometry.attributes.position.array;
for (let i = 0; i < trailBuffer.length; i++) {
positions[i*3] = trailBuffer[i][0];
positions[i*3 + 1] = trailBuffer[i][1];
positions[i*3 + 2] = trailBuffer[i][2];
}
trailGeometry.attributes.position.needsUpdate = true;
trailGeometry.setDrawRange(0, trailBuffer.length);
// Update colors with gradient (oldest = dark, newest = bright)
if (!trailGeometry.attributes.color) {
const colors = new Float32Array(state.trailLength * 3);
trailGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
}
const colors = trailGeometry.attributes.color.array;
for (let i = 0; i < trailBuffer.length; i++) {
const t = i / Math.max(1, trailBuffer.length - 1); // 0 to 1
// Gradient: red (#ff2200) -> orange -> yellow
let r, g, b;
if (t < 0.5) {
// Red to orange
const lerp = t * 2;
r = 1.0;
g = lerp * 0.65; // 0 -> 165/255
b = 0;
} else {
// Orange to yellow
const lerp = (t - 0.5) * 2;
r = 1.0;
g = 0.65 + lerp * 0.35; // 0.65 -> 1.0
b = 0;
}
colors[i*3] = r;
colors[i*3 + 1] = g;
colors[i*3 + 2] = b;
}
trailGeometry.attributes.color.needsUpdate = true;
// Update camera position (auto-rotate + mouse orbit)
if (!mouseDown) {
cameraAzimuth += 0.0003; // Slow auto-rotate
}
const x = cameraDistance * Math.cos(cameraElevation) * Math.cos(cameraAzimuth);
const y = cameraDistance * Math.sin(cameraElevation);
const z = cameraDistance * Math.cos(cameraElevation) * Math.sin(cameraAzimuth);
camera.position.set(x, y, z);
camera.lookAt(0, 10, 0);
// Update glow geometry
if (glowLine) {
const glowPositions = glowLine.geometry.attributes.position.array;
for (let i = 0; i < trailBuffer.length; i++) {
glowPositions[i*3] = trailBuffer[i][0];
glowPositions[i*3 + 1] = trailBuffer[i][1];
glowPositions[i*3 + 2] = trailBuffer[i][2];
}
glowLine.geometry.attributes.position.needsUpdate = true;
glowLine.geometry.setDrawRange(0, trailBuffer.length);
}
// Update stats
updateStats();
renderer.render(scene, camera);
}
animate();
// ===== RESPONSIVE =====
window.addEventListener('resize', () => {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
updateStatusIndicator();