Playertwo Live Demo

This is a live Pong game running directly in the documentation, demonstrating the playertwo state-first architecture.

Controls: W/S = Left paddle | Arrow Up/Down = Right paddle

How This Works

This demo shows playertwo’s core pattern — even without a network transport:

  1. State is a plain object: paddles (position + score) and ball (position + velocity)
  2. Actions process input: move actions update paddle positions
  3. Physics runs on the “host”: ball movement, collisions, scoring
  4. Rendering reads state and draws — no game logic in the renderer

In a real multiplayer setup, you’d replace the shared state with a LocalTransport (for testing) or TrysteroTransport (for P2P WebRTC). The game code stays identical.

// Same game, now multiplayer
import { defineGame, GameRuntime } from '@playertwo/core';
import { TrysteroTransport } from '@playertwo/transport-trystero';

const game = defineGame({
  setup: ({ playerIds }) => ({
    paddles: Object.fromEntries(
      playerIds.map((id, i) => [id, { y: 200, score: 0, side: i === 0 ? 'left' : 'right' }])
    ),
    ball: { x: 300, y: 200, vx: 4, vy: 3 },
  }),
  actions: {
    move: {
      apply(state, context, input) {
        state.paddles[context.targetId].y += input.dy;
      },
    },
  },
});

const transport = new TrysteroTransport({ roomId: 'pong-room', appId: 'ptero-demo' });
const runtime = new GameRuntime(game, transport, { isHost: transport.isHost() });

Embedding in Ptero Docs

Ptero uses MDsveX, so you can embed any HTML via iframes or Svelte components directly in documentation pages. For playertwo demos, the pattern is:

  1. Build a standalone game HTML page (canvas or Phaser)
  2. Place it in static/demos/
  3. Embed with <iframe src="/demos/your-game.html" />

For the full multiplayer experience with dual-view testing, use the @playertwo/ide component with the iframe-bridge transport.