artwork

Clear Connection, Wassily Kandinsky, 1925

PlaySocket

PlaySocket is a WebSocket-based synchronization library built for creating collaborative experiences, such as multiplayer games.

The two unique aspects of PlaySocket are its CRDT-based architecture that allows for optimistic updates without any extra logic, and its synchronized storage that works beautifully with reactive frontend frameworks such as React or Svelte.

Moreover, the library enables rapid prototyping, as complete multiplayer logic can be client-only during active development with server-side validation added later.

Security and reliability are top priorities of PlaySocket. Out of the box, it protects against XSS attacks and includes message rate limiting and automatic reconnection handling.

The library is lightweight, relying only on WS and MessagePack.

The problem

When building a UX that combines user input with server requests, latency becomes troublesome. It feels odd when a UI update isn't immediate.

Instant

Delayed

Optimistic updates – updating the UI before the request resolves, and reverting on error – are what developers reach for in these scenarios.

When there's only one user making changes to an interface at a time, this is relatively trivial to do. The real complexity arises when multiple users can interact with an interface simultaneously.

Ordering complexity

Let's think through a scenario where two users collaborate to pick a color in an optimistically-updated user interface:

  1. Michael selects green, and his UI shows green.
  2. Lana selects blue, and her UI shows blue.
  3. Server receives green and broadcasts it to all clients.
  4. Lana's UI now shows green, Michael's already does.
  5. Server receives blue and broadcasts it to all clients.
  6. Lana's UI now shows blue again, Michael's now shows blue.

The issue with this flow is that Lana's UI briefly flashes green, even though that color was selected by Michael before her selection.

PlaySocket uses a vector clock to avoid this issue – with it, Lana's client, upon receiving Michael's color update, knows that her selection is newer than Michael's and her UI remains unchanged.

Merging complexity

Ordering isn't the only thing that can go wrong when multiple users act at once. Let's think through a scenario where two players add items to a shared, optimistically-updated inventory at the same time:

  1. The inventory starts out as torch.
  2. Michael adds rope, and his UI shows torch, rope.
  3. Lana adds map, and her UI shows torch, map.
  4. Server receives Michael's inventory torch, rope and broadcasts it.
  5. Server receives Lana's inventory torch, map and broadcasts it.
  6. Both UIs now show torch, map.

The issue with this flow is that each client sends the entire new value of the invetory, so whichever update arrives last wins. Michael's addition to the inventory, the rope, is silently lost.

PlaySocket avoids this by describing changes as operations. Instead of setting the whole inventory, both clients send an array-add operation. The two changes simply combine, and every client converges to the expected result.

A quick example

The code below isn't quite production ready, but should give you an idea of how the library can be used to solve the inventory problem.

Client

import PlaySocket from 'playsocketjs';

const socket = new PlaySocket("michael's-id", {
    endpoint: "wss://example.com/socket"
});

socket.onEvent("storageUpdated", (storage) => {
	console.log("Current inventory:", storage.inventory);
}

// Assuming Lana has created a room
await socket.init();
await socket.joinRoom("lana's-room-id");

socket.updateStorage("inventory", "array-add", "rope");

Server

import PlaySocketServer from 'playsocketjs/server';

const server = new PlaySocketServer({ path: "/socket" });

The magic part here is that updateStorage() is synchronous. The storage updates – and storageUpdated fires – immediately. You don't need to think about handling optimistic updates yourself.

Get started

To get started with PlaySocket, please refer to the documentation.

Documentation GitHub repository