Transports

martini-kit is transport-agnostic - swap networking backends without changing game code. Choose based on your deployment needs.

Available Transports

LocalTransport

@martini-kit/transport-local

Use case: Local development, testing, side-by-side demos

In-memory transport for same-page multiplayer. Perfect for testing and demos where multiple “players” run in the same browser.

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

const transport = new LocalTransport();

Features:

  • Zero network latency
  • No server required
  • Perfect for development
  • Side-by-side player views

Limitations:

  • Same browser only
  • Not suitable for production

When to use:

  • Local testing
  • IDE dual-view mode
  • Demos with multiple tabs
  • Development

TrysteroTransport

@martini-kit/transport-trystero

Use case: P2P prototypes, classrooms, serverless games

WebRTC peer-to-peer transport using Trystero for signaling. No server needed - players connect directly.

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

const transport = new TrysteroTransport({
  appId: 'my-game-v1',      // Unique app identifier
  roomId: 'room-123',       // Room/session ID
  password: 'optional-pwd'  // Optional room password
});

Configuration

interface TrysteroConfig {
  appId: string;        // Unique app ID (change for incompatible versions)
  roomId: string;       // Room/session ID
  password?: string;    // Optional password
  relayUrls?: string[]; // Custom STUN/TURN servers
}

Features:

  • No hosting costs
  • Low latency (direct connections)
  • URL-based host selection
  • Works offline on LAN

Limitations:

  • NAT/firewall issues (~5-10% of users)
  • Host must stay online
  • No persistence
  • Not suitable for large-scale games
NAT Traversal

WebRTC works ~90% of the time. Some school/corporate networks block P2P connections. Use WebSocket transport for production.

When to use:

  • Quick prototypes
  • Classroom demos
  • Local LAN parties
  • Low-cost MVPs

WebSocketTransport

@martini-kit/transport-ws

Use case: Production deployments, reliable connectivity

Server-based transport using WebSockets. Reliable, works everywhere, requires server hosting.

pnpm add @martini-kit/transport-ws
import { WebSocketTransport } from '@martini-kit/transport-ws';

const transport = new WebSocketTransport({
  url: 'wss://game.example.com',
  roomId: 'room-123',
  autoReconnect: true
});

Configuration

interface WebSocketConfig {
  url: string;              // WebSocket server URL
  roomId: string;           // Room/session ID
  autoReconnect?: boolean;  // Auto-reconnect on disconnect (default: true)
  reconnectDelay?: number;  // Delay between reconnect attempts (ms)
}

Features:

  • Reliable on any network
  • Works behind firewalls
  • Server can add auth, matchmaking, persistence
  • Scalable

Limitations:

  • Requires server hosting
  • Slightly higher latency than P2P
  • Operating costs

When to use:

  • Production games
  • Commercial deployments
  • Locked-down networks
  • Games requiring matchmaking/auth

IframeBridgeTransport

@martini-kit/transport-iframe-bridge

Use case: IDE sandboxing, embedded games

Iframe-to-parent communication via postMessage. Enables the martini-kit IDE’s dual-view mode.

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

const transport = new IframeBridgeTransport({
  targetOrigin: 'https://ide.martini-kit.com'
});

Features:

  • Sandboxed execution
  • IDE integration
  • Dual-view testing

Limitations:

  • Specific to iframe embedding
  • Not for general use

When to use:

  • martini-kit IDE integration
  • Embedded game platforms
  • Sandboxed testing

Transport Selection

NeedRecommended Transport
Local testingLocalTransport
Quick prototypeTrysteroTransport
Production gameWebSocketTransport
Classroom demoTrysteroTransport
Behind firewallWebSocketTransport
IDE integrationIframeBridgeTransport

Transport Interface

All transports implement the same interface. This is what makes them swappable.

interface Transport {
  // Send messages
  send(message: WireMessage, targetId?: string): void;

  // Receive messages
  onMessage(
    handler: (message: WireMessage, senderId: string) => void
  ): () => void;

  // Peer lifecycle
  onPeerJoin(handler: (peerId: string) => void): () => void;
  onPeerLeave(handler: (peerId: string) => void): () => void;

  // State
  getPlayerId(): string;
  getPeerIds(): string[];
  isHost(): boolean;

  // Lifecycle
  disconnect(): void;

  // Observability (optional)
  metrics?: TransportMetrics;
}

Platform Configuration

When using initializeGame(), the transport is auto-selected based on __martini-kit_CONFIG__:

// Set by platform (IDE, hosting platform, etc.)
window.__martini-kit_CONFIG__ = {
  transport: {
    type: 'local' | 'iframe-bridge' | 'trystero' | 'websocket',
    roomId: 'room-123',
    isHost: true,
    // ... transport-specific config
  }
};

// initializeGame() reads this and creates the right transport
initializeGame({ game, scene, phaserConfig });

For manual setup:

const transport = new LocalTransport();
const runtime = new GameRuntime(game, transport, {
  isHost: true,
  playerIds: ['p1']
});

Migration Path

Development → Production:

// Development
const transport = new LocalTransport();

// Prototype
const transport = new TrysteroTransport({ appId: 'game', roomId: 'test' });

// Production
const transport = new WebSocketTransport({ url: 'wss://game.com', roomId });

Game code stays identical - just swap the transport.


Next Steps

Examples

Working transport examples: