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.

Your browser TypeScript + WebGL2 your dog, predicted: now everything else: ~100 ms ago 5,000 sheep drawn in 0.8 ms Server Rust · 30 ticks a second · one thread 1move the dogs 2dogs paint a fear field 3every sheep reads it same cost per sheep, any crowd 32 m chunks each diffed once, then shared a still sheep costs 1 bit input 18 B positions snapshot · 15 a second · only the chunks you can see · ~0.5–1 KB
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.

0510 0 s1 s2 s3 s 20 m10 m0 m dog speed, m/s distance to cursor full speed (9.6 m/s) in 0.5 s starts slowing 4.5 m out speed distance
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.

fear builds dog, 5 m/s at the edge, it walks away 1.2 m/s closer in, it gets scared and runs up to 3.8 m/s panic spreads even beyond the dog’s reach flight zone: 7 m, plus 1 m per m/s of speed stretched ×1.5 in front of a charging dog surrounded? the pushes cancel, so it bolts through the gap
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.

Every sheep asks every dog 1,000 dogs × 30,000 sheep = 30 million checks a tick Dogs paint, sheep read reads 4 cells 1,000 dogs × ~100 cells + 30,000 reads ≈ 130,000 a tick
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.

fence dog the flock the bellwether flee the dog’s field stay with the flock match neighbours follow the bell keep off the fence One direction speed is the largest of walking away1.2 m/s fear1.2–3.8 m/s grazing0.35 m/s
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 fold, 64 m across hedge rings 110, 230, 360 m out, gaps are the chokepoints 896 m (the old valley was 1,536) the 12 m band 8 gates each 8 m wide toward the fold: full strength sideways is fine too back out: muted nobody can hold a gate
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.

one flock of 26 three strays: a flock of their own Every 0.2 s 1each sheep drops into a 2 m cell 2touching cells join, corners too 3per flock and dog: pressure, marks 4in control: 1.5× the next dog O(sheep), plus a sort over the sheep under pressure
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.

all markedhalf00 s1 s2 s3 s4 s5 s 30 unmarked: 1.2 s 30 of someone else’s: 2.8 s bark their owner barks every 1.2 s: the raid never finishes how far the hold has got
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.

yours: strong someone else’s: faint unmarked
  • 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.

Fly’s view, plus a margin Moss’s view, plus a margin encoded once, sent to both unwatched, no work
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.

One chunk, 120 sheep, 30 of them moving left arrived moved? 1 bit each dx, dy · 2 bytes per sheep that moved fear mark 1–2 B ~7 B each 15 B 60 B ~2 B 0 B ≈ 80 bytes every sheep sent in full: 120 × 8 = 960 bytes, a line twelve times as long
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.

Server Your browser On screen a tick every 33 ms #42#43#44#45 snapshot: “applied #42, your dog was here” replay #43–#45 on top of it same bits, so the correction is 0 ~100–150 ms sheep: ~120 ms ago your dog: now
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.

Client one code path Transport WebSocket Rust server tokio sockets · sim thread Room postMessage Web Worker the same Rust as Wasm, 281 KB Room same bytes
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 players1,000 players1,000 round the fold
Sheep in the valley3,01030,00030,000
Server tick (budget 33 ms)2.1–2.9 ms9.2–10.5 ms7.9 ms
Download per player59 kbps88 kbps329 kbps (p95 388)
Snapshots per second15.015.015.0
Late or dropped snapshots00 (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.