How the Sheep Get Home · Sheepdogs
How the Sheep Get Home
By Ege · written 30 September 2026, updated 20 October 2026
This post is about the first Sheepdogs, where your cursor was a sheepdog. Since 1 November 2026 the game at sheepdogs.io is the one in Herding by hand, and this version is still playable at sheepdogs.io/legacy.
Sheepdogs is a free multiplayer browser game where your cursor is a sheepdog and everyone online herds sheep in the same valley: a thousand people, thirty thousand sheep, and one fold in the middle. I wanted to write down what actually happens between moving your mouse and a sheep running away from your dog: how the dog follows your cursor, how each sheep decides where to go, how the server keeps a thousand browsers in sync, and how your browser draws it all. Most of the numbers below come from real measurements. (You can play it at sheepdogs.io.)
your cursor is the dog · move it over the sheep
1,000real WebSocket players on one server process
30,000sheep, each with its own little brain
~8 msper server tick at 1,000 players, out of 33 ms
329 kbpsper player, with all 1,000 crowding the fold
37.4 KBof gzipped JavaScript for the whole client
How your browser and the server share the work
Your browser and the server run the same game loop, at different speeds. The server decides what really happened: 30 times a second, it moves every dog and every sheep. Your browser only decides where your own dog is. It moves your dog as soon as your hand moves, and later compares the result with what the server says. Everything else on your screen is a copy of the server’s state from a moment ago, smoothed out so it doesn’t jump.
The loop: your inputs go up to the server 30 times a second, and snapshots of the world come back 15 times a second. Each snapshot only covers the part of the valley you can see.
How your dog follows your cursor
Your cursor marks where your dog wants to go, and the dog runs toward it. The farther away the cursor is, the faster the dog runs, but I capped how quickly it can speed up or slow down (30 m/s²). So the dog can’t jump to your cursor and can’t stop instantly, and if you swing the mouse around a flock, the dog runs straight through the space the sheep keep around themselves and scares them. That’s the whole game: careful hands score more.
What “mass” means here. The cursor jumps 20 m away, and the dog reaches full speed in half a second, runs, and slows down over the last 4.5 m as it reaches the cursor. These are the real constants from World::move_dog (arrive gain 2.2/s, top speed 10 m/s, acceleration cap 30 m/s²).
This is also why the game feels instant online. Dogs jump fences and pass through each other, so where a dog goes depends only on its own input. That means your browser can run an exact copy of the server’s dog physics, with every addition and multiplication rounded to 32-bit floats the same way Rust rounds them. A test replays 600 steps and checks that the browser and the server agree bit for bit, so when the server confirms where your dog is, there’s nothing to correct.
How a dog moves sheep without touching them
Dogs never push sheep physically. Each dog has a flight zone around it, an area sheep don’t like to be in: 7 m for a standing dog, growing by 1 m for every m/s of speed up to 18 m, and stretched to 1.5× in front of a dog running straight at the sheep (livestock handlers have known about head-on approaches for a long time, I just borrowed it). A sheep near the edge of the zone calmly walks away. If the dog comes closer, the sheep gets scared and starts to run, and the sheep around it get scared too.
One dog and three sheep. Pressure from the dog makes sheep walk, fear makes them run, and fear spreads from sheep to sheep. When several dogs squeeze a sheep from all sides, their pushes cancel out, so it packs in with the others for a moment and then bolts through the weakest side.
The obvious way to compute this is to have every sheep check every dog. With 1,000 dogs and 30,000 sheep, that’s 30 million checks, 30 times a second, which is far too slow. So I did it the other way around: each dog paints how scary it is into a grid of 2 m cells, and each sheep only reads the four cells under its feet. A sheep costs the same whether there’s one dog nearby or two hundred.
Flocking works the same way. Each 6 m cell keeps totals for the sheep inside it (how many, where their centre is, which way they’re heading, how scared they are), so a sheep can see what its neighbours are doing without searching for them one by one. Only actual collisions need an exact search, so nothing a sheep does in a tick gets more expensive as the crowd grows.
Inside a sheep’s head, 30 times a second
Every tick, each sheep decides which direction to walk in and how fast. The direction comes from a few pulls added together: keep some distance from the other sheep, stay with the flock, get away from the dog, and keep going the way you were going. I started with the weights from the published herding models (Strömbom et al. 2014 rank the pulls in that order), then tuned them by measuring how often a flock actually made it home.
Five pulls, one direction. Scared sheep care more about staying with the flock (the selfish herd), which is why a flock under pressure bunches up and doesn’t scatter. Every flock also has a bellwether with a bell, and the others follow her closely, so the best move is usually to steer her.
Two of the biggest improvements came from deleting things. Sheep used to walk back to their home pasture whenever the dog left, and in one test a dog penned 0% of its flock in a day. After I removed that: 95–100%. Sheep also used to ignore a dog standing right next to them until it scared them. Once I separated the two reactions (pressure makes them walk, fear makes them run), flocks stopped creeping along and actually moved.
One fold, and whose sheep are whose
The valley is an arena now: one round fold in the middle, and anyone can drive anyone’s sheep. That’s what makes it fun, but it needs one rule to decide whose sheep are whose. Every sheep wears the mark of the last dog that clearly drove its flock, and when it walks into the fold, that dog gets the points. Sheep start the day unmarked, so you have to go out into the valley and find them.
The arena to scale: 896 m across, three rings of hedges whose gaps are the bottlenecks, and one fold. On the right, the fold up close. Within 12 m of its fence, a dog’s pressure can only push sheep toward the fold. Pushing them back out barely works, and nobody’s sheep can be taken in that band, so waiting at a gate gets you nothing. In the game, a faint ring of trodden grass shows where the band ends. Defending your sheep happens out in the field.
A flock is just a group of sheep standing close together, and the server works out the groups five times a second. Each sheep goes into a 2 m grid cell, cells that touch are joined, and every connected group is a flock. Then, for each flock, the server adds up how much pressure each dog puts on it, counting each sheep only for the dog pressing on it hardest. A dog is in control when its pressure is 1.5× anyone else’s and at least 0.1 per sheep, so taking over a big flock needs real driving.
Pressure only counts for the dog pressing hardest on each sheep, so two dogs flanking a flock from opposite sides split it evenly, and neither of them is in control.
While you’re in control, a ring in your dye fills up around the flock. When the ring closes, every sheep in the flock takes your mark in the same tick. I didn’t want sheep changing colour a few at a time, because the moment the whole flock turns your colour is the best part. Unmarked sheep take 0.6 s plus 0.02 s per sheep, and someone else’s take 1 s plus 0.06 s per sheep, up to 4 s. The owner can stop a raid by pushing back with at least half the raider’s pressure, or by barking nearby. That’s what the bark is for now.
A raid, using the sim’s own numbers. Marking 30 unmarked sheep takes 1.2 s; taking 30 of someone else’s takes 2.8 s. If the owner is close and barks every 1.2 s (the bark’s cooldown), each bark resets the raid for 0.6 s, so it never finishes. A raider can still take strays the owner can’t reach.
Marks need to be readable at a glance without making the valley look busy. A mark is a rough smudge across a sheep’s back, strong on your sheep and faint on everyone else’s. With a thousand players the ten dyes repeat, so the strength of the mark is how you tell your sheep apart.
- raddle
- woad
- weld
- ochre
- heather
- lichen
- madder
- walnut
- pale woad
- soot
Ten natural dyes, the colours a shepherd’s raddle comes in. Each dog’s collar is its dye, and a new mark flares for a moment as it lands.
Six tests check these rules: a driven flock is marked all at once and never partly, a raider can take an unguarded flock, an owner barking nearby keeps theirs, strays get marked in under 1.5 s, sheep only pay the dog whose mark they wear (unmarked sheep pay nobody), and marks wash off when a dog leaves. One of them found a bug on its first run. The bark window was 0.6 s ÷ (1/30 s) = 17.999…, which got rounded down to 17 ticks, so a bark exactly 18 ticks old didn’t count.
Sending the valley in 32-metre chunks
Each player only needs the sheep near them. So the server cuts the 896 m valley into 32 m chunks, and 15 times a second it compares each chunk that someone is looking at with how it looked last time and encodes the difference into bytes, once. Every player whose view covers that chunk gets the same bytes, and chunks nobody is looking at cost nothing.
Views overlap all the time, for example a crowd around the fold or friends working the same flock. Encoding each chunk once and sharing the bytes between players made building snapshots about 20× cheaper in my benchmarks. With 1,000 players spread across the valley, the server spends about 1.6 ms per tick on it.
What’s in a chunk’s difference? Positions are rounded to 1/16 of a metre, so a sheep that took a step costs two small numbers (dx and dy, one byte each), and a sheep that didn’t move costs one bit. Grazing sheep stand still most of the time, so a calm valley is almost free to send. Each sheep also carries the id of the dog whose mark it wears: one byte while it’s unmarked, and in an update it’s only sent for sheep whose mark just changed. In a real crowd, a player gets at most the nearest 1,200 sheep individually, and the rest of the view as flock summaries.
Updates rely on TCP delivering things in order: a chunk update always applies to what your browser already has. If a player’s connection falls behind, the server skips one snapshot for them and sends full copies next time. A test replays 600 real snapshots in TypeScript and checks that every sheep ends up exactly where the server has it, with the same mark.
Two moments on one screen
Your screen actually shows two different moments at once. Your dog is drawn in the present, predicted from your own inputs. Everything else is drawn about 100–150 ms in the past, smoothly interpolated between the two latest snapshots so nothing jumps. You’d think that delay would feel like lag, but real sheep have reaction times too, and they react at the edge of the flight zone, well before your dog reaches them. So the delay ends up looking like a sheep noticing your dog.
Your browser numbers every input. The server tells it which input it applied last and where your dog ended up, and the browser re-applies the newer inputs on top of that. Since the physics match bit for bit, the dog ends up in exactly the same place.
Everything is drawn with one instanced WebGL2 call per layer. Every sheep, dog, hedge, pond and tree is a small square, and a pixel shader draws the actual shape from math (signed-distance shapes): scalloped wool, a dark head, legs that show when it trots, a bell on the bellwether. There are no image files at all. On my machine, drawing 5,000 sheep takes 0.77 ms per frame, and 20,000 take 3.2 ms.
The server also fits in your browser
The idea for the game came from Tangle by Ian Kettlewell, which makes a WebAssembly game multiplayer without any server code. Every player’s browser runs the same game, and when someone’s input arrives late, every browser goes back to a saved copy and replays from there. Tangle was how I first planned to build this. But I wanted one valley with hundreds of strangers and tens of thousands of sheep, and at that size every browser would be rewinding the whole valley almost every frame. So one server runs the valley, and each player only gets what’s around their dog. The part that stayed from Tangle is that the same simulation runs in more than one place.
The part that runs a game (the world, the players, the input queues and the snapshot encoder) is one Rust type called Room. The real server wraps it in WebSockets. The same code also compiles to a 281 KB WebAssembly file that runs in a Web Worker and sends exactly the same bytes. That’s why the game also works without a server, and why the client only needs one way of doing things.
On sheepdogs.io you play online; add ?local to the address and the same game runs in a Web Worker in your tab.
Numbers
I measured these on my M4 Max, with a load tester that opens real WebSocket connections and steers each dog the way a player would. The first two columns are the old valley, with everyone spread out. The last one has all 1,000 players working around the fold, which is the arena at its most crowded.
| 100 players | 1,000 players | 1,000 round the fold |
| Sheep in the valley | 3,010 | 30,000 | 30,000 |
| Server tick (budget 33 ms) | 2.1–2.9 ms | 9.2–10.5 ms | 7.9 ms |
| Download per player | 59 kbps | 88 kbps | 329 kbps (p95 388) |
| Snapshots per second | 15.0 | 15.0 | 15.0 |
| Late or dropped snapshots | 0 | 0 (worst gap 75 ms) | 0 (worst gap 79 ms) |
The last column is higher because the arena puts everyone in one crowd on purpose, and in a crowd each player gets the nearest 1,200 sheep individually.
What the AI dogs do
Besides herding their own sheep, the AI dogs in the arena raid and defend. Half of them go after other dogs’ flocks, especially ones that are almost home, and press in close until the flock takes their mark. When someone raids an AI dog’s flock, the dog runs back from up to 160 m away and barks every time its bark is ready, which stops the raid for as long as it stays there. If the flock is taken anyway, the dog goes after it for 30 seconds and takes it back if the new owner leaves it unguarded. Over one five-minute day with 100 AI dogs, 455 flocks changed hands, 97 of them taken from another dog, and 1,917 sheep came home.
What’s next
Since the first draft, the arena went live at sheepdogs.io. New players now join somewhere quiet instead of next to their friends, which brought bandwidth at 1,000 players down from 1.6 Mbps to 88 kbps per player. Sheep near your dog are predicted so a charging dog never overlaps one, and you can zoom out over the whole valley. A day lasts ten minutes now, a bark sends a ripple through the grass, and there’s a four-step guide for first-time players. Here’s what I want to work on next.
Play with real people
Friends first. After that I want to tune how long a capture takes, the 1.5× lead a raider needs and the bark window, and see whether losing a flock feels fair. The dog’s weight might need another look too.
Better AI dogs
When I first wrote this, 100 AI dogs brought home about 11–18% of the flock in a day. Since then I built a test for them: a fixed set of games and set pieces, scored from where the sheep and dogs actually are and what got home. After three rounds of tuning against it, 100 AI dogs bring home 34–55% of the valley’s sheep in the same five-minute day. Near the fold they still lose time. A flock that reaches the fence between two gates presses against it and splits, so next I want them to bring the flock round in front of one gate and walk it straight in, the way a careful player does.
Piano Movers
A second game on the same engine: same room, same networking, same prediction. This time everyone moves one huge, awkward object together.