← Farboard

Four ways to pass a chess move, and why Farboard uses Cloudflare

Two phones in different places need something in between to pass moves. Four designs were considered: WebRTC peer-to-peer, a relay on a rented server (VPS), AWS serverless, and Cloudflare Durable Objects. This page compares them. Architecture explains the one that won.

WebRTC VPS relay AWS serverless Cloudflare Durable Objects (chosen)
The requirements

What any design has to do

Every design below has to meet the same bar. These come from what Farboard already promises players (see the home page and Architecture):

1. Opening connections

Both phones contact the server

Phone A creates a game and shares an invite link or QR code. When Phone B opens it, B loads the page and initiates its own connection to join the same game. A also initiates its own connection. The invite gives B the details it needs to join.

Read the arrows: each arrow points from the phone opening a connection (or sending a setup request) to the service receiving it. The Cloudflare arrow from Worker to Room shows routing. Page downloads and server responses are omitted.

Connection setup in four designs Phone A and Phone B each contact the services themselves. In WebRTC, both contact signalling and STUN services and can request TURN relay candidates, then test paths with ICE. For a VPS, both open sockets to your server. For AWS, both open sockets to API Gateway. For Cloudflare, both send WebSocket upgrades to the Worker, which routes them to the same Room. WebRTC contact setup services Phone A Phone B Signalling server STUN server TURN relay Then both peers test candidate paths (ICE). VPS relay open WebSockets Phone A Phone B Your server holds both sockets AWS serverless open WebSockets Phone A Phone B API Gateway holds both sockets The backend records the room and connection IDs. Cloudflare open WebSockets Phone A Phone B Worker routes each upgrade same room ID Room · one per game accepts both sockets

WebRTC: signalling carries offers, answers and candidate addresses in both directions. STUN helps discover a public address; TURN supplies a relay path if a direct path cannot be used (orange dashed arrows). ICE tests the candidate paths between peers; those checks are omitted here. See WebRTC connectivity.

For the three WebSocket designs, each phone opens a connection that can then carry messages in both directions. Farboard's full joining sequence shows the requests and replies over time.

2. Sending a move

An example move from Phone A to Phone B

Both players are now connected. Phone A plays e2 → e4 and sends the encrypted move. The arrows below follow that message to Phone B over the connections already established. When B plays, the message travels back the other way.

Read the arrows: green shows the move being delivered; orange dashed arrows show the alternative TURN route. In AWS, grey arrows show the lookup and delivery calls needed to forward it.

How a move travels in four designs WebRTC sends the move directly from Phone A to Phone B, or through TURN if a relay path was selected. A VPS relay passes moves through one server you run. AWS invokes Lambda for the move; Lambda looks up the peer in DynamoDB and asks API Gateway to deliver it. Cloudflare passes the move directly through the Room holding both existing sockets; the Worker only routes the initial connection. WebRTC direct or relayed after setup Phone A Phone B TURN relay direct, if the networks allow relay path selected VPS relay one server process that you run Phone A Your server one region · you patch it Phone B AWS serverless three services for every move Phone A API Gateway Lambda DynamoDB Phone B find the peer delivery over B’s open socket Cloudflare one object per game CHOSEN Phone A Phone B Room · one per game holds both sockets

AWS: API Gateway invokes Lambda. Lambda looks up B’s connection ID in DynamoDB, then calls PostToConnection on API Gateway to deliver the move over B’s existing socket. AWS WebSocket API overview.

Cloudflare: the Worker routes the initial upgrade; subsequent messages are handled by the Room over its accepted sockets. See Cloudflare WebSockets. The Cloudflare lane has the same shape as the VPS lane: one thing in the middle holds both players. The difference is that Cloudflare runs it, one per game, and puts it to sleep between moves.

The candidates

The four designs

1. WebRTC peer-to-peer

ruled out

The phones talk to each other directly over a WebRTC data channel. A signalling server introduces them first, and STUN helps each phone find its public address. It sounds as if it needs no server, and the data channel is encrypted by default.

Why not: direct connections fail on some mobile and office networks, which then need a TURN relay. You still run a signalling server as well, so it takes two servers to avoid one. More below.

2. VPS relay

the familiar one

A small rented server runs a WebSocket process. Both players connect to it, and it forwards each move to the other socket. The code is short and the model is the one most web developers already know.

Why not: a monthly fee whether anyone plays or not. You also own the operating system updates, TLS certificates, restarts and monitoring. It sits in one region, and a one-button copy for other people is much harder.

3. AWS serverless

many moving parts

API Gateway holds the WebSockets. Each message starts a Lambda, which looks up the other player's connection in DynamoDB and sends the move with PostToConnection. IAM roles and infrastructure as code tie it together.

Why not: about $1 a month at 100 games a day, and it keeps billing if usage spikes. Budget alerts warn you but do not stop spending. Cost breakdown.

4. Cloudflare Durable Objects

chosen

A Worker routes each game to its own Durable Object, called a Room. The Room holds both WebSockets and forwards moves with one send(). The code is shaped like a VPS relay, but there is no server to run.

Why: $0 on the free plan. 100 games a day uses about 2% of the allowance, and at the limit it pauses instead of billing. It hibernates between moves, and one button deploys a copy. The trade-off is that it's specific to Cloudflare.

Side by side

How they compare on what matters

WebRTC VPS relay AWS serverless Cloudflare (today)
Servers or services to set up Signalling + TURN One server API Gateway, Lambda, DynamoDB, IAM One Worker, one class
Works on every network Only with TURN Symmetric NAT and UDP-blocking firewalls stop direct links Yes Plain HTTPS WebSocket Yes Plain HTTPS WebSocket Yes Plain HTTPS WebSocket
After a phone sleeps The connection has to be negotiated again through the signalling server Reconnect to the same server Reconnect; a new connection id goes into DynamoDB Reconnect to the same Room by name
Who sees your IP address Your opponent, unless every packet goes through TURN Only the server Only AWS Only the relay
Running it Two services, and debugging connections that work on one network but not another OS updates, TLS certificates, restarts, monitoring Several consoles, IAM policies, budgets Nothing to patch; logs and metrics in one dashboard
Cost at 100 games a day Small. Chess uses little TURN bandwidth, but the signalling and TURN services still have to be hosted somewhere A fixed monthly fee even when nobody plays ≈ $1 a month $0
If usage spikes Depends where the two servers run Slows or drops connections; the bill stays fixed Keeps billing Online play pauses until 00:00 UTC; never billed
A copy of your own Hard: two services to deploy Hard: rent and set up a server Hard: an AWS account and a stack to deploy One Deploy to Cloudflare button
Lock-in Low: an open standard None High: three AWS-only services Medium: Durable Objects exist only on Cloudflare, but the relay is one small file (relay/src/room.js)

AWS and Cloudflare figures come from Architecture, which also covers Vercel.

Why not WebRTC

Peer-to-peer isn't always reachable

WebRTC lets two browsers connect directly, but only if their networks allow it. Each phone asks a STUN server what its public address looks like, then both try to punch through their routers to reach each other (a process called ICE). That works on most home networks. It often fails when:

Those connections need a TURN server that relays every packet. By common estimates that's roughly 10–25% of real-world sessions (Fora Soft, BlogGeek.me). So a WebRTC design is really three parts: a signalling server, a TURN relay, and the direct link when it works.

For a video call, avoiding a relay saves real bandwidth. Chess moves are a few hundred bytes each, so that saving doesn't matter. WebRTC would add a second way of connecting to test and debug, and it would show each player's IP address to the other. A relay was needed either way, so the simplest design was a relay for everyone.

Old ideas, new economics

The winner is a familiar design, now almost free to run

None of the ideas behind the Cloudflare design are new. What changed is how little they cost to run.

The actor model (1970s) and Erlang (1980s)

Each piece of state lives in its own small process, and only that process can change it. A Durable Object is exactly this: one Room per game, the only code that can change that game's seats. That rules out locks and database race conditions.

The stateful game server

It's what you would write on a VPS: one process holds both connections and forwards moves. A Room is that process, one per game, running on Cloudflare's network instead of on a machine you rent.

Isolates, not containers

Workers run in V8 isolates, the same sandbox a browser gives each tab. One starts in milliseconds and uses little memory, so a single machine can hold thousands of them.

Hibernation

A Room sleeps between moves while its WebSockets stay open at the edge. A chess game spends almost all of its time waiting for someone to think, so it costs almost nothing.

AWS built serverless by combining separate services, each billed on its own. Cloudflare built a smaller set of building blocks around one cheap unit, the isolate, and gave state a home right next to it. Cloudflare also builds its Agents SDK on the same building block, one Durable Object per AI agent. That suggests the design will be supported for a long time.

How it was chosen

A note from the author

I've run VPS servers for 20 years, so a VPS relay was my natural choice. AWS serverless felt like it needed a certification just to understand the moving parts. I didn't know Cloudflare's design existed until an AI coding assistant suggested it, together with the VPS and WebRTC options on this page.

The AI helped in three ways. It showed me the option existed. It built a working version quickly enough that trying it was worth the time. And it explained Durable Objects well enough that I could judge the design myself. Without that, I'd have stayed with the VPS, because testing and designing something unfamiliar would have cost more time than it could save.

The choice was still mine. I made it by comparing working versions instead of guessing.

Old ideas, new economics tells the longer story.

Sources and notes

WebRTC connection figures are estimates from Fora Soft and BlogGeek.me; see also MDN: Introduction to WebRTC protocols. AWS and Cloudflare costs are from Architecture, checked on 26 September 2026.