Skip to content

P2P Real-Time Data

Pulse P2P lets your users collaborate on structured, shared data — kanban boards, whiteboards, counters, diagrams — with changes syncing instantly between browsers.

Why P2P?

Pulse's existing features (comments, cursors, presence) flow through the server via WebSocket. This is perfect for persistent data that needs to be stored and queried.

P2P adds a second data path for high-frequency, collaborative state — data that changes rapidly and needs to sync with minimal latency:

WebSocket (existing):   Browser ←→ Server ←→ DB    (comments, presence, threads)
P2P (new):              Browser ←←← P2P ←→→ Browser  (boards, drawings, app state)

Both work together in the same room. Your users get comments, cursors, and presence over WebSocket plus shared data structures over peer-to-peer — all from one SDK.

How It Works

Signaling

P2P connections are established through Pulse's existing WebSocket. When two users are in the same room and P2P is enabled:

  1. User A sends a WebRTC offer through the Pulse server
  2. Server relays it to User B (no inspection, just forwarding)
  3. User B responds with an answer
  4. ICE candidates are exchanged
  5. A direct peer-to-peer connection is established

After signaling, data flows directly between browsers — the server is not involved.

CRDT Conflict Resolution

P2P data is backed by Yjs CRDTs (Conflict-free Replicated Data Types). This means:

  • No conflicts — two users can edit the same data simultaneously
  • Automatic merging — changes converge to the same state regardless of order
  • Offline support — Yjs buffers updates and syncs when reconnected

You don't need to implement conflict resolution. The SDK handles it.

Transport Fallback

When a room has too many users (above the configurable mesh limit, default 6), new peers automatically fall back to WebSocket relay. The shared data structures work identically over both transports — your code doesn't change.

Auto-Persistence

CRDT state is automatically saved to the server every 5 seconds (configurable). When a user joins a room later, the persisted state is loaded first, then live updates sync on top. Close all browsers, come back tomorrow — everything's there.

What You Get

FeatureDescription
SharedMapKey-value store — like a JS Map that syncs across all peers
SharedListOrdered array — push, insert, delete, move with automatic conflict resolution
SharedCounterDistributed counter — increment/decrement from any peer, always converges
Raw Data ChannelsSend any message directly to peers — build your own sync protocol
Auto-PersistenceState snapshots saved to server, restored on late join
WebSocket FallbackSeamless fallback when P2P can't connect or room is too large

Architecture

┌─────────────────────────────────────────────────┐
│                   Your App                       │
│                                                  │
│   SharedMap    SharedList    SharedCounter        │
│       ↓            ↓             ↓               │
│   ┌──────────────────────────────────────────┐   │
│   │            Pulse P2P Manager             │   │
│   │  ┌─────────────┐  ┌──────────────────┐   │   │
│   │  │ PeerManager │  │   SyncEngine     │   │   │
│   │  │ (WebRTC)    │  │   (Yjs + CRDT)   │   │   │
│   │  └──────┬──────┘  └────────┬─────────┘   │   │
│   └─────────┼──────────────────┼─────────────┘   │
│             │                  │                  │
│     ┌───────┴───────┐  ┌──────┴──────┐           │
│     │ P2P DataChannel│  │ WS Fallback │           │
│     │ (direct)      │  │ (via server) │           │
│     └───────────────┘  └─────────────┘           │
└─────────────────────────────────────────────────┘

Next Steps

Pulse Collaboration SDK