Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rommapp/romm/llms.txt

Use this file to discover all available pages before exploring further.

RomM’s netplay feature lets you play retro games online with friends in real time. It uses WebRTC for peer-to-peer game data exchange, with RomM’s own Socket.IO server acting as the signaling layer that helps players find each other and establish direct connections.

How It Works

1

Room creation

The host player opens a ROM in the EmulatorJS player and creates a netplay room. The room is registered in RomM’s Redis-backed room store with a unique session ID, a room name, an optional password, and a configurable player cap (default: 4).
2

Signaling

RomM’s Socket.IO endpoint at /netplay/socket.io acts as the signaling server. It brokers the initial WebRTC handshake: players exchange ICE candidates, SDP offers, and SDP answers through the server without any game data passing through it.
3

Peer-to-peer gameplay

Once the WebRTC connection is established, game inputs, snapshots, and data messages flow directly between players’ browsers — the RomM server is no longer in the data path.
4

Room teardown

When all players leave or disconnect, the room is automatically deleted from Redis. If the host leaves early, ownership is transferred to the next connected player.
Netplay works best with low-latency connections. High latency or packet loss will cause noticeable input lag and desynchronization. For the best experience, keep geographic distance between players minimal and use a wired connection where possible.

Enabling Netplay

Netplay is disabled by default. Enable it under the emulatorjs.netplay key in your config.yml:
emulatorjs:
  netplay:
    enabled: true

Configuring ICE Servers

WebRTC requires at least one STUN server to discover public IP addresses, and typically a TURN server to relay traffic when a direct peer-to-peer connection cannot be established (e.g. behind symmetric NAT). Configure your ICE servers under emulatorjs.netplay.ice_servers:
emulatorjs:
  netplay:
    enabled: true
    ice_servers:
      - urls: "stun:stun.relay.metered.ca:80"
      - urls: "turn:global.relay.metered.ca:80"
        username: "<username>"
        credential: "<password>"
In restricted network environments (corporate firewalls, carrier-grade NAT, strict home routers), a direct peer-to-peer WebRTC connection often cannot be formed without a TURN relay server. If players cannot connect to each other, ensure you have a reachable TURN server configured. Free-tier TURN credentials are available from providers such as Open Relay.

Playing with Friends

1

Open a ROM

Navigate to a game’s detail page and click Play to launch the EmulatorJS player.
2

Create a room

Click the Netplay button in the player toolbar. Give the room a name and, optionally, set a password to restrict who can join. The room is now listed publicly for other users browsing the same ROM.
3

Share the room

Send the session ID or room name to your friends. They open the same ROM, click Netplay, and select your room from the list.
4

Join and play

Once all players have joined, gameplay begins. Each player’s inputs are exchanged peer-to-peer via the established WebRTC data channels.
All players must have the same ROM file — specifically, files with matching content hashes. RomM uses the content hash to verify compatibility before allowing players to join a room.

Room Management API

RomM exposes a REST endpoint to inspect currently open rooms for a given game. This is the same data the EmulatorJS player UI uses to populate the room list.

List open rooms

GET /api/netplay/list?game_id={rom_id}
Returns a map of session_id → room_info objects. Only rooms that are not yet full and belong to the requested game are included. Example response:
{
  "abc123": {
    "room_name": "Mario Kart Friday Night",
    "current": 2,
    "max": 4,
    "player_name": "alice",
    "hasPassword": false
  }
}
FieldDescription
room_nameHuman-readable name set by the room owner
currentNumber of players currently in the room
maxMaximum number of players allowed (default: 4)
player_nameDisplay name of the room owner
hasPasswordWhether a password is required to join

Socket.IO Events

The real-time room lifecycle is managed over the Socket.IO endpoint at /netplay/socket.io. The table below describes the events your client or the EmulatorJS player exchanges with RomM’s netplay server.
EventDirectionDescription
open-roomClient → ServerCreate a new netplay room
join-roomClient → ServerJoin an existing room by session ID
leave-roomClient → ServerVoluntarily leave the current room
users-updatedServer → RoomBroadcast updated player list to all room members
webrtc-signalClient ↔ Server ↔ ClientRelay ICE candidates, SDP offers, and SDP answers
webrtc-signal-errorClient → ServerReport a WebRTC signaling error (no-op on server)
data-messageClient → RoomBroadcast arbitrary game data to all other players
snapshotClient → RoomBroadcast a game state snapshot
inputClient → RoomBroadcast controller input to all other players
disconnectServerPlayer disconnected; room updated automatically
Rooms are stored in Redis and survive a RomM web-process restart as long as your Redis/Valkey container keeps running. Players whose browsers stay connected will remain in the room; players who disconnect will be removed automatically via the disconnect handler.

Build docs developers (and LLMs) love