Private
Payloads are opaque. Your code runs on the clients; the network only orders and relays.
A relay mesh for chat, voice, games and shared state. Your clients send; every client gets the same stream in the same order. 100 GB/month free.

Live games running on the public network right now. Their worlds, voices and characters come down the same mesh anyone can build on.
rooftop.chat
A rooftop bar after dark, with a coworking floor beside it: take a public space or open a room of your own. Everyone stands in one world on one tick and talks over positional voice, and the bartenders and regulars are ARRR NPCs — they hear you, answer in their own voices, and remember you next time. 18+.
Ticks · Voice · NPCs
mecharoyale.com
Crewed mechs in a shrinking arena, on a phone or a desktop. Every client steps the same simulation from the same ordered ticks, crews call shots over push-to-talk, and players sign in with ARRR accounts — Google, Discord or guest.
Ticks · Voice · Accounts
Shipped something on the network? Tell us and it goes on this page.
Users served today
—
Loading the last 14 days…
An ordered log with join and leave events nobody can fake.
One upload per talker, a bounded download per listener, any room size.
Same operations, same order, on every client. No merge step.
Inputs batched into ticks. Snapshots for late joiners, desync detection built in.
Anything with a WebSocket can publish. Every subscriber reads the same sequence.
JSON, bytes or audio. What it means is yours.
One connect(), then read the stream as messages, ticks, or voice.
import { connect } from 'arrr-network';
const room = await connect('support-42', {
appId: 'my-app',
apiKey: 'arrr_…', // console → Apps
user: { id: 'alice' },
onMessage(data, seq) { // same order on every client
log.append(seq, data);
}
});
room.send({ text: 'hello' });import { connect } from 'arrr-network';
const room = await connect('duel-42', {
appId: 'my-app',
apiKey: 'arrr_…', // console → Apps
user: { id: 'alice' },
onTick(frame, inputs) { // one batch per tick, identical everywhere
for (const input of inputs) world.apply(input.data);
world.step();
}
});
room.send({ move: { x: 1, y: 0 } });import { connect } from 'arrr-network';
const room = await connect('lounge', {
appId: 'my-app',
apiKey: 'arrr_…', // console → Apps
user: { id: 'alice' },
onVoice(from, seq, x, y, z, frame) {
speakers.play(from, frame); // one encoded frame from a nearby talker
}
});
room.sendVoice(x, y, z, opusFrame); // while talking
room.sendVoice(x, y, z); // every 500 ms while quiet: position only<script src="https://www.arrr.fun/sdk/arrr-network.iife.js"></script>
<script>
(async () => {
const room = await arrrNetwork.connect('support-42', {
appId: 'my-app',
apiKey: 'arrr_…', // console → Apps
user: { id: 'alice' },
onMessage(data, seq) { log.append(seq, data); }
});
room.send({ text: 'hello' });
})();
</script>send() takes JSON or a Uint8Array. The API key comes from the console; see API Keys.
Create an app in the console and pass its API key to connect(). The network hosts your rooms.
Mint a node token in the console and run arrr-node with it. Your node joins the mesh and hosts rooms.
onMessage or onTick; what it means is your code's business.Voice skips the queue. Audio rides its own channel on the same socket, unordered, forwarded to the nearest listeners by position. Nothing waits for a tick.