Skip to content

Getting Started

ARRR (Agentic Real-time Relay Routing) delivers everything a room's clients send to every client in one order. You bring the app, whether that is a chat, a voice room, a collaborative tool or a game; the network keeps everyone in step. This page installs the SDK, connects a room, and builds a chat in one file.

Install the SDK

The package on npm is arrr-network. It is an ES module, runs in Node.js 18 or newer and in any browser bundler, and has no runtime dependencies beyond ws for Node.

bash
npm install arrr-network
js
import { connect } from 'arrr-network';
html
<!-- Defines the global arrrNetwork; arrrNetwork.connect is the same connect() -->
<script src="https://www.arrr.fun/sdk/arrr-network.iife.js"></script>

The browser bundle above is the one this site serves. To host your own copy, clone arrr-sdk and run npm run build:browser; the file lands in dist/.

Get an app id and a key

On the public network a room belongs to an app. Sign up at the cloud console, create an app, and generate its API key; both go into connect(). Against a local service in open mode you can skip the key and use any appId. Details in API Keys.

Connect

js
const room = await connect('lobby', {
  appId: 'app_…',                       // from the console
  apiKey: 'arrr_…',                     // from the console; required on the public network
  user: { id: 'alice' },                // who this client is; shows up in join/leave events
  onTick(frame, inputs) {               // every client gets the same batch, in the same order
    for (const input of inputs) apply(input.data);
  }
});

room.send({ move: { x: 1, y: 0 } });    // becomes someone's input.data next tick

The service picks a node for the room and the SDK connects to it. The default service is https://cloud.arrr.fun; pass centralServiceUrl to use your own.

Options you will use

OptionWhat it does
appIdThe app the room belongs to. Required.
apiKeyThe app's key. Sent to the service, not to other players. Required when the service runs APP_REGISTRATION=key, which the public network does.
userAn object with at least id. It is what other clients see in join, leave, disconnect and reconnect events, and what lets a dropped client reconnect as itself.
centralServiceUrlWhere to ask for a node. Default https://cloud.arrr.fun.
onConnect(snapshot, inputs, frame, node, fps, clientId)You are in. snapshot and inputs are what a late joiner needs to catch up; fps is the room's tick rate, or 0 for a room with no clock, where a tick arrives only when someone sent something.
onTick(frame, inputs)The ordered batch for one tick. Each input has data (what someone sent), clientId, seq and frame. Games live here.
onMessage(data, seq)The same inputs, one at a time, without frames. Enough for chat and tools.
onDisconnect() / onError(message)The socket dropped, or the service refused you (a bad key is an onError).
getStateHash()Return a hash of your world each tick; the nodes compare hashes across clients and flag desync.
sendSnapshot(snapshot, hash) / onSnapshotPublish your world so late joiners start from it instead of replaying everything.
sendVoice(x, y, z, data) / onVoicePositional voice on the same socket, relayed to the nearest listeners and never into the simulation.

The connection has send(data), leaveRoom() (a permanent exit, which others see as leave), close() (a drop, which others see as disconnect and you can reconnect from), plus connected and clientId.

Session events

Join and leave are inputs like any other, generated by the node so nobody can fake them. They arrive in onTick and onMessage with a type:

js
{ type: 'join',       clientId: '…', user: { id: 'alice' } }
{ type: 'leave',      clientId: '…', user: { id: 'alice' } }   // called leaveRoom()
{ type: 'disconnect', clientId: '…', user: { id: 'alice' } }   // socket dropped
{ type: 'reconnect',  clientId: '…', user: { id: 'alice' } }   // came back as the same user

Example: a chat in one file

html
<!DOCTYPE html>
<body>
  <div id="messages"></div>
  <input id="text" placeholder="Type a message..." onkeypress="if(event.key==='Enter')send()">
  <button onclick="send()">Send</button>

  <script src="https://www.arrr.fun/sdk/arrr-network.iife.js"></script>
  <script>
    const me = 'guest-' + Math.random().toString(36).slice(2, 6);
    let chat;

    (async () => {
      chat = await arrrNetwork.connect('lobby', {
        appId: 'app_…',                 // your app, from the console
        apiKey: 'arrr_…',               // its key
        user: { id: me },
        onConnect() { log('Connected as ' + me); },
        onMessage(data) {
          if (data.type === 'join') log(data.user.id + ' joined');
          else if (data.type === 'leave' || data.type === 'disconnect') log(data.user.id + ' left');
          else if (data.text) log(data.from + ': ' + data.text);
        },
        onError(message) { log('Error: ' + message); }
      });
    })();

    function send() {
      if (!text.value.trim() || !chat) return;
      chat.send({ from: me, text: text.value });
      text.value = '';
    }

    function log(msg) {
      const div = document.createElement('div');
      div.textContent = msg;              // never innerHTML with other people's text
      messages.appendChild(div);
    }
  </script>
</body>

Save it as index.html, serve it with npx serve ., and open it in two tabs. Everything each tab sends arrives in both, in the same order.

Running your own service

Against a local cloud service (npm run dev in arrr-cloud, then a node), pass centralServiceUrl: 'http://localhost:9001'. In its default open mode any appId works with no key.

From chat to a game

A game is the same connection with onTick instead of onMessage: run your simulation one step per tick, applying that tick's inputs first. Keep the simulation deterministic (same inputs, same result, on every machine), return a hash of the world from getStateHash, and publish snapshots with sendSnapshot so a player who joins late lands on the current frame. The live demos do exactly this; harness.js in the SDK repo's e2e fixtures is the tick loop to copy.

Next steps

  • API Keys: register your app for the public network
  • Live demos: a first-person shooter, a top-down shooter and a chat sharing state across tabs
  • What is ARRR?: how the network orders inputs, and what it does and does not guarantee
  • SDK on GitHub: source and the full option list