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
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 enoughDurable Object = “Stage Host“
One DO instance hosts N gameplay roomsDO lifetime is independent of room lifetime
Rooms are lightweight in-memory structuresRoom = 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
Detect user region
Resolve StageHost DO ID for region
Ask DO for an available room
Return
{ stageId, roomId, wsUrl }
Step 2: Durable Object = StageHost
It maintains multiple rooms, Track occupancy
Allocate players to rooms
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 🌱


![[WIP] Digital Being - Texture v1](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fuploads%2Fcovers%2F682665f051e3d254b7cd5062%2F0a0b4f8e-d369-4de0-8d46-ee0d7cc55db2.webp&w=3840&q=75)

