Boids flocking · mistral-small-3.2-24b-instruct
Create a full-screen flocking simulation of boids using separation, alignment, and cohesion, drawn as oriented triangles that steer smoothly and flee from the mouse cursor.
transcript · 4 messages
Create a full-screen flocking simulation of boids using separation, alignment, and cohesion, drawn as oriented triangles that steer smoothly and flee from the mouse cursor.
index.html (7374 bytes)Successfully wrote 7374 bytes to index.html
Here's a single-page web app that implements a full-screen flocking simulation of boids. The boids use separation, alignment, and cohesion behaviors, are drawn as oriented triangles, steer smoothly, and flee from the mouse cursor. The complete code is contained in index.html: ```html <!DOCTYPE html> <html> <head> <title>Boids Flocking Simulation</title> <style> body { margin: 0; overflow: hidden; background-color: #f0f0f0; } canvas { display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Set canvas to full window size canvas.width = window.innerWidth; canvas.height = window.innerHeight(); // Boid class class Boid { constructor() { this.position = { x: Math.random() * canvas.width, y: Math.random() * canvas.height }; this.velocity = { x: Math.random() * 2 - 1, y: Math.random() * 2 - 1 }; this.acceleration = { x: 0, y: 0 }; this.maxSpeed = 3; this.perceptionRadius = 50; } // Update boid position and velocity update() { this.position.x += this.velocity.x; this.position.y += this.velocity.y; this.velocity.x += this.acceleration.x; this.velocity.y += this.acceleration.y; // Limit speed const speed = Math.sqrt( this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y ); if (speed > this.maxSpeed) { this.velocity.x = ( this.velocity.x / speed ) * this.maxSpeed; this.velocity.y = ( this.velocity.y / speed ) * this.maxSpeed; } this.acceleration = { x: 0, y: 0 }; } // Draw boid as a triangle draw() { const angle = Math.atan2(this.velocity.y, this.velocity.x); ctx.beginPath(); ctx.moveTo( this.position.x + 10 * Math.cos(angle), this.position.y + 10 * Math.sin(angle) ); ctx.lineTo( this.position.x + 10 * Math.cos(angle + (2.7 * Math.PI / 3)), this.position.y + 10 * Math.sin(angle + (2.7 * Math.PI / 3)) ); ctx.lineTo( this.position.x + 10 * Math.cos(angle + (4.7 * Math.PI / 3)), this.position.y + 10 * Math.sin(angle + (4.7 * Math.PI / 3)) ); ctx.closePath(); ctx.strokeStyle = '#000000'; ctx.stroke(); ctx.fillStyle = '#000000'; ctx.fill(); } // Separation: steer to avoid crowding local boids separation(boids) { let steer = { x: 0, y: 0 }; let total = 0; for (let i = 0; i < boids.length; i++) { const d = Math.sqrt( (this.position.x - boids[i].position.x) ** 2 + (this.position.y - boids[i].position.y) ** 2 ); if (d > 0 && d < this.perceptionRadius) { const diff = { x: this.position.x - boids[i].position.x, y: this.position.y - boids[i].position.y }; steer.x += diff.x / d; steer.y += diff.y / d; total++; } } if (total > 0) { steer.x /= total; steer.y /= total; // Normalize const l = Math.sqrt(steer.x * steer.x + steer.y * steer.y); if (l > 0) { steer.x /= l; steer.y /= l; } // Apply to acceleration steer.x /= 10; steer.y /= 10; this.acceleration.x -= steer.x; this.acceleration.y -= steer.y; } } // Alignment: steer toward the average heading of local boids alignment(boids) { let avgVelocity = { x: 0, y: 0 }; let total = 0; for (let i = 0; i < boids.length; i++) { const d = Math.sqrt( (this.position.x - boids[i].position.x) ** 2 + (this.position.y - boids[i].position.y) ** 2 ); if (d > 0 && d < this.perceptionRadius) { avgVelocity.x += boids[i].velocity.x; avgVelocity.y += boids[i].velocity.y; total++; } } if (total > 0) { avgVelocity.x /= total; avgVelocity.y /= total; // Normalize const l = Math.sqrt( avgVelocity.x * avgVelocity.x + avgVelocity.y * avgVelocity.y ); if (l > 0) { avgVelocity.x /= l; avgVelocity.y /= l; } // Apply to acceleration avgVelocity.x /= 10; avgVelocity.y /= 10; this.acceleration.x += avgVelocity.x; this.acceleration.y += avgVelocity.y; } } // Cohesion: steer toward the average position of local boids cohesion(boids) { let avgPosition = { x: 0, y: 0 }; let total = 0; for (let i = 0; i < boids.length; i++) { const d = Math.sqrt( (this.position.x - boids[i].position.x) ** 2 + (this.position.y - boids[i].position.y) ** 2 ); if (d > 0 && d < this.perceptionRadius) { avgPosition.x += boids[i].position.x; avgPosition.y += boids[i].position.y; total++; } } if (total > 0) { avgPosition.x /= total; avgPosition.y /= total; // Steer toward average position const steer = { x: avgPosition.x - this.position.x, y: avgPosition.y - this.position.y }; // Normalize const l = Math.sqrt(steer.x * steer.x + steer.y * steer.y); if (l > 0) { steer.x /= l; steer.y /= l; } // Apply to acceleration steer.x /= 10; steer.y /= 10; this.acceleration.x += steer.x; this.acceleration.y += steer.y; } } // Flee from mouse cursor fleeFromMouse(mouseX, mouseY) { const dx = this.position.x - mouseX; const dy = this.position.y - mouseY; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 100) { // Normalize const steer = { x: dx / dist, y: dy / dist }; // Apply to acceleration this.acceleration.x += steer.x; this.acceleration.y += steer.y; } } } // Create boids const boids = []; for (let i = 0; i < 50; i++) { boids.push(new Boid()); } // Mouse position let mouseX = 0; let mouseY = 0; // Update canvas size on window resize window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight(); }); // Handle mouse movement window.addEventListener('mousemove', (event) => { mouseX = event.clientX; mouseY = event.clientY; }); // Animation loop function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < boids.length; i++) { boids[i].separation(boids); boids[i].alignment(boids); boids[i].cohesion(boids); boids[i].fleeFromMouse(mouseX, mouseY); boids[i].update(); boids[i].draw(); } requestAnimationFrame(animate); } // Start animation animate(); </script> </body> </html> ```
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.
RUN
EVALUATION
Blank gray screen throughout; JS error prevented boids simulation from rendering at all.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.
- window.innerHeight is not a function