logo

Babylon.js Market

PokerMultiplayer

tabletop-casino · poker-multiplayer

PokerMultiplayer

1
Unlock

Install with the CLI:

bjs download PokerMultiplayer

PokerMultiplayer

PokerMultiplayer turns a single-player poker table into a networked one without touching the table. It is the poker translator that rides on a P2PNetwork peer network — the game-agnostic WebRTC layer where every seat is equal, no host, no client — and bridges remote actions and deals onto the EventBus, so the same PokerTableDirector that conducts a table against NPCs conducts it against humans, unchanged.

What it does

Two browsers can play the same hand without a server refereeing the game — but only if they deal the same cards. The trick is determinism: instead of one machine dealing and broadcasting every card, every machine shuffles the same deck from the same seed. Seed the shuffle with the room code plus the hand number and all seats compute the identical deck, so the only thing that has to cross the wire is intent — who folded, who raised, when to deal the next hand.

The shared seeded PRNG it deals from — a Mulberry32 generator (createGameRng from the room code + hand number) — lives one layer down in P2PNetwork.core, the game-agnostic networking primitive any multiplayer game can seed from. PokerMultiplayer's own deterministic half lives in PokerMultiplayer.core and is plain math: the seat-perspective maps that translate an absolute seat number into the local group a given browser renders it as (seatToGroupMP/groupToSeatMP, counting only occupied chairs), and remapCards, which rewrites the host-dealt card ids so each player sees their own hole cards at the local player slot and opponents at the right npc slots — never touching the shared river. None of it imports a renderer, a network, or the DOM, so every peer agrees by construction.

The System owns no wire — P2PNetwork does (the RTCPeerConnection network, the offer/answer signal poll during setup, the peer-to-peer DataChannel routing). PokerMultiplayer subscribes to that network's generic net.* traffic on the bus and translates it both directions:

  • a remote seat's action arrives as a P2P message and re-emits as poker.turnAction — the director's own input — attributed to that seat's local group;
  • a remote deal re-emits pokerTable.deal, so every browser deals the next hand on the same seed;
  • a late-joiner's sync adopts the table's current hand number, so its first deal lines up with everyone else's RNG.

Locally it listens for the human's own poker.actionTaken and for pokerTable.deal, and asks the network to relay them by emitting net.broadcast. P2PNetwork owns who's actually seated; PokerMultiplayer just mirrors the live occupiedSeats set from net.seatsChanged so its seat-perspective maths stays in step, and greets a joining peer (net.peerJoined) with the current hand number.

Because it holds no transport of its own, the System constructs and ticks cleanly anywhere — Node, SSR, a test — with no special guarding; the browser-only WebRTC code all lives one layer down in P2PNetwork.

Use it in a scene

Put PokerMultiplayer and the P2PNetwork peer network it rides on together on a Net entity, alongside the PokerTableDirector they feed. P2PNetwork carries the connection inputs (myPlayerId, existingPlayers, autoConnect); PokerMultiplayer carries the poker view (mySeat, occupiedSeats, handNumber).

A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.

Fastest path

bjs download scene PokerMultiplayer

Pulls this exact scene into your project and runs it — no copy-paste. Add --all to grab the components and assets it needs too.

Or build it from scratch with the bjs CLI:

  1. 1Scaffold a project
    npm create @babylonjsmarket/arcade@latest my-game

    World, renderer, and dev server — ready to run.

  2. 2Install dependencies
    cd my-game && npm install
  3. 3Add PokerMultiplayer
    bjs download PokerMultiplayer

    Copies its source into src/ so the scene resolves.

    First time? Run bjs login once.

  4. 4Paste the scene into src/scenes/arcade-room.ts and run
    npm run dev

    SceneLoader builds the world from the JSON; reload to rebuild.

JSON
{
  "Net": {
    "components": {
      "P2PNetwork": {
        "roomCode": "table-7",
        "mySeat": 0,
        "myPlayerId": "seat-0",
        "occupiedSeats": [0, 2],
        "autoConnect": true
      },
      "PokerMultiplayer": {
        "roomCode": "table-7",
        "mySeat": 0,
        "occupiedSeats": [0, 2],
        "handNumber": 0
      }
    }
  }
}

See example.scene.json for a full table — the director, a DeckDealer/PokerSeating dealer, a PokerBetting/PokerHandEval table, a CardFan, and the Net entity (both P2PNetwork + PokerMultiplayer) that wires it to a peer. (The example sets autoConnect: false so it loads headless; flip it to true in a live browser.)

When the network is up, the component bridges everything automatically: a local fold or raise broadcasts to the peers, a remote one lands on the director as a turn action, and a deal confirmation from any seat deals the same hand for all.

Props

These are PokerMultiplayer's props — the poker view of the table. The connection inputs (myPlayerId, existingPlayers, autoConnect, signalBasePath) live on the sibling P2PNetwork component.

PropTypeDefaultNotes
roomCodestring''Shared room id — also the RNG seed prefix for the local hand PRNG.
mySeatnumber0This client's absolute seat. Seat 0 is the host perspective the deck deals from.
occupiedSeatsnumber[][0]The seats live at the table; mirrored from the network's net.seatsChanged as peers join and leave.
handNumbernumber0Current hand — a late joiner adopts the table's via a sync message so its deal seeds identically.

Events

This component is a translator: it consumes the network's generic net.* traffic and the table's own poker events, and produces the table's input events plus relay requests back to the network. (The connection-state events — net.connected/net.peerJoined/net.peerLeft/net.seatsChanged — are emitted by P2PNetwork, not here.)

Emits:

NameWhenPayload
poker.turnActionA remote seat acted — re-emitted as the director's turn input, attributed to that seat's local group{ playerId, action }
pokerTable.dealA remote deal — re-emitted so every browser deals the same hand{}
net.broadcastRelay a message to the peers (the local action, the local deal, or a join sync){ msg }

Listens:

NameFromEffect
net.messageP2PNetworkA peer's {action} / {deal} / {sync} — translated into the rows above
net.seatsChangedP2PNetworkMirror the live occupiedSeats so the seat maps stay in step
net.peerJoinedP2PNetworkGreet the newcomer with the current hand number (a sync broadcast)
poker.actionTakenPokerBettingThe human's own action — relayed to the peers (only playerId === 'player')
pokerTable.dealPokerTableDirectorA local deal confirmation — relayed to the peers

The poker inputs are the existing PokerBetting and PokerTableDirector events, reused on purpose — the component listens to the table's own traffic rather than inventing parallel names. Only the local seat's own poker.actionTaken is relayed, so a remote action re-emitted as poker.turnAction never echoes back onto the wire.

Dependencies

  • P2PNetwork — the game-agnostic WebRTC network it rides on. PokerMultiplayer owns no transport; it talks to the peers entirely through P2PNetwork's net.* events (consuming net.message / net.seatsChanged / net.peerJoined, emitting net.broadcast).
  • PokerTableDirector — the match it networks. The component feeds remote actions and deals into the director's existing inputs and relays the local seat's outputs; the director never knows whether a seat is a human across the wire or an NPC in the same process.

Notes

  • Networking lives in P2PNetwork. This component is pure poker glue over the EventBus — it never imports RTCPeerConnection, fetch, or window. The network, the signaling, and the browser guard all live in the sibling P2PNetwork component, which polls the room server (/api/poker/room/[code]/signals by default, configurable via P2PNetwork's signalBasePath) only during the initial handshake.
  • Determinism is the whole design. Because every seat seeds its shuffle from roomCode:handNumber, nobody has to send card faces — only actions. Keep the seed inputs in lockstep (a joining peer is told the current handNumber) and the decks can't diverge.
  • The transport is injectable — on P2PNetwork. P2PNetworkSystem accepts a connectionFactory option (defaulting to the real WebRTC createMeshConnection); that seam is how the tests drive every message and membership branch with a fake transport. PokerMultiplayer itself needs nothing special to test — it's driven entirely by emitting net.* events on the bus.
  • Safe to instantiate anywhere. Holding no transport, PokerMultiplayer constructs and ticks in any environment; P2PNetwork's browserHasWebRTC() guard is what decides whether a real network actually opens.

More like this

P2PNetwork
PokerBetting
PokerInput
PokerTableDirector
BallReset
CardFan
DeckDealer
GameConfig
Goal
MissionDirector
PokerAI
PokerAIBill
PokerAICali
PokerAIRandy
PokerAIShark
PokerAISurge
PokerAITilt
PokerHUD
PokerHandEval
PokerSeating
PokerTableCards
SoccerDirector
TurnPacer
WaveDirector

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search