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

Revising [https://blog.sprited.app/spritedx-playground-multiplayer-design-v01](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

```plaintext
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.
    

```typescript
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

```typescript
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 🌱
