Choosing a Transport

martini-kit supports multiple transport layers. This guide helps you choose the right one for your game.

Architecture Comparison

Peer-to-Peer (P2P) Architecture

┌─────────┐         ┌─────────┐
│ Player1 │ ←─WebRTC─→ Player2 │
│ (Host)  │         │ (Client)│
└─────────┘         └─────────┘
      ↓                  ↑
      └─────────WebRTC───┘
           Player3 (Client)

Pros:

  • ✅ No server costs
  • ✅ Low latency (direct connection)
  • ✅ Scales naturally
  • ✅ Easy deployment (static hosting)

Cons:

  • ❌ Host migration complexity
  • ❌ NAT traversal issues (some networks block P2P)
  • ❌ Host has all power (cheating concerns)
  • ❌ Limited to 2-8 players typically

Best for: Casual games, prototypes, cooperative games


Client-Server Architecture

┌─────────┐         ┌─────────┐
│ Player1 │ ←─WSS──→ │ Server  │
│         │         │ (Host)  │
└─────────┘         └─────────┘

┌─────────┐              │
│ Player2 │ ─────WSS─────┘
└─────────┘

Pros:

  • ✅ Server-authoritative (anti-cheat)
  • ✅ Reliable connections
  • ✅ Host never disconnects
  • ✅ Scales to many players (10+)

Cons:

  • ❌ Server hosting costs
  • ❌ Latency depends on server location
  • ❌ Requires backend infrastructure

Best for: Competitive games, large player counts, games requiring anti-cheat


Transport Options

LocalTransport (Development)

import { LocalTransport } from '@martini-kit/transport-local';

const transport = new LocalTransport({
  roomId: 'test-room',
  isHost: true
});

Use case: Local testing, development, CI/CD

Learn more →


TrysteroTransport (P2P Production)

import { TrysteroTransport } from '@martini-kit/transport-trystero';

const transport = new TrysteroTransport({
  appId: 'my-game-v1',
  roomId: getRoomCode(),
  isHost: determineIfHost(),
  config: {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' }
    ]
  }
});

Use case: Casual multiplayer, 2-8 players, no server costs

Room Management:

// Generate room code
function generateRoomCode(): string {
  return Math.random().toString(36).substring(2, 8).toUpperCase();
}

// Join room from URL
const params = new URLSearchParams(window.location.search);
const roomId = params.get('room') || generateRoomCode();

// Share room link
const shareableLink = `${window.location.origin}?room=${roomId}`;

API Reference →


WebSocket Server (Custom)

// Client-side
class WebSocketTransport implements Transport {
  private ws: WebSocket;
  
  constructor(url: string, roomId: string) {
    this.ws = new WebSocket(url);
    // ... implementation
  }
}

const transport = new WebSocketTransport('wss://your-server.com', 'room-123');

Use case: Production games, competitive, 10+ players

Custom Transport Guide →


IframeBridge (IDE Integration)

import { IframeBridgeTransport } from '@martini-kit/transport-iframe-bridge';

const transport = new IframeBridgeTransport({
  isHost: window.parent !== window
});

Use case: Embedded games, IDE previews

API Reference →


Decision Matrix

RequirementRecommended Transport
Local testingLocalTransport
2-4 players, casualTrysteroTransport (P2P)
5-8 players, casualTrysteroTransport (P2P)
10+ playersWebSocket Server
Competitive/rankedWebSocket Server
Anti-cheat requiredWebSocket Server
Zero server costsTrysteroTransport (P2P)
IDE integrationIframeBridge

NAT Traversal Considerations

P2P connections may fail on restrictive networks:

Solutions:

  1. Provide TURN servers (relay traffic when direct P2P fails)
  2. Fall back to WebSocket server for failed P2P connections
  3. Show clear error messages to users
const transport = new TrysteroTransport({
  appId: 'my-game',
  roomId: 'room-123',
  config: {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' },
      {
        urls: 'turn:your-turn-server.com:3478',
        username: 'username',
        credential: 'password'
      }
    ]
  }
});

Next Steps