PushFlo
Back to Blog
4 min readBy Marek

The Complete Guide to Real-time Multiplayer Games on Serverless

How to build multiplayer browser games using serverless architecture. Covers game state sync, player presence, turn-based and real-time patterns with practical code examples.

multiplayergame-developmentserverlesswebsocketreal-time

Building multiplayer games traditionally means running dedicated game servers 24/7, managing socket connections, and handling all the complexity of real-time synchronization.

But what if you could build multiplayer games using the same serverless architecture you use for your web apps? No servers to manage, pay only for what you use, and scale automatically from 2 players to 2,000.

This guide covers everything you need to know about building multiplayer games on serverless platforms like Vercel and Cloudflare Workers.

The Architecture

Multiplayer games on serverless use a different architecture than traditional game servers — for a deeper look at why, see why serverless can't hold WebSocket connections. The key insight: separate the WebSocket connections from the game logic, using a serverless WebSocket service to handle the persistent connections.

  • PushFlo (or similar) handles the WebSocket connections
  • Your serverless functions handle game logic
  • A database (Redis, DynamoDB, Supabase) stores game state

What Types of Games Work on Serverless?

Great Fit

  • Turn-based games — Chess, card games, word games
  • Casual multiplayer — Trivia, drawing games, party games
  • Slow-paced real-time — Tower defense, idle games, tycoons
  • Social games — Chat-based games, werewolf, mafia

Challenging but Possible

  • Fast-paced real-time — Shooters, racing (requires careful optimization)
  • Physics-based — Depends on complexity and update rate

Not Ideal

  • Twitch shooters — Sub-16ms requirements are hard to meet
  • Large-scale MMOs — Sheer volume of state updates

This guide focuses on turn-based and casual real-time games, which cover 90% of indie multiplayer projects.

Building a Multiplayer Game: Step by Step

Let's build a real-time multiplayer Tic-Tac-Toe game. Simple enough to understand, but the patterns apply to any game.

Step 1: Define the Game State

// types/game.ts
interface GameState {
  id: string;
  board: (string | null)[]; // 9 cells: 'X', 'O', or null
  players: {
    X: string; // player ID
    O: string | null; // null if waiting for opponent
  };
  currentTurn: 'X' | 'O';
  status: 'waiting' | 'playing' | 'finished';
  winner: 'X' | 'O' | 'draw' | null;
  createdAt: string;
  updatedAt: string;
}

Step 2: Create the Game Logic (Pure Functions)

// lib/game-logic.ts
export function createGame(playerId: string): GameState {
  return {
    id: generateId(),
    board: Array(9).fill(null),
    players: { X: playerId, O: null },
    currentTurn: 'X',
    status: 'waiting',
    winner: null,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };
}

export function makeMove(
  game: GameState,
  playerId: string,
  position: number
): GameState {
  // Validate and apply move...
  // Check for winner...
  return updatedGame;
}

Step 3: Real-time Updates with PushFlo

// lib/realtime.ts
export async function publishGameUpdate(game: GameState) {
  await fetch('https://api.pushflo.dev/api/v1/publish', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.PUSHFLO_SECRET_KEY}`,
    },
    body: JSON.stringify({
      channel: `game-${game.id}`,
      event: 'update',
      data: game,
    }),
  });
}

Step 4: Client-Side Game Component

// components/TicTacToe.tsx
'use client';

import { useState, useEffect } from 'react';
import { PushFloClient } from '@pushflodev/sdk';

export function TicTacToe({ gameId, playerId }: Props) {
  const [game, setGame] = useState<GameState | null>(null);

  useEffect(() => {
    // Fetch initial state
    fetch(`/api/games/${gameId}`)
      .then(res => res.json())
      .then(setGame);

    // Subscribe to updates
    const client = new PushFloClient({
      publishKey: process.env.NEXT_PUBLIC_PUSHFLO_PUBLISH_KEY!,
    });

    client.subscribe(`game-${gameId}`, {
      onMessage: (updatedGame: GameState) => {
        setGame(updatedGame);
      },
    });

    client.connect();

    return () => client.disconnect();
  }, [gameId]);

  const makeMove = async (position: number) => {
    // Send move to server
    await fetch(`/api/games/${gameId}/move`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ playerId, position }),
    });
  };

  // Render game board...
}

Advanced Patterns

Pattern 1: Optimistic Updates with Reconciliation

For responsive gameplay, update the UI immediately and reconcile with server state.

Pattern 2: Player Presence

Show who's in the game room using presence channels.

Pattern 3: Game Timers

For timed turns, use server-authoritative timers.

Pattern 4: Matchmaking Queue

For games with matchmaking, implement a queue system.

Database Options

DatabaseBest ForLatencyCost
Vercel KV (Redis)Fast reads/writes<10ms$
SupabasePostgres + Realtime<50ms$
PlanetScaleMySQL, scaling<50ms$$
DynamoDBAWS ecosystem<20ms$$

Conclusion

Building multiplayer games on serverless is absolutely viable for:

  • Turn-based games
  • Casual real-time games
  • Social/party games
  • Games with <100ms latency requirements

The architecture is simpler than traditional game servers:

  • No servers to manage
  • No scaling to worry about
  • Pay only for actual usage
  • Deploy alongside your web app

The key is separating concerns: let PushFlo handle WebSocket connections, your serverless functions handle game logic, and your database handle state.


Ready to build your multiplayer game? Start with PushFlo free — perfect for game prototypes and indie projects.

Build your multiplayer game today

PushFlo is perfect for game prototypes and indie projects — start free.