Lorenz attractor · mercury-2
Using the pre-provided three.min.js (global THREE), visualize the Lorenz attractor as a glowing 3D butterfly trajectory that draws over time, with orbit-drag camera controls.
transcript · 10 messages
Using the pre-provided three.min.js (global THREE), visualize the Lorenz attractor as a glowing 3D butterfly trajectory that draws over time, with orbit-drag camera controls.
index.html (3036 bytes)Successfully wrote 3036 bytes to index.html
{"path":"index.html","offset":1,"limit":2000}<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Lorenz Attractor 3D</title> <style> body { margin:0; overflow:hidden; background:#000; } canvas { display:block; } </style> </head> <body> <script src="three.min.js"></script> <script> // Setup scene const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(30, 30, 30); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); renderer.setClearColor(0x000000); renderer.autoClear = false; document.body.appendChild(renderer.domElement); // Orbit controls (if available) if (THREE.OrbitControls) { const controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; } // Lorenz parameters const sigma = 10; const rho = 28; const beta = 8 / 3; // Geometry for the trajectory const maxPoints = 20000; // limit const positions = new Float32Array(maxPoints * 3); const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setDrawRange(0, 0); const material = new THREE.LineBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.7, blending: THREE.AdditiveBlending, linewidth: 2, }); const line = new THREE.Line(geometry, material); scene.add(line); // Initial state let x = 0.1, y = 0, z = 0; let index = 0; const dt = 0.005; function lorenzStep() { const dx = sigma * (y - x) * dt; const dy = (x * (rho - z) - y) * dt; const dz = (x * y - beta * z) * dt; x += dx; y += dy; z += dz; } function animate() { requestAnimationFrame(animate); // Add several points per frame for smoother line for (let i = 0; i < 5; i++) { lorenzStep(); if (index < maxPoints) { positions[index * 3] = x; positions[index * 3 + 1] = y; positions[index * 3 + 2] = z; index++; geometry.setDrawRange(0, index); geometry.attributes.position.needsUpdate = true; } else { // shift buffer left when full positions.copyWithin(0, 3); positions[(maxPoints - 1) * 3] = x; positions[(maxPoints - 1) * 3 + 1] = y; positions[(maxPoints - 1) * 3 + 2] = z; geometry.attributes.position.needsUpdate = true; } } if (controls) controls.update(); renderer.clear(); renderer.render(scene, camera); } animate(); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html>
{"path":"index.html","edits":[{"oldText":" // Orbit controls (if available)\n if (THREE.OrbitControls) {\n const controls = new TSuccessfully replaced 2 block(s) in index.html.
{"path":"index.html","offset":1,"limit":2000}<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Lorenz Attractor 3D</title> <style> body { margin:0; overflow:hidden; background:#000; } canvas { display:block; } </style> </head> <body> <script src="three.min.js"></script> <script> // Setup scene const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(30, 30, 30); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); renderer.setClearColor(0x000000); renderer.autoClear = false; document.body.appendChild(renderer.domElement); // Simple orbit-drag controls let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; const rotateSpeed = 0.005; renderer.domElement.addEventListener('mousedown', function(e) { isDragging = true; previousMousePosition = { x: e.clientX, y: e.clientY }; }); renderer.domElement.addEventListener('mousemove', function(e) { if (!isDragging) return; const deltaMove = { x: e.clientX - previousMousePosition.x, y: e.clientY - previousMousePosition.y }; const offset = new THREE.Vector3(); offset.copy(camera.position).sub(scene.position); const spherical = new THREE.Spherical(); spherical.setFromVector3(offset); spherical.theta -= deltaMove.x * rotateSpeed; spherical.phi -= deltaMove.y * rotateSpeed; spherical.phi = Math.max(0.01, Math.min(Math.PI - 0.01, spherical.phi)); offset.setFromSpherical(spherical); camera.position.copy(scene.position).add(offset); camera.lookAt(scene.position); previousMousePosition = { x: e.clientX, y: e.clientY }; }); renderer.domElement.addEventListener('mouseup', function() { isDragging = false; }); renderer.domElement.addEventListener('mouseleave', function() { isDragging = false; }); // Lorenz parameters const sigma = 10; const rho = 28; const beta = 8 / 3; // Geometry for the trajectory const maxPoints = 20000; // limit const positions = new Float32Array(maxPoints * 3); const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setDrawRange(0, 0); const material = new THREE.LineBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.7, blending: THREE.AdditiveBlending, linewidth: 2, }); const line = new THREE.Line(geometry, material); scene.add(line); // Initial state let x = 0.1, y = 0, z = 0; let index = 0; const dt = 0.005; function lorenzStep() { const dx = sigma * (y - x) * dt; const dy = (x * (rho - z) - y) * dt; const dz = (x * y - beta * z) * dt; x += dx; y += dy; z += dz; } function animate() { requestAnimationFrame(animate); // Add several points per frame for smoother line for (let i = 0; i < 5; i++) { lorenzStep(); if (index < maxPoints) { positions[index * 3] = x; positions[index * 3 + 1] = y; positions[index * 3 + 2] = z; index++; geometry.setDrawRange(0, index); geometry.attributes.position.needsUpdate = true; } else { // shift buffer left when full positions.copyWithin(0, 3); positions[(maxPoints - 1) * 3] = x; positions[(maxPoints - 1) * 3 + 1] = y; positions[(maxPoints - 1) * 3 + 2] = z; geometry.attributes.position.needsUpdate = true; } } // no external controls; manual drag handled above renderer.clear(); renderer.render(scene, camera); } animate(); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); </script> </body> </html>
The **index.html** file has been created (or updated) in the current directory. It contains a self‑contained web app that: * Loads the provided `three.min.js` (global `THREE`). * Sets up a scene, camera, and renderer. * Implements a custom **orbit‑drag** control (click‑drag to rotate the view). * Computes the Lorenz attractor in real time and draws its trajectory as an additive‑blended line, giving a glowing “butterfly” appearance. * Handles window resizing. You can open `index.html` in a browser (no network access required) to see the 3D Lorenz attractor visualized with the requested drag controls.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.