← All work

Multiplayer 3D web game

MoonLite

MoonLite is a free multiplayer 3D "after-hours park": strangers make music together on a shared timeline, watch films under an open sky, race go-karts, bowl and play chess. I built all of it alone, from the netcode and the audio engine to the servers and the legal pages, and took it from first commit to public launch in about three weeks.

Role
Solo: design, engineering, audio, infrastructure, security
Timeline
June 2026 – present · live since July 2026
Platform
Any modern browser (Windows, macOS, Linux, iOS, Android) · Electron + Steam build

The challenge

Crowded 3D rooms usually die the same way: one player moves, the server tells everyone, everyone moves, the server tells everyone. Per-event fan-out grows with the square of the room size.

Worse, crowd traffic shares a channel with the one thing that must never glitch: music. If an emote storm can delay a mix command, the product is broken. And it all had to run on hardware an indie studio can afford.

Key decisions

  1. 01

    Three traffic planes that never share a buffer

    A reliable control plane for state that must never be lost, a lossy binary presence plane (8-byte position frames, a 15 Hz server tick, area-of-interest culling and delta snapshots) and a batched reaction plane. Lossy traffic is dropped under backpressure; control traffic never is. Per-event server work is bounded by the tick rate, not by how hard clients send.

  2. 02

    Mini-games with no frames on the wire

    All twelve attractions (chess, 8-ball, bowling, pinball, go-karts, rides, a synced cinema...) are pure, deterministic step(state, inputs) functions shared by browser and server. Only tick-stamped inputs travel; a late joiner's whole state can be as small as 54 bytes. As a bonus, the sims are tested like a library, and the tests caught real bugs.

  3. 03

    Media bytes never touch the server

    Audio stems are content-addressed (SHA-256) and resolve from a local IndexedDB cache, then Cloudflare R2 at zero egress. A shared transport clock keeps every client's playback aligned. I first built a full WebRTC peer-to-peer layer for this, then deleted it: NAT traversal rarely worked on mobile networks, it leaked peer IPs, and R2 already made it free.

  4. 04

    A browser audio engine from scratch

    A multi-stem mixer with Ableton-style auto-warp, pitch-preserving WSOLA time-stretch in Web Workers, an AudioWorklet wavetable synth with an FX bus, and a beat-grid arrangement DAW with waveform clips, a piano roll and collaborative editing.

  5. 05

    Production, security and operations end to end

    Cloudflare Pages plus a ~68-route Worker API (D1, KV, R2), a multi-VPS relay fleet with drain-based rolling deploys and client auto-update, Google OAuth, CSP/HSTS, an admin console behind Cloudflare Access with an audit log, and end-to-end encrypted DMs with a hybrid post-quantum scheme (X25519 + ML-KEM-768). I also wrote the threat model and the EN/TR GDPR–KVKK legal set.

Results

480
concurrent players on a €4/month VPS
~2 ms
worst server tick at full load (budget: 66 ms)
0
dropped connections in load tests
~53k
lines of strict TypeScript, solo
418
automated tests
900 → 12
draw calls after batching the scenery

What I learned

  • Decide which traffic may be dropped and which may never be, and give them different pipes.
  • Put the simulation in a pure function and test it like a library. Netcode bugs are mostly sim bugs with latency on top.
  • Measure on the real box through the real path before buying a bigger one. My "scale" problem turned out to be a room-cap setting.
  • Be willing to delete the part you're proudest of. Removing the P2P layer made the product better.

Multiplayer 3D web game

MoonLite

MoonLite key art: a full moon over a starry night sky

1 / 3

Contact

Have a project or a role in mind?

I'm open to freelance projects and full-time roles, remote or in İstanbul. Write me a few lines about what you need; I usually reply within a day.