Skip to content

P2P Quick Start

Get peer-to-peer shared data working in 3 steps.

Prerequisites

  • Pulse server running with P2P enabled in the environment config
  • A publishable key (pk_) and user token

Enable P2P

P2P is disabled by default. Enable it from the Admin Panel → Environment Settings → P2P / Real-time Data → toggle "Enable P2P data channels".

1. Connect with P2P Enabled

Using the Widget

html
<pulse-widget
  api-key="pk_..."
  token="eyJ..."
  room="my-board"
  endpoint="wss://pulse.example.com"
  p2p="true"
></pulse-widget>

Using the Client SDK

typescript
import { PulseClient } from '@gamention/pulse-core';

const client = new PulseClient({
  apiKey: 'pk_...',
  token: userToken,
  room: 'my-board',
  endpoint: 'wss://pulse.example.com',
  p2p: true,
});

client.connect();
OptionTypeDefaultDescription
p2pbooleanfalseEnable P2P data channels
iceServersRTCIceServer[]Google STUNCustom STUN/TURN servers

TURN Credentials

If your environment has a TURN server configured in the admin panel, the SDK automatically fetches time-limited credentials from the server. You don't need to pass iceServers manually.

2. Access the P2P Manager

The P2P module is lazy-loaded — it only downloads when you first access it. This keeps the base SDK lightweight.

typescript
const p2p = await client.p2p;

This returns a P2PManager instance after:

  1. Waiting for authentication to complete
  2. Loading the P2P module (Yjs + WebRTC code)
  3. Fetching TURN credentials (if configured)
  4. Bootstrapping persisted state from the server

3. Create Shared Data

typescript
const p2p = await client.p2p;

// Create shared data structures
const board = p2p.sharedMap('kanban');
const taskOrder = p2p.sharedList('task-order');
const likes = p2p.sharedCounter('likes');

// Set data — syncs to all peers instantly
board.set('task-1', { title: 'Design homepage', column: 'doing' });
taskOrder.push('task-1');
likes.increment();

// Listen for changes (local or remote)
board.on('change', (changes) => {
  for (const change of changes) {
    console.log(`${change.action}: ${change.key}`, change.value);
  }
});

That's it. Any user in the same room with P2P enabled will see the same data, updated in real-time.

Full Example: Collaborative Counter

A minimal example — two users increment a counter and see updates instantly:

typescript
import { PulseClient } from '@gamention/pulse-core';

const client = new PulseClient({
  apiKey: 'pk_...',
  token: userToken,
  room: 'counter-demo',
  endpoint: 'wss://pulse.example.com',
  p2p: true,
});
client.connect();

const p2p = await client.p2p;
const counter = p2p.sharedCounter('votes');

// Display current value
document.getElementById('count').textContent = counter.value;

// Update on changes from any peer
counter.on('change', (value) => {
  document.getElementById('count').textContent = value;
});

// Increment on button click
document.getElementById('vote-btn').addEventListener('click', () => {
  counter.increment();
});

What Happens Under the Hood

  1. client.connect() — opens a WebSocket to the Pulse server, authenticates
  2. client.p2p — lazy-loads the P2P module, bootstraps persisted state
  3. WebRTC peer connections are established with other users in the room
  4. sharedMap/sharedList/sharedCounter — creates Yjs-backed data structures
  5. Mutations sync via P2P DataChannels (or WebSocket fallback if P2P unavailable)
  6. State auto-persists to the server every 5 seconds

Next Steps

Pulse Collaboration SDK