---
format: typebulb/v1
name: Birds
---

**code.tsx**

```tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { useEffect, useRef } from "react";

type Vec2 = { x: number; y: number };

const vec = {
  sub: (a: Vec2, b: Vec2): Vec2 => ({ x: a.x - b.x, y: a.y - b.y }),
  add: (a: Vec2, b: Vec2): Vec2 => ({ x: a.x + b.x, y: a.y + b.y }),
  scale: (v: Vec2, s: number): Vec2 => ({ x: v.x * s, y: v.y * s }),
  mag: (v: Vec2) => Math.sqrt(v.x * v.x + v.y * v.y),
  norm: (v: Vec2): Vec2 => { const m = vec.mag(v); return m > 0 ? vec.scale(v, 1 / m) : { x: 0, y: 0 }; },
  limit: (v: Vec2, max: number): Vec2 => { const m = vec.mag(v); return m > max ? vec.scale(vec.norm(v), max) : v; }
};

class Boid {
  pos: Vec2;
  vel: Vec2;
  acc: Vec2 = { x: 0, y: 0 };
  flapPhase = 0;
  flapTimer = 50 + Math.random() * 100;
  isFlapping = false;

  constructor(x: number, y: number) {
    this.pos = { x, y };
    const angle = Math.random() * Math.PI * 2;
    this.vel = { x: Math.cos(angle) * 2, y: Math.sin(angle) * 2 };
  }

  steer(boids: Boid[], radius: number, fn: (o: Boid, d: number) => Vec2, seek = false): Vec2 {
    let sum = { x: 0, y: 0 }, count = 0;
    for (const o of boids) {
      const d = vec.mag(vec.sub(this.pos, o.pos));
      if (d > 0 && d < radius) { const v = fn(o, d); sum.x += v.x; sum.y += v.y; count++; }
    }
    if (!count) return { x: 0, y: 0 };
    const avg = vec.scale(sum, 1 / count);
    const target = seek ? vec.sub(avg, this.pos) : avg;
    return vec.mag(target) ? vec.limit(vec.sub(vec.scale(vec.norm(target), 3), this.vel), 0.03) : { x: 0, y: 0 };
  }

  flock(boids: Boid[]) {
    const sep = this.steer(boids, 55, (o, d) => vec.scale(vec.norm(vec.sub(this.pos, o.pos)), ((55 - d) / 55) ** 2));
    const ali = this.steer(boids, 90, o => o.vel);
    const coh = this.steer(boids, 90, o => o.pos, true);
    this.acc = vec.add(this.acc, vec.add(vec.scale(sep, 1.3), vec.add(vec.scale(ali, 1.2), coh)));
  }

  update(w: number, h: number) {
    this.vel = vec.limit(vec.add(this.vel, this.acc), 3);
    this.pos = vec.add(this.pos, this.vel);
    this.acc = { x: 0, y: 0 };
    this.pos.x = (this.pos.x + w) % w;
    this.pos.y = (this.pos.y + h) % h;

    this.flapTimer--;
    if (this.isFlapping) {
      this.flapPhase += 0.5;
      if (this.flapTimer <= 0) { this.isFlapping = false; this.flapTimer = 60 + Math.random() * 120; }
    } else if (this.flapTimer <= 0) {
      this.isFlapping = true; this.flapTimer = 20 + Math.random() * 30; this.flapPhase = 0;
    }
  }

  draw(ctx: CanvasRenderingContext2D) {
    ctx.save();
    ctx.translate(this.pos.x, this.pos.y);
    ctx.rotate(Math.atan2(this.vel.y, this.vel.x));
    const f = this.isFlapping ? Math.sin(this.flapPhase) : 0;
    const t = 1 - f * 0.4, m = 1 - f * 0.2;
    ctx.beginPath();
    ctx.moveTo(10, 0);
    for (const [x, y] of [[2, -2], [-4, -14 * t], [-6, -10 * m], [-2, -2], [-8, 0], [-2, 2], [-6, 10 * m], [-4, 14 * t], [2, 2]])
      ctx.lineTo(x, y);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
  }
}

function App() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const boidsRef = useRef<Boid[]>([]);

  useEffect(() => {
    const canvas = canvasRef.current!, ctx = canvas.getContext("2d")!;
    const resize = () => { canvas.width = innerWidth; canvas.height = innerHeight; };
    resize();
    addEventListener("resize", resize);

    if (!boidsRef.current.length)
      boidsRef.current = Array.from({ length: 100 }, () => new Boid(Math.random() * canvas.width, Math.random() * canvas.height));

    let frameId: number;
    const animate = () => {
      const style = getComputedStyle(document.documentElement);
      ctx.fillStyle = style.getPropertyValue("--bg-color");
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = style.getPropertyValue("--bird-color");
      for (const b of boidsRef.current) { b.flock(boidsRef.current); b.update(canvas.width, canvas.height); b.draw(ctx); }
      frameId = requestAnimationFrame(animate);
    };
    animate();

    return () => { removeEventListener("resize", resize); cancelAnimationFrame(frameId); };
  }, []);

  return (
    <div className="container">
      <canvas ref={canvasRef} className="canvas" />
      <div className="info">
        <h1>Boids Flocking Simulation</h1>
        <p>Watch as 100 birds demonstrate emergent flocking behavior through three simple rules:</p>
        <ul>
          <li><strong>Separation:</strong> Avoid crowding neighbors</li>
          <li><strong>Alignment:</strong> Steer toward average heading</li>
          <li><strong>Cohesion:</strong> Steer toward average position</li>
        </ul>
      </div>
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);

```
**styles.css**

```css
html[data-theme="light"] {
  color-scheme: light;
  --bg-color: #f0f4f8;
  --bird-color: #2d3748;
  --text-color: #1a202c;
  --info-bg: rgba(255, 255, 255, 0.7);
  --info-border: rgba(45, 55, 72, 0.1);
}

html[data-theme="dark"] {
  color-scheme: dark;
  --bg-color: #1a202c;
  --bird-color: #63b3ed;
  --text-color: #e2e8f0;
  --info-bg: rgba(26, 32, 44, 0.7);
  --info-border: rgba(226, 232, 240, 0.1);
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  overflow: hidden;
  background-color: var(--bg-color);
}

.container {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.info {
  position: absolute;
  top: 20px;
  left: 20px;
  max-width: 400px;
  padding: 5px;   
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  color: var(--text-color);  
}

.info h1 {
  font-size: 1.5rem;
  margin-bottom: 12px;
  font-weight: 600;
}

.info p {
  margin-bottom: 12px;
  line-height: 1.5;
}

.info ul {
  margin-left: 20px;
  line-height: 1.6;
}
```
**index.html**

```html
<div id="root"></div>
```
**config.json**

```json
{
  "dependencies": {
    "react": "^19.2.3",
    "react-dom": "^19.2.3"
  },
  "description": "Boids Flocking Simulation using the **Boids algorithm** created by Craig Reynolds in 1986, simulating the flocking behavior of birds through emergent complexity from simple rules."
}
```