# Realtime

> Build realtime Cloudflare applications using Durable Objects with WebSockets for data realtime and Cloudflare Realtime/WebRTC for audio and video. Use for chat rooms, multiplayer, collaboration, presence, live dashboards, and media-session routing.

- Skill: `zllovesuki/realtime` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zllovesuki/realtime`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zllovesuki/realtime/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: zllovesuki (https://skillmd.com/u/zllovesuki)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/zllovesuki/realtime

---

# Realtime

Use this skill for realtime application design. Separate **data realtime** from **media realtime** before writing code.

## Data vs media

- Data realtime: chat, presence, multiplayer state, collaborative editing, live dashboards. Prefer Durable Objects + WebSockets.
- Media realtime: audio/video/screen share over WebRTC. Prefer Cloudflare Realtime/SFU patterns.
- Do not use WebSockets as the primary transport for audio/video media unless you have a very specific reason.

## Durable Object room model

A room-like Durable Object serializes membership and broadcasts messages to connected clients.

```text
Worker route /rooms/:id/ws
  -> derive room ID from URL and authenticated tenant
  -> env.ROOM.getByName(`tenant:${tenantId}:room:${roomId}`)
  -> DO accepts WebSocket and stores/broadcasts room events
```

## WebSocket handler sketch

```ts
import { DurableObject } from "cloudflare:workers";

export class Room extends DurableObject<Env> {
  private sockets = new Set<WebSocket>();

  async fetch(request: Request): Promise<Response> {
    const pair = new WebSocketPair();
    const [client, server] = Object.values(pair);

    server.accept();
    this.sockets.add(server);

    server.addEventListener("message", (event) => {
      const text = typeof event.data === "string" ? event.data : "";
      for (const socket of this.sockets) {
        if (socket.readyState === WebSocket.OPEN) socket.send(text);
      }
    });

    server.addEventListener("close", () => this.sockets.delete(server));
    server.addEventListener("error", () => this.sockets.delete(server));

    return new Response(null, { status: 101, webSocket: client });
  }
}
```

Validate this sketch against current hibernation/WebSocket APIs before production.

## Production rules

- Authenticate before selecting the room object.
- Include tenant ID in the object key for multi-tenant apps.
- Rate-limit inbound messages.
- Validate message schema and size.
- Persist important events before broadcasting if clients must be able to replay.
- Use heartbeats/presence timeouts carefully; clients vanish without clean close events.
- Design for reconnect and resume.

## Realtime media rules

- Use WebRTC/SFU patterns for audio/video.
- Treat packet loss and jitter differently from data delivery; stale media frames should usually be dropped, not retried.
- Keep application state/signaling separate from media transport.
- Store recordings or generated media in R2; store metadata in D1.

## Anti-patterns

- A single global room object for all tenants.
- Trusting client-supplied room IDs without authz.
- Broadcast before validation.
- Using KV to track live presence.
- Assuming every connected socket will close cleanly.

