Skip to content

matthew-kissinger/sds

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

790 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Sheep Dog Simulator

Play now   AGPL-3.0 Code   CC BY-SA 4.0 Assets   Star on GitHub

Three.js 0.184 React 19 Vite 7.3 Tailwind 4.1 Cloudflare Workers + D1 Vitest 4 Tests

Herd up to 5,000 sheep across four biomes in a modern browser, with progressive WebGPU on supported hardware and WebGL fallback everywhere else. No install, no signup, no ads. Free to play, source-readable, and forkable under AGPL-3.0; hosted or modified versions must publish corresponding source and preserve attribution.

Play it now → sheepdogsim.com

Current WebGPU scene captures:

Newsheepdogland Home Field Rolling Hills Open Country
Sheep Dog Sim Newsheepdogland WebGPU capture: the sheepdog on the dusk shore facing the island mountain across the bay Sheep Dog Sim Home Field capture with the sheepdog and flock in grass Sheep Dog Sim Rolling Hills capture with the sheepdog by the shoreline Sheep Dog Sim Open Country capture with the sheepdog facing the portal objective

Why this exists

Most browser 3D games are tech demos with no game inside, or closed-source mobile-clone ports with no tech to learn from. This is neither. It's a fully playable 3D game whose source you can read end-to-end, study, fork, and reshape under a license that keeps hosted improvements open.

The whole stack:

  • Client engine: vanilla JavaScript, Three.js, progressive WebGPU on supported browsers, and WebGL fallback
  • Server: ~600-line TypeScript Cloudflare Worker with Durable Objects and D1
  • Shared sim: deterministic boid + obstacle modules imported byte-identically by both
  • Tests: Vitest 4 coverage for renderer adapters, atmosphere, heightfield, scene-obstacles, island-boundary, tree placement, sim-baseline, refactor-baseline characterization goldens, worker D1 validation, integration harness, practice-mode, SEO, and water shoreline math

If you're learning 3D web games, real-time multiplayer on edge compute, or large-scale boid simulation, this codebase is a rare opportunity to read a complete shipped product instead of yet another minimal example.


What's actually in here

🐑 5,000-sheep flocking in a single draw call

  • GPU-instanced sheep via Three.js InstancedMesh + custom vertex shader (legs and heads animate per-instance)
  • Force-based steering on real obstacles — sheep + dog actually route around tree trunks, large rocks, and terrain via a deterministic kdbush spatial index
  • Adaptive boid AI: tighter cohesion at higher counts so 5,000 sheep stay readable instead of dissolving into noise

🌍 Four hand-built biomes

Scene Shape Hook
Home Field Flat fenced rect (±100 m) Single perimeter pen, gate-passage retirement. The starter.
Newsheepdogland Boot-shaped survival island Homestead pen, wolves after dark, longer-reach sheepdog bark, darker nights, and the current WebGPU hero scene.
Rolling Hills 180 m island with rolling heightfield Lightning-zap corral, water + Mediterranean tree mix, golden-hour mood.
Open Country 380 m island (~4.2× area) Multi-stage objective: gather 40 sheep into the round-up zone for 2 sec, then drive them through a magical portal at the north shore.

🎮 Six gameplay modes

  • Just Play (Practice Paddock — 30 sheep, no timer, no fail state) — new in v2.1.0
  • Solo Classic (200 sheep, scene-default goal, leaderboard)
  • Solo Extreme (1,000 sheep)
  • Solo Insane (3,000 sheep)
  • Solo Chaos (5,000 sheep — the flock is the antagonist)
  • Survival (Newsheepdogland — start with 10 sheep, bark to push sheep and repel wolves, bring them home before the flock thins after dark)
  • Multiplayer: 2–4 player real-time co-op + competitive rooms + 3-minute timed mode + 2-player local split-screen + sandbox editor with shareable URLs

⚡ Authoritative 60 Hz multiplayer on Cloudflare's edge

  • Durable Objects host per-room sim — rooms live wherever Cloudflare schedules them, no cold start
  • MessagePack-over-WebSocket state frames; client adaptive-jitter-buffer widens automatically as RTT stddev rises
  • D1 leaderboards with per-mode best times and a discriminator-based identity (#0001-style tags)
  • Reconnect grace window — drop a tab and rejoin within 15 s without losing your run
  • Wire protocol v3 (v2.3.0): changed-sheep delta frames with a full keyframe every 60 ticks; older clients soft-degrade to full frames, and a socket that stays saturated past 256 KB for ~4 s is evicted through the normal host-migration path
  • Server-minted identity (P-SEC-1): the worker derives your persistent id from a verified token bound to a device-held secret, so a leaked id alone is useless

🎨 Cinematic visual layer

  • WebGPU-primary renderer path on capable browsers, with explicit WebGL fallback and a forced ?renderer=webgl escape hatch
  • Hosek-Wilkie analytic sky with day/night presets, parallax cloud layer, shoreline-aware water with sun-glint, billboarded sun disc
  • Hundreds of thousands of grass blades with directional wind shader, dog-bends-grass-along-its-facing interaction, per-scene density tuning, stochastic-dither LOD
  • Apple-correct tone mapping — Mac/iPhone/iPad use Neutral instead of ACES so the sky doesn't wash white on Metal-ANGLE
  • Per-scene tree LOD + impostor atlases. The WebGPU tree route now has explicit material-parity and octahedral sidecar proof, but it still stays opt-in until mobile frame budgets and transition quality justify making it the default.
  • Three camera modes: Classic (top-down isometric), Follow (cinematic chase with ridge-clearance lift), Free (mouse-yaw orbit)

🌐 Mobile + i18n + accessibility

  • Interactive tutorial (v2.3.0): a guided first-run herding lesson, offered once to fresh profiles and replayable from the menu
  • 9 achievements and full keyboard/gamepad control rebinding (v2.3.0)
  • Touch joystick + on-screen sprint button + responsive HUD; PWA-installable from any browser
  • Full gamepad support (analog stick + buttons)
  • 5 languages across the whole UI and the tutorial: English, Spanish, Portuguese, Japanese, Simplified Chinese (more contributions welcome)

🔬 SEO + share-ready

  • Per-scene metadata: <title>, og:image, og:title, og:description, twitter:* all switch on ?scene=X deeplink — new in v2.1.0
  • Schema.org VideoGame + FAQPage + WebApplication structured data
  • Lighthouse SEO 100 / 100

Try it locally in 30 seconds

Requires Node 22+ and a modern browser (Chrome 80+, Firefox 75+, Safari 13+, Edge 80+).

git clone https://github.com/matthew-kissinger/sds.git
cd sds
npm install
npm run dev:setup      # apply D1 migrations to local sqlite (one-time)
npm run dev            # vite (:3000) + wrangler (:8787) together

Then open http://localhost:3000.

Granular alternatives:

npm run dev:client     # just Vite (no multiplayer worker)
npm run dev:worker     # just wrangler
npm run dev:lan        # vite --host + wrangler (LAN-accessible — for mobile testing)

npm test               # Vitest suite
npm run test:ios-water # BrowserStack real iOS Safari water canary
npm run build          # production output to dist/

URL params for fast scene picking + shoot setup:

  • ?scene=field — Home Field
  • ?scene=rolling-hills — Rolling Hills
  • ?scene=open-country — Open Country
  • ?scene=newsheepdogland — Newsheepdogland survival island (experimental, performance tuning ongoing)
  • ?cinematic=1 — exposes window.__sdsCinema for scripted captures + free-fly camera + tone-map override
  • ?ui=off — hide React overlay (canvas-only render)
  • ?sun=N — N in 0..1 (0.06 = dusk, 0.20 = golden hour, 0.50 = noon)
  • ?renderer=webgpu — request the production WebGPU path where the browser can create a device; this is also the default on capable browsers
  • ?renderer=webgl — force the WebGL fallback path
  • ?webgpuNativeTreeImpostors=1 — opt into the Cycle 38 explicit three-tier WebGPU tree route for review

Controls

Input Desktop Mobile
Move W A S D / Arrows Virtual joystick (bottom-left)
Sprint Shift (drains stamina; locks until released after empty) Sprint button above joystick
Bark Space / gamepad RB Bark button on the HUD
Camera mode C (cycles Classic / Follow / Free) Tap the camera-mode chip on the HUD
Zoom Mouse wheel Slider (bottom-right)
Pause Escape Pause button on the HUD
Gamepad Full analog support

Camera modes: Classic (high-isometric, world-axis WASD), Follow (close cinematic chase, camera-relative WASD, ridge-clearance lift), Free (mouse-yaw orbit on top of Follow's pitch). Per-scene preference is remembered in localStorage.


Architecture (one-page version)

┌─────────── CLIENT (Cloudflare Pages) ──────────────────────────────────┐
│                                                                         │
│  StartScreen → GameState → OptimizedSheep → SceneManager                │
│       ↓             ↓             ↓              ↓                      │
│  MobileControls  InputHandler   Sheepdog    TerrainBuilder              │
│       ↓             ↓             ↓              ↓                      │
│  AudioManager   GamepadManager  GrassSystem   Atmosphere + Effects      │
│       │             │             │              │                      │
│       │             │             │              ├── HosekWilkieSky     │
│       │             │             │              ├── CloudLayer         │
│       │             │             │              ├── SunBillboard       │
│       │             │             │              ├── PortalEffect       │
│       │             │             │              └── CorralZapEffect    │
│       └─────────────┴──── shared/ deterministic kernels ──────────┐     │
│                       (boid, MovementPhysics, BoundaryCollision,  │     │
│                        TreePlacement, SceneObstacles, scenes/)    │     │
│                                                                   │     │
│                    NetworkManager                                 │     │
│            (WebSocket + MessagePack + fetch, adaptive jitter buffer)    │
└────────────────────────────────┬────────────────────────────────────────┘
                            HTTPS │ WSS
┌────────────────────────────── ▼ ── SERVER (Cloudflare Worker) ─────────┐
│                                                                         │
│  index.ts                                                               │
│    ├── HTTP:  /api/register, /api/rooms, /api/score, /api/leaderboards  │
│    └── WS:    /r/:code/ws → RoomDO                                      │
│         ↓                                                               │
│  RoomDO   — per-room, runs GameSim at 60 Hz, broadcasts state frames    │
│  LobbyDO  — singleton, public lobby list + quick-match + room codes     │
│         ↓                                                               │
│  D1 (sds-db)                                                            │
│    ├── players                  (materialized best per mode + identity) │
│    ├── discriminators           (#0001 allocation)                      │
│    └── score_submissions        (audit trail)                           │
└─────────────────────────────────────────────────────────────────────────┘

The shared/ modules import byte-identically into both client (Vite) and worker (esbuild) — flocking, obstacle queries, boundary collision, tree placement, the multi-stage objective state machine (round-up → drive on Open Country), and scene definitions all live there so solo and multiplayer agree on physics. The sim-baseline tests pin deterministic Field + island-boundary + corral-retirement + objective-stage runs to JSON fixtures and have stayed bit-identical across cycles 5–34.

Full diagrams + network protocol + module-level details: ARCHITECTURE.md.


Tech stack

Client: Three.js 0.184 WebGL/WebGPU renderer paths · React 19.2 · Vite 7.3 · Tailwind 4.1 · @msgpack/msgpack 3 · i18next 25 · lz-string · nipple.js · kdbush

Server: Cloudflare Workers · Durable Objects · D1 · wrangler 4

Shared: shared/ deterministic boid + physics + obstacle modules, imported by both runtimes

Testing: Vitest 4.1 covering renderer adapters, atmosphere, heightfield, scene-obstacles, island-boundary, tree-placement, sim-baseline, refactor-baseline characterization goldens, worker D1 validation, integration harness, practice-mode contracts, SEO, and water shoreline math. ESLint enforces the shared/ deterministic boundary (npm run lint). Playwright covers browser smoke and perf-baseline harnesses; BrowserStack covers the real iOS Safari water canary.


Current state

We work in numbered cycles; player-visible ships get a vN.N.N tag with a CHANGELOG entry. The last cycles delivered everything you see today; here's where the surface is moving right now:

  • v2.3.0 (2026-06-09) - Hardening release: interactive tutorial in 5 languages, 9 achievements, control rebinding, wire protocol v3 (changed-sheep delta frames with soft-degrade for older clients), backpressure eviction, server-minted identity (P-SEC-1/2), and full-room rejoin rehydration.
  • v2.2.5 (2026-06-09) - Mobile WebGPU primary hotfix: WebGPU-capable mobile browsers stay on the production WebGPU path for Newsheepdogland, and the homestead terrain mesh now covers the spawn so the dog no longer snaps to the water/skirt surface.
  • v2.2.4 (2026-06-09) - Cycle 83 wolf/bark/night polish: wolves are larger and clearer, bark reaches sheep and wolves at the intended medium/long distances, bark audio unlocks from the bark command, and Newsheepdogland night is darker with the visual sun below the horizon at NIGHT_T.
  • v2.2.3 (2026-06-09) - Newsheepdogland feel-and-hero release: desktop WebGPU flagship proof on the 3070, shorter survival pressure, validated two-dog survival co-op, and the Newsheepdogland homestead/pen/grass hero as the entrance default.
  • Cycle 54 closed (2026-06-04) - Windows Electron distributor path: installer/portable/unpacked artifacts, app identity, logs/crash paths, signing-ready posture, packaged WebGL/WebGPU proofs, native resize proof, and Steam/store handoff. Local Steam depot dry-run is plausible; public store submission is still gated on signing policy, metadata, install QA, screenshots/capsules, controller/cloud-save policy, and release-channel decisions.
  • v2.2.0 (2026-06-03) - forward-only license transition: current source is AGPL-3.0-or-later, current assets are CC BY-SA 4.0, visible AGPL source notices are in the app, and the first Windows Electron / Capacitor Android native-shell proof is documented.
  • v2.1.10 (2026-05-28) - Cycle 42 WebGPU material parity: warmer sun/sky, darker water, and octahedral tree-impostor proof.
  • v2.1.6 (2026-05-16) — Cycle 38 tree-placement readability patch: deterministic cross-zone canopy spacing removes stacked tree clumps, and tighter scale jitter keeps production trees from reading as saplings.
  • v2.1.5 (2026-05-16) — Cycle 38 WebGPU tree-impostor packet: branch/leaf-preserving tree rebakes, explicit three-tier WebGPU tree route behind ?webgpuNativeTreeImpostors=1, dynamic impostor tile plumbing, and refreshed desktop/Android proof artifacts. Desktop proof is green; Android WebGPU remains budget-red and is not a mobile-ready claim.
  • v2.1.4 (2026-05-10) — real iOS Safari water validation via BrowserStack + shoreline-based water shader, removing the fragile depth pre-pass.
  • v2.1.3 (2026-05-09) — public-surface pass: crawler body content, per-scene landing pages, sitemap fix, footer links, and repo topic refresh.
  • v2.1.1 (2026-05-08) — OG card refresh: new Rolling Hills dusk + Field farmhouse social-share images. _headers cache TTL added so future asset refreshes propagate fast at the CF edge.
  • v2.1.0 (2026-05-08) — Practice Paddock (30-sheep no-pressure mode at position 0 of the mode picker, with first-visit pulsing-glow nudge gated by localStorage) + per-scene SEO (document.title + full og:* + twitter:* switch on every scene change).
  • v2.0.5 — deleted dead AtmosphericDesatPatch machinery (~190 LOC) — final piece of the Cycle 25 polish-program cleanup.
  • v2.0.4 — extended Apple tone-mapping branch from Mac to iPhone/iPad to fix the iOS water-sheen wash.
  • v2.0.3 — Mac white-hue fix (ACES → Neutral on Mac platforms; the sky-blue fog was pushing toward white through ACES + extended-sRGB on Metal-ANGLE).
  • v2.0.0 (Cycle 25 close) — eight-phase polish-mega-cycle: validation infrastructure, LOD truth (drop LOD1 desktop), HeightFogPatch foundation, per-mode camera zoom + persistence, per-scene tree distribution profiles, shimmer-skeleton scene-swap overlay.

CHANGELOG.md has the full per-version log; docs/BACKLOG.md keeps per-cycle headlines; DECISIONS.md is the chronological "why" record.


Roadmap — where help would move the game

The backend on Cloudflare's edge is solved-as-far-as-it-needs-to-be. The visual layer is now progressively WebGPU-backed on supported browsers while retaining WebGL fallback. Android WebGPU frame budgets and tree representation quality are still active engineering work. Each item below is a real PR-able piece of work — issue numbers welcome.

  • Steam/desktop release lane. The Windows Electron package boots and plays from built web assets in both WebGL and WebGPU. The useful next step is not a new shell; it is release discipline: signing choice, installer/uninstaller QA, Steam depot dry-run, store metadata, capsule/screenshots, controller/cloud-save policy, and a repeatable native preflight.
  • Dynamic weather + time-of-day as gameplay levers. The Hosek-Wilkie sky already supports dawn/noon/dusk/golden/overcast presets; wiring a DayNightCycle driver where sheep flocking tightens at dusk and visibility drops in fog turns atmosphere into mechanic.
  • More multi-stage objectives beyond Open Country's gather→drive. River crossings, predator-flushing, lost-sheep recovery, scattered sub-flocks at multiple cardinal directions — each unlocked by data in shared/scenes/*.js rather than new code paths.
  • Predators + NPCs. Wolves/strays as obstacle-aware boids using the same SceneObstacles index sheep already query. A second AI shepherd that competes or assists.
  • Mod-friendly scene format. Sandbox already uses lz-string-encoded URLs; growing this to full scene descriptions (heightmap + tree zones + woods clusters + objective def) so a custom biome ships as a link.
  • Production octahedral tree impostors. Cycle 38 deliberately keeps the WebGPU tree route opt-in while desktop/Android evidence improves. The next useful version is a real view-dependent impostor or hybrid trunk/branch geometry plus impostor canopy, with grounded pivots, tile selection, relighting, LOD transition hysteresis, and screenshot gates before it replaces native LODs on mobile.
  • Competitive seasons + tournaments once the leaderboard has enough player history to make them meaningful.

Contributing

This codebase is deliberately easy to read. Whether you want to mod the game, use it as a reference for a 3D browser project of your own, or ship a PR, here's the recommended reading order:

  1. ARCHITECTURE.md — module map, render pipeline, network protocol
  2. DEVELOPMENT.md — local setup, dev servers, mobile testing, mp testing
  3. DECISIONS.md — chronological "why we made the calls we did"
  4. docs/BACKLOG.md — per-cycle "what shipped" headlines
  5. docs/INTERFACE_FENCE.md — files where stability matters more than ergonomics
  6. docs/README.md — full doc navigation index (Diátaxis-tagged) — start here if you want a full map of the docs tree

Good first issues — concrete things a PR could fix

  • More languages. i18n keys live in js/locales/; PRs add a new directory + JSON files. Today: 5 languages (en, es, ja, pt, zh-CN). Want German? French? Korean? Open a PR.
  • Dog GLB compression via gltf-transform + Draco. Each dog is 25–40k triangles; five dog models. Trims the bundle.
  • Real-device mobile WebGPU proof. v2.2.5 passes mobile-emulated Chrome WebGPU for Newsheepdogland's Play flow and terrain spawn. Run the same proof on Matt's actual phone after deploy, then keep reducing Android draw-call and sprint-start cost where the measured device budget still says to.
  • MP joiner renderer sync. Joiners whose URL-param scene differs from the room's see correct sim but mismatched visuals.
  • LOD2 → LOD0 cross-fade at the 100 m boundary. The hard pop is visible — alpha-dither / fade across a 5–10 m hysteresis band would soften it.
  • Small-window HUD polish. Native resize is proven, but very small desktop windows still deserve a dedicated HUD comfort pass before public desktop release.
  • Mod gallery. If you ship a fork or mod and comply with the AGPL/asset notice requirements, open an issue and we can link it from this README.

How to ship a PR

  1. Fork → clone → branch off main
  2. npm install && npm run dev to confirm it boots locally
  3. npm test should be green before opening — it runs in ~1.5s
  4. Open the PR with a brief description of intent + what you tested
  5. Cycle plans live in docs/cycle-N-plan.md — if your work spans a phase or two, it's reasonable to coordinate via an issue first; small PRs don't need to

We use squash-merge with a [type](scope): summary first-line convention (see git log --oneline for examples).


License

Source code is licensed under GNU AGPL-3.0-or-later. Non-code assets are licensed under Creative Commons Attribution-ShareAlike 4.0. See LICENSING.md for the forward-only transition details.

The in-app notice appears on the about page, the start/loading flow, and the in-game HUD. Modified versions must preserve these notices in reasonably visible locations.

If you build something with this codebase — a fork, a mod, an academic project, a stream — we'd love to hear about it. Open an issue and we'll link it from this README.


Links

About

A browser-based 3D sheep herding game with solo leaderboard modes, 2-4 player online co-op and competitive rooms, split-screen, a sandbox editor, and full mobile + gamepad support. Three.js + React + Cloudflare Workers. Play at sheepdogsim.com. AGPL-3.0.

Topics

Resources

License

AGPL-3.0, Unknown licenses found

Licenses found

AGPL-3.0
LICENSE
Unknown
LICENSE-ASSETS

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors