koi-pond
v0.1.0
Published
Interactive koi fish pond simulation using boids flocking — drop it on any canvas
Maintainers
Readme
koi-pond
Interactive koi fish simulation using boids flocking. Drop it on any HTML canvas.
Fish school together, orbit your cursor, and scatter when you click or swipe. Built with Canvas 2D, zero dependencies at runtime.
Install
npm install koi-pondQuick start
<canvas id="pond" style="width: 100%; height: 100vh"></canvas>
<script type="module">
import { createKoiPond } from "koi-pond";
const canvas = document.getElementById("pond");
const pond = createKoiPond(canvas);
pond.start();
</script>Options
import { createKoiPond } from "koi-pond";
const pond = createKoiPond(canvas, {
// Number of fish (default: 18)
count: 24,
// Per-fish opacity — useful for fading fish behind UI elements
alphaFn: (koi) => (koi.pos.x < 200 ? 0.15 : 1),
// Override any simulation config values
config: {
maxSpeed: 3,
separationRadius: 80,
},
// Respect prefers-reduced-motion (default: true)
// When true and user prefers reduced motion, renders a single static frame
respectReducedMotion: true,
});
pond.start();
// Later...
pond.stop(); // pause animation (listeners stay attached)
pond.destroy(); // stop + remove all event listenersLow-level API
For full control over the simulation loop:
import {
createSchool,
stepSimulation,
createRenderer,
defaultConfig,
type PondConfig,
type MouseState,
} from "koi-pond";
const config: PondConfig = {
...defaultConfig,
width: canvas.width,
height: canvas.height,
};
let school = createSchool(config);
const renderer = createRenderer(canvas, config);
let mouse: MouseState | null = null;
const tick = (now: number) => {
const dt = /* your delta time */ 1;
school = stepSimulation(school, mouse, config, dt);
renderer.render(school, now / 1000);
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);How it works
Each fish runs Craig Reynolds' boids algorithm with three forces:
- Separation — avoid crowding nearby fish
- Alignment — steer toward the average heading of neighbors
- Cohesion — move toward the center of nearby fish
On top of that, fish:
- Avoid canvas edges with a soft turn force
- Wander randomly for natural movement
- Approach a still cursor and orbit it
- Scatter away from fast cursor movement or clicks
Each fish carries a chain-spine: an array of world-space points that follow the head like links in a chain. Turns bend the body, straight swimming straightens it. The renderer draws the body outline from this spine, with colored splotch patches, pectoral fins, and a flowing tail ribbon.
License
MIT
