Skip to content

Project

Shard Islands

Real-time multiplayer 3D flight game in the browser, with a world generated entirely in code

Feel free to share my work. I'd really appreciate it, because I don't tend to share it enough myself.

Role
Mine end to end: the concept, the decisions on netcode and gameplay, and testing every section
Status
Live
A light-glider trailing a glowing wake over procedurally generated terrain in Shard Islands, with floating energy cores and a domed beacon on the horizon.
0.003cm
client/server drift over 280m of flight
6× faster
frame time after profiling, 4.3ms → 0.70ms
17 KB/s
wire cost, full 24-player room
20Hz
authoritative server tick

The idea

The home page is a frosted glass floor with a single glowing handprint on it. There's no text and no buttons. When you press it, the glass shatters into real 3D pieces, the camera falls through the gap, and about twelve seconds later you're flying over a generated world with other people in it.

There's no loading screen and no lobby. The game connects you the moment you click, while the glass is still breaking, so by the time it's finished falling you're already in. If the connection fails you just fly on your own and never see an error, because a spinner would spoil the whole effect.

Everything in the world is generated by code. There are no 3D models, textures or artwork files anywhere in the project.

Keeping the browser and server in step

The server runs the flight simulation, so it needs to know where the ground is and how a glider moves. Just sharing a few settings wasn't enough. Two separate versions of the same physics drift apart, and every bit of drift shows up as your glider being yanked back into place.

So the flight code and the terrain code exist only once, in a shared package used by both the browser and the server, and both run exactly the same maths on exactly the same inputs. The browser sends the player's controls, never their position. The server works out where everyone is and sends that back. Meanwhile the browser predicts ahead every frame, keeps track of the inputs the server hasn't confirmed yet, and when an update arrives it takes the server's position and replays those inputs on top.

Because both sides run the same code, the replay almost always lands exactly where the prediction did. I measured the drift at 0.003cm over 280 metres of flight. Any small difference is smoothed out rather than snapping. Other players are shown 100ms behind real time, blended between the two most recent updates.

How it's tested

Most of the tricky problems are about timing between two machines, and you can't check them by hand. Two people on two browsers can tell you someone's trail got cut, but not whether it happened for the right reason, or whether the near misses really were misses.

So the tests use automated players that fly set routes and check what happened. They confirm that the browser and server agree to within 0.003cm over 280m, that two players reaching the same energy core at the same moment only get it awarded once, that a core is still collected when someone crosses it at speed between updates, that player colours pair up properly with nine players connected, that trail cuts behave correctly across five staged set-ups, that an abandoned browser tab stops counting as a player, and that the live server turns away connections from sites it shouldn't accept.

Making it fast

I assumed the number of triangles would be what slowed it down. It wasn't. The terrain has 180,000 triangles and draws in a single pass at no real cost, and half a million more for the trees and grass made no measurable difference.

Three quarters of the time was going on 260 clouds, each with its own geometry and material, even though between them they only had 404 triangles. Sorting that out cut draw calls from 225 to 72, and each frame from 4.3ms to 0.70ms. A full room of 24 players now takes 95 draw calls and 1.05ms, which is less than a quarter of what an empty world cost before, and each extra player adds roughly one draw call. A full room uses 17 KB/s of bandwidth, and filling it slows the server down by just 0.6%.

Where it runs

The website runs on Next.js and React Three Fiber on Vercel. The game server is bundled into a single file with no dependencies, packaged in a small Docker image, and runs on one Fly.io machine.

It runs in one region deliberately. Each game room lives in a single process, so a second region wouldn't make the game any faster. It would just split players into two separate worlds.

Built with

  • Next.js
  • Three.js
  • React Three Fiber
  • WebGL
  • Colyseus
  • Fly.io

Next

E-commerce site for a small business selling digital cross-stitch patterns