oneshotlm
Compare all models on this prompt →

Boids flocking · laguna-m.1

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.

⛨ sandboxed Open in new tab ↗
transcript · 8 messages
user

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.

assistant
thinking
The user wants me to create a full-screen flocking simulation (boids) in a single index.html file. Let me break down the requirements:

1. Full-screen - should use the canvas to fill the entire viewport
2. Flocking simulation with boids (separation, alignment, cohesion)
3. Drawn as oriented triangles - each boid should be a triangle pointing in its direction of movement
4. Smooth steering - nice smooth movement
5. Flee from mouse cursor - boids should avoid the cursor
6. Self-contained - all CSS and JS in the HTML file, no external resources

Let me create this simulation. I'll use:
- HTML5 Canvas for rendering
- Boids algorithm with proper flocking behavior
- Mouse position detection for flee behavior
- Triangle sprites that rotate based on velocity
- Smooth animation with requestAnimationFrame

I'll create a comprehensive flocking simulation with:
- A good number of boids (maybe 100-200)
- Proper separation, alignment, and cohesion forces
- Mouse fleeing behavior
- Edge wrapping or bouncing
- Nice visual styling

I'll create a full-screen flocking simulation with boids drawn as oriented triangles that flee from the mouse cursor. This will be a complete self-contained HTML file.

write /workspace/index.html (11466 bytes)
toolResult

Successfully wrote 11466 bytes to /workspace/index.html

assistant
thinking
The file has been created successfully. Let me verify it was written correctly by reading it.

The flocking simulation has been created. Let me verify the file:

read {"path":"/workspace/index.html"}
toolResult

<!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> * { margin: 0; padding: 0; overflow: hidden; } body { background: linear-gradient(135deg, #0c0e20, #1a1033); height: 100vh; width: 100vw; } canvas { display: block; } #info { position: fixed; top: 20px; left: 20px; color: rgba(255, 255, 255, 0.7); font-family: 'Courier New', monospace; font-size: 14px; pointer-events: none; text-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } #stats { position: fixed; bottom: 20px; left: 20px; color: rgba(255, 255, 255, 0.5); font-family: 'Courier New', monospace; font-size: 12px; pointer-events: none; } .cursor-indicator { position: fixed; width: 40px; height: 40px; border: 2px solid rgba(255, 100, 100, 0.5); border-radius: 50%; transform: translate(-50%, -50%); pointer-events: none; z-index: 10; display: none; } </style> </head> <body> <canvas id="canvas"></canvas> <div id="info">Flocking Boids Simulation<br>Move mouse to see them flee!</div> <div id="stats">Boids: <span id="count">200</span></div> <div class="cursor-indicator" id="cursorIndicator"></div> <script> // Canvas setup const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const countEl = document.getElementById('count'); const cursorIndicator = document.getElementById('cursorIndicator'); function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); // Mouse position tracking const mouse = { x: -1000, y: -1000, radius: 100 }; document.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; cursorIndicator.style.display = 'block'; cursorIndicator.style.left = e.clientX + 'px'; cursorIndicator.style.top = e.clientY + 'px'; }); document.addEventListener('mouseleave', () => { mouse.x = -1000; mouse.y = -1000; cursorIndicator.style.display = 'none'; }); // Vector utility class class Vector { constructor(x = 0, y = 0) { 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(s) { this.x *= s; this.y *= s; return this; } div(s) { this.x /= s; this.y /= s; return this; } mag() { return Math.sqrt(this.x * this.x + this.y * this.y); } setMag(s) { const m = this.mag(); if (m > 0) { this.x = (this.x / m) * s; this.y = (this.y / m) * s; } return this; } normalize() { const m = this.mag(); if (m > 0) { this.x /= m; this.y /= m; } return this; } static sub(v1, v2) { return new Vector(v1.x - v2.x, v1.y - v2.y); } static add(v1, v2) { return new Vector(v1.x + v2.x, v1.y + v2.y); } static mul(v, s) { return new Vector(v.x * s, v.y * s); } static div(v, s) { return new Vector(v.x / s, v.y / s); } static dist(v1, v2) { return Vector.sub(v1, v2).mag(); } } // Boid class class Boid { constructor() { this.position = new Vector(Math.random() * canvas.width, Math.random() * canvas.height); const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 2 + 1; this.velocity = new Vector(Math.cos(angle) * speed, Math.sin(angle) * speed); this.acceleration = new Vector(); this.maxSpeed = 3; this.maxForce = 0.05; this.size = 8; } update(boids) { // Calculate flocking behaviors const sep = this.separate(boids); const ali = this.align(boids); const coh = this.cohere(boids); const fle = this.flee(mouse); // Apply weights sep.mul(1.8); ali.mul(1.0); coh.mul(1.0); fle.mul(2.0); // Accumulate acceleration this.acceleration.add(sep).add(ali).add(coh).add(fle); // Update velocity and position this.velocity.add(this.acceleration); this.velocity.limit(this.maxSpeed); this.position.add(this.velocity); this.acceleration.mul(0); // Wrap around edges this.wrapEdges(); } wrapEdges() { if (this.position.x < 0) this.position.x = canvas.width; if (this.position.x > canvas.width) this.position.x = 0; if (this.position.y < 0) this.position.y = canvas.height; if (this.position.y > canvas.height) this.position.y = 0; } separate(boids) { const steer = new Vector(); let count = 0; const perception = 30; for (const other of boids) { const d = Vector.dist(this.position, other.position); if (other !== this && d > 0 && d < perception) { const diff = Vector.sub(this.position, other.position); diff.normalize(); diff.div(d); steer.add(diff); count++; } } if (count > 0) { steer.div(count); steer.normalize(); steer.mul(this.maxSpeed); steer.sub(this.velocity); steer.limit(this.maxForce); } return steer; } align(boids) { const sum = new Vector(); let count = 0; const perception = 50; const range = 100; for (const other of boids) { const d = Vector.dist(this.position, other.position); if (other !== this && d > 0 && d < perception) { sum.add(other.velocity); count++; } } if (count > 0) { sum.div(count); sum.setMag(this.maxSpeed); const steer = Vector.sub(sum, this.velocity); steer.limit(this.maxForce); return steer; } return new Vector(); } cohere(boids) { const sum = new Vector(); let count = 0; const perception = 50; const range = 100; for (const other of boids) { const d = Vector.dist(this.position, other.position); if (other !== this && d > 0 && d < perception) { sum.add(other.position); count++; } } if (count > 0) { sum.div(count); return this.seek(sum); } return new Vector(); } seek(target) { const desired = Vector.sub(target, this.position); const d = desired.mag(); if (d > 0) { desired.normalize(); desired.mul(this.maxSpeed); const steer = Vector.sub(desired, this.velocity); steer.limit(this.maxForce); return steer; } return new Vector(); } flee(target) { const d = Vector.dist(this.position, target); if (d < mouse.radius) { const desired = Vector.sub(this.position, target); desired.normalize(); desired.mul(this.maxSpeed); const steer = Vector.sub(desired, this.velocity); steer.limit(this.maxForce * 3); return steer; } return new Vector(); } draw() { const angle = Math.atan2(this.velocity.y, this.velocity.x); ctx.save(); ctx.translate(this.position.x, this.position.y); ctx.rotate(angle); // Create gradient for glow effect const speed = this.velocity.mag(); const hue = 200 + Math.min(speed * 50, 60); // Draw triangle boid ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(-this.size, -this.size/2); ctx.lineTo(-this.size, this.size/2); ctx.closePath(); // Triangle fill with gradient const gradient = ctx.createLinearGradient(-this.size, 0, 0, 0); gradient.addColorStop(0, `hsla(${hue}, 80%, 60%, 0.8)`); gradient.addColorStop(1, `hsla(${hue}, 100%, 70%, 1)`); ctx.fillStyle = gradient; ctx.strokeStyle = `hsla(${hue}, 100%, 80%, 0.9)`; ctx.lineWidth = 1.5; ctx.fill(); ctx.stroke(); // Add glow effect ctx.shadowBlur = 15; ctx.shadowColor = `hsla(${hue}, 100%, 70%, 0.6)`; ctx.fill(); ctx.restore(); } } // Vector limit method Vector.prototype.limit = function(max) { if (this.mag() > max) { this.setMag(max); } return this; }; // Initialize boids const boids = []; const numBoids = 200; for (let i = 0; i < numBoids; i++) { boids.push(new Boid()); } countEl.textContent = numBoids; // Animation loop function animate() { // Semi-transparent fill for trail effect ctx.fillStyle = 'rgba(12, 14, 32, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Update and draw boids for (const boid of boids) { boid.update(boids); boid.draw(); } requestAnimationFrame(animate); } animate(); </script> </body> </html>

assistant
thinking
The file looks correct. Let me verify it exists and check the file size.

bash {"command":"ls -la /workspace/index.html"}
toolResult

-rw-r--r-- 1 node node 11466 Jul 22 16:48 /workspace/index.html

assistant
thinking
The file is created successfully. Let me verify it's valid HTML by checking if it can be parsed.

The flocking simulation has been created at `/workspace/index.html`. It includes: - **200 boids** drawn as oriented triangles (pointing in their direction of travel) - **Three core flocking behaviors**: - **Separation** - boids avoid crowding neighbors (perception radius: 30px) - **Alignment** - boids steer toward average heading of neighbors (perception: 50px) - **Cohesion** - boids steer toward center of neighbors (perception: 50px) - **Mouse flee behavior** - boids flee from the cursor when within 100px radius - **Full-screen canvas** with dark gradient background - **Smooth trails** using semi-transparent fade - **Colorful glowing triangles** with speed-based hue variation - **Responsive** - resizes with window - **No external dependencies** - fully self-contained with inline CSS and JS

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

modellaguna-m.1
vendorPoolside
harnesspi-agent
generated2026-07-22

EVALUATION

4/5 working

Boids flock smoothly as glowing purple triangles with organic clustering; mouse cursor circle shown but flee effect subtle.

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.

idle motion29.6%
on click29.2%
on drag29.2%
on wheel30.2%
on enter + space28.2%
on arrow keys25.5%
on w/a/s/d26.7%
frame spread19.4 / 255
console errors0
js errors none

METRICS

runtime41.9s
tokens in9.2k
tokens out3.5k
cached13k
cost / run$0.0045