Boids flocking · north-mini-code
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 · 6 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.
thinking
The user wants a full-screen flocking simulation of boids with the following requirements: 1. Separation, alignment, and cohesion rules 2. Drawn as oriented triangles 3. Smooth steering 4. Flee from mouse cursor 5. All in a single self-contained HTML file I need to create an HTML file that includes: - CSS in a <style> tag - JavaScript in a <script> tag - No external resources or network requests - Full-screen simulation Let me create this step by step: 1. First, I'll create the basic HTML structure 2. Add CSS for styling (full-screen, canvas, triangles) 3. Add JavaScript with Boid class and flocking logic 4. Add mouse interaction for flee behavior 5. Add animation loop Let me start by creating the index.html file.
index.html (8613 bytes)Successfully wrote 8613 bytes to index.html
thinking
The user wants me to open this file to verify the current structure. Let me use the read function to see what's currently in the index.html file.
{"path":"index.html"}<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flocking Simulation</title> <style> body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #000; } canvas { display: block; width: 100%; height: 100%; } #info { position: absolute; top: 10px; left: 10px; color: white; font-family: monospace; font-size: 14px; pointer-events: none; } </style> </head> <body> <div id="info">Flocking Simulation</div> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let width, height; // Reset canvas size function resize() { width = canvas.width = window.innerWidth; height = canvas.height = window.innerHeight; } window.addEventListener('resize', resize); // Mouse position const mouse = { x: 0, y: 0, radius: 150 }; canvas.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; }); // Boid class class Boid { constructor() { this.x = Math.random() * width; this.y = Math.random() * height; this.vx = (Math.random() - 0.5) * 2; this.vy = (Math.random() - 0.5) * 2; this.ax = 0; this.ay = 0; this.maxSpeed = 4; this.maxForce = 0.2; this.size = 5; this.color = `hsl(${Math.random() * 60 + 180}, 70%, 50%)`; } // Separation separate(boids) { let steer = { x: 0, y: 0 }; let count = 0; for (let other of boids) { if (other === this) continue; const d = Math.hypot(this.x - other.x, this.y - other.y); if (d < mouse.radius * 0.8) { const diffX = this.x - other.x; const diffY = this.y - other.y; steer.x += diffX / d; steer.y += diffY / d; count++; } } if (count > 0) { steer.x /= count; steer.y /= count; steer.x = Math.min(steer.x, this.maxForce); steer.y = Math.min(steer.y, this.maxForce); } return steer; } // Alignment align(boids) { let steer = { x: 0, y: 0 }; let count = 0; for (let other of boids) { if (other === this) continue; const d = Math.hypot(this.x - other.x, this.y - other.y); if (d < mouse.radius * 1.5) { steer.x += other.vx; steer.y += other.vy; count++; } } if (count > 0) { steer.x /= count; steer.y /= count; steer.x -= this.vx; steer.y -= this.vy; steer.x = Math.min(steer.x, this.maxForce); steer.y = Math.min(steer.y, this.maxForce); } return steer; } // Cohesion cohesion(boids) { let steer = { x: 0, y: 0 }; let count = 0; for (let other of boids) { if (other === this) continue; const d = Math.hypot(this.x - other.x, this.y - other.y); if (d < mouse.radius * 1.5) { steer.x += other.x; steer.y += other.y; count++; } } if (count > 0) { steer.x /= count; steer.y /= count; steer.x -= this.x; steer.y -= this.y; steer.x = Math.min(steer.x, this.maxForce); steer.y = Math.min(steer.y, this.maxForce); } return steer; } // Flee from mouse flee() { const d = Math.hypot(this.x - mouse.x, this.y - mouse.y); if (d < mouse.radius) { const diffX = this.x - mouse.x; const diffY = this.y - mouse.y; const angle = Math.atan2(diffY, diffX); return { x: Math.cos(angle) * this.maxForce * 2, y: Math.sin(angle) * this.maxForce * 2 }; } return { x: 0, y: 0 }; } // Update boid position update(boids) { // Calculate steering forces const separate = this.separate(boids); const align = this.align(boids); const cohesion = this.cohesion(boids); const flee = this.flee(); // Apply forces this.ax += separate.x + align.x + cohesion.x + flee.x; this.ay += separate.y + align.y + cohesion.y + flee.y; // Update velocity this.vx += this.ax; this.vy += this.ay; // Apply max speed const speed = Math.hypot(this.vx, this.vy); if (speed > this.maxSpeed) { this.vx = (this.vx / speed) * this.maxSpeed; this.vy = (this.vy / speed) * this.maxSpeed; } // Update position this.x += this.vx; this.y += this.vy; // Wrap around screen if (this.x < 0) this.x = width; if (this.x > width) this.x = 0; if (this.y < 0) this.y = height; if (this.y > height) this.y = 0; // Reset acceleration this.ax = 0; this.ay = 0; } // Draw boid as oriented triangle draw() { ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(Math.atan2(this.vy, this.vx)); ctx.beginPath(); ctx.moveTo(0, -this.size); ctx.lineTo(this.size, this.size / 2); ctx.lineTo(-this.size, this.size / 2); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); } } // Simulation variables const boids = []; const numBoids = 50; const mouseFleeRadius = 100; // Initialize function init() { resize(); for (let i = 0; i < numBoids; i++) { boids.push(new Boid()); } } // Animation loop function animate() { ctx.clearRect(0, 0, width, height); // Update and draw all boids for (let i = 0; i < boids.length; i++) { boids[i].update(boids); boids[i].draw(); } // Draw mouse radius ctx.save(); ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(mouse.x, mouse.y, mouse.radius, 0, Math.PI * 2); ctx.stroke(); ctx.restore(); requestAnimationFrame(animate); } // Start simulation init(); animate(); </script> </body> </html>
thinking
The index.html file has been successfully created with a full-screen flocking simulation. The implementation includes: 1. **Boid Class** with: - Separation from nearby boids - Alignment with nearby boids' velocities - Cohesion to stay together with nearby boids - Flee behavior from mouse cursor 2. **Rendering**: - Boids are drawn as oriented triangles facing their direction of movement - Smooth steering with acceleration and velocity - Wrap-around screen boundaries 3. **Features**: - 50 boids in the simulation - Mouse interaction causing boids to flee from the cursor - Visual feedback showing the mouse's influence radius - Responsive full-screen design The simulation runs in a continuous animation loop and is completely self-contained without external dependencies. The HTML file uses inline CSS and JavaScript, making it portable and easy to use.
Created a full-screen flocking simulation with: **Features:** - 50 boids with individual behaviors (separation, alignment, cohesion) - Orientated triangle rendering that faces movement direction - Smooth steering with acceleration and velocity control - Flee behavior from mouse cursor - Wrap-around screen boundaries - Visual mouse radius indicator - Responsive full-screen design - Color-coded boids for visual distinction The simulation is completely self-contained in a single `index.html` file with no external dependencies. Run it in any modern browser to see the flocking behavior in action. Move your mouse to make the boids flee from the cursor.
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
Boids render as tiny sparse triangles clustered along thin lines, not a dense flock; mouse circle visible but no clear flee behavior.
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.