Skip to main content

Command Palette

Search for a command to run...

SpriteDX - Playground - Multiplayer Proof of Concept - Design v0.2

Updated
4 min read
SpriteDX - Playground - Multiplayer Proof of Concept - Design v0.2

Revising https://blog.sprited.app/spritedx-playground-multiplayer-design-v01.

So far, the keywords are:

  • WebRTC

  • Authoritative Server

  • Portable Architecture

  • Serverless Architecture

  • Support 4-12 players

What does this mean in practice? I will need to build or use architecture that is able to instantiate on the fly then serve the game in seconds. We would also need to pre-warm servers such that the game servers can handle traffic without load and that game servers can handle multiple gameplay sessions rather than just one.

Provider Options? I’m currently looking at Cloudflare Durable Objects which allow for WebSocket connections and advertises that it is able to handle multi-player games. I am not sure if it support full WebRTC level stuff. However, this makes it highly versatile choice. There is also a free tier option which basically allows for me to experiment the ideas in free manner.

Alternatively, I could go the ephemeral VM route or bare metal. However, there is no true scale-to-zero situation. Technically I could have it always running for $10 dollars per month. However, every now and then, you will be asked to upgrade something and if nobody ends up using, it is so easy to turn it off to save cost.

I want true scale-to-zero solution and I trust the Cloudflare brand. Let me get started with Cloudflare Durable objects since I’m already using Cloudflare workers, Cloudflare tunnels, Cloudflare DNS, etc.

What’s next? I will create a more thorough implementation plan.


Proof-of-Concept — Implementation Plan

First, we will build a proof of concept.

POC Goal

Create a shared stage where:

  • Users are auto-matched by region

  • They join an available room inside a Durable Object

  • Each Durable OBject can host multiple gameplay rooms.

  • Rooms exist only while players are present

  • Infrastructure scales to zero naturally

  • No VM management, no orchestration

Core Concepts

  1. Region-based sharding
    Use cloudflare colo / region hint (request.cf.colo)
    Players in the same region prefer the same DO instance
    Do not over-optimize — best-effort is enough

  2. Durable Object = “Stage Host“
    One DO instance hosts N gameplay rooms

    DO lifetime is independent of room lifetime
    Rooms are lightweight in-memory structures

  3. Room = lightweight state machine

    A room has roomId, players, state, lastActiveAt.

Architecture

Client ---HTTP--> Cloudflare Worker 
                         |
                    get/create DO
                         v
                   Durable Object 
                         |
                    websocket upgrade
                         v
                    Gameplay rooms

Steps

Step 1: Endpoint POST /matchmake

  1. Detect user region

  2. Resolve StageHost DO ID for region

  3. Ask DO for an available room

  4. Return { stageId, roomId, wsUrl }

Step 2: Durable Object = StageHost

  1. It maintains multiple rooms, Track occupancy

  2. Allocate players to rooms

  3. Clean up empty rooms.

rooms: Map<roomId, RoomState>
// where
RoomState = {
  players: Map<playerId, WebSocket>
  state: { positions, etc }
  lastActiveAt: number
}

Step 3: Room Allocation Logic

POST /allocate-room

Simple rules (POC)

  • Max players per room: e.g. 8

  • Reuse existing room if not full

  • Otherwise create a new room

  • Return connection info

Pseudocode

for (room of rooms.values()) {
  if (room.players.size < MAX_PLAYERS) {
    return room;
  }
}

createNewRoom();
return newRoom;

Step 4: WebSocket Join

wss://…/connect?roomId=abc&playerId=xyz

DO Behavior

  • Accept WebSocket

  • Attach socket to room

  • Send initial snapshot

  • Broadcast “player joined”

On disconnect

  • Remove player from room

  • If room empty → delete room

  • If DO has zero rooms, it becomes idle and may be evicted natrually

You do not manually destroy the DO.

Step 5: Room Update Loop

  • Event-driven, not fixed 60Hz

  • On player input:

    • Update state

    • Broadcast diff

  • Optional soft tick:

    • setTimeout every ~100ms

    • Reconcile positions

    • Broadcast snapshot

This keeps CPU low and predictable.

Step 6: Cleanup & Idle Behavior

Room cleanup: If players.size is zero, delete room. Keep no persistent state for POC.

DO lifecycle: When no WebSockets are open and no timers running, cloudflare may evict the DO.

Configuration Knobs

  • Players per room: 4-8

  • Rooms per DO: Unlimited bounded by memory)

  • Tick rate: 10Hz

  • Transport: WebSocket

  • Persistence: None

  • Auth: Anonymous (POC)

What POC intentionally does not do

  • No WebRTC

  • No Persistence

  • No global matchmaking

  • No cross-region migration

  • No cheat prevention

  • No reconnection logic beyond basics

A note to myself

Why am I focusing on Playground now? The holiday storm is approaching, and I need something sticky—something that keeps me emotionally attached to the project. Working on image upload, BYO templates, and pipeline editors would meaningfully improve completeness, but there’s a real risk of losing focus.

So, I’m going all-in on Playground.

My goal is simple:
build it, and have my kid play it with me at bedside.

That’s it.

— Sprited Dev 🌱

SpriteDX - Playground - Multiplayer Proof of Concept - Design v0.2