Boids flocking · gemma-4-26b-a4b-it
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.
This one-shot run produced no runnable artifact.
agent produced no index.html
The transcript below shows what the model did — read it to see why the one shot fell short.
transcript · 2 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 (Boids) in a single `index.html` file.
The simulation should include:
1. **Boids algorithm**: Separation, Alignment, Cohesion.
2. **Visuals**: Oriented triangles that steer smoothly.
3. **Interaction**: Flee from the mouse cursor.
4. **Self-contained**: All CSS and JS inside `index.html`, no external resources.
Plan:
1. Set up a basic HTML structure with a `<canvas>` element covering the full screen.
2. Add CSS to make the canvas full-screen and remove margins/scrollbars.
3. Implement the Boids logic in JavaScript:
* `Boid` class: position, velocity, acceleration, max speed, max force.
* `flock` method: apply separation, alignment, cohesion, and mouse avoidance.
* `update` method: apply acceleration to velocity, velocity to position, and limit velocity/acceleration.
* `draw` method: draw the boid as an oriented triangle.
4. Implement the main animation loop.
5. Handle window resizing.
6. Handle mouse movement for the "flee" behavior.
Implementation details:
- **Separation**: Steer to avoid crowding neighbors.
- **Alignment**: Steer towards the average heading of neighbors.
- **Cohesion**: Steer towards the average position (center of mass) of neighbors.
- **Mouse Avoidance**: Steer away from the mouse cursor.
- **Optimization**: Since it's a simulation, I might need a simple spatial partitioning if there are many boids, but for a single-file demo, a simple $O(n^2)$ approach might suffice if $n$ is reasonable (e.g., 100-200 boids). Let's aim for ~150 boids.
Let's write the code.```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Boids Flocking Simulation</title> <style> body { margin: 0; overflow: hidden; background-color: #1a1a2e; } canvas { display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let width, height; let boids = []; const BOID_COUNT = 150; const mouse = { x: -1000, y: -1000, active: false }; // Configuration constants const CONFIG = { maxSpeed: 3, maxForce: 0.05, perceptionRadius: 50, separationDistance: 30, cohesionWeight: 1, alignmentWeight: 1, separationWeight: 1.5, avoidMouseWeight: 2.5, mouseAvoidanceRadius: 150 }; class Vector { constructor(x, y) { this.x = x; this.y = y; } add(v) { this.x += v.x; this.y += v.y; return this; } sub(v) { this.x -= v.x; this.y -= v.y; return this; } mul(n) { this.x *= n; this.y *= n; return this; } div(n) { this.x /= n; this.y /= n; return this; } mag() { return Math.sqrt(this.x * this.x + this.y * this.y); } setMag(n) { const m = this.mag(); if (m !== 0) this.mul(n / m); return this; } limit(max) { const m = this.mag(); if (m > max) { this.div(m).mul(max); } return this; } static dist(v1, v2) { return Math.sqrt((v1.x - v2.x) ** 2 + (v1.y - v2.y) ** 2); } static sub(v1, v2) { return new Vector(v1.x - v2.x, v1.y - v2.y); } } class Boid { constructor() { this.position = new Vector(Math.random() * width, Math.random() * height); this.velocity = new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1).setMag(Math.random() * 2 + 2); this.acceleration = new Vector(0, 0); this.color = `hsl(${Math.random() * 40 + 180}, 70%, 60%)`; } edges() { if (this.position.x > width) this.position.x = 0; else if (this.position.x < 0) this.position.x = width; if (this.position.y > height) this.position.y = 0; else if (this.position.y < 0) this.position.y = height; } flock(boids) { let alignment = new Vector(0, 0); let cohesion = new Vector(0, 0); let separation = new Vector(0, 0); let total = 0; for (let other of boids) { let d = Vector.dist(this.position, other.position); if (other !== this && d < CONFIG.perceptionRadius) { alignment.add(other.velocity); cohesion.add(other.position); if (d < CONFIG.separationDistance) { let diff = Vector.sub(this.position, other.position); diff.div(d * d); // Weight by distance (inverse square law feel) separation.add(diff); } total++; } } if (total > 0) { alignment.div(total).setMag(CONFIG.maxSpeed).sub(this.velocity).limit(CONFIG.maxForce); cohesion.div(total).sub(this.position).setMag(CONFIG.maxSpeed).sub(this.velocity).limit(CONFIG.maxForce); separation.div(total).setMag(CONFIG.maxSpeed).sub(this.velocity).limit(CONFIG.maxForce); this.acceleration.add(alignment.mul(CONFIG.alignmentWeight)); this.acceleration.add(cohesion.mul(CONFIG.cohesionWeight)); this.acceleration.add(separation.mul(CONFIG.separationWeight)); } // Mouse avoidance if (mouse.active) { let d = Vector.dist(this.position, mouse); if (d < CONFIG.mouseAvoidanceRadius) { let flee = Vector.sub(this.position, mouse); flee.setMag(CONFIG.maxSpeed).sub(this.velocity).limit(CONFIG.maxForce * 2); this.acceleration.add(flee.mul(CONFIG.avoidMouseWeight)); } } } update() { this.position.add(this.velocity); this.velocity.add(this.acceleration); this.velocity.limit(CONFIG.maxSpeed); this.acceleration.mul(0); } draw() { const angle = Math.atan2(this.velocity.y, this.velocity.x); ctx.save(); ctx.translate(this.position.x, this.position.y); ctx.rotate(angle); ctx.beginPath(); ctx.moveTo(10, 0); ctx.lineTo(-6, -5); ctx.lineTo(-4, 0); ctx.lineTo(-6, 5); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); // Glow effect ctx.shadowBlur = 10; ctx.shadowColor = this.color; ctx.restore(); } } function resize() { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; } function init() { resize(); boids = []; for (let i = 0; i < BOID_COUNT; i++) { boids.push(new Boid()); } } function animate() { // Semi-transparent clear for motion blur effect ctx.fillStyle = 'rgba(26, 26, 46, 0.2)'; ctx.fillRect(0, 0, width, height); for (let boid of boids) { boid.edges(); boid.flock(boids); boid.update(); boid.draw(); } requestAnimationFrame(animate); } window.addEventListener('resize', resize); window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; mouse.active = true; }); window.addEventListener('mouseleave', () => { mouse.active = false; }); init(); animate(); </script> </body> </html> ```
TAKE IT TO CODE
This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.