velven
Docs
Menu

Rooms and presence

A room is a relay on Velven's servers for 2 to 16 signed-in players of your space: what one sends, the others get at once. You need no server of your own. Presence tells a player's friends what they are playing and lets them join.

View guide as Markdown

Open and join a room

A player opens a room and is its host; others join by its id. A player is in one room at a time, and has at most five rooms open; a room nobody enters within two minutes closes.

JavaScript
const made = await Velven.rooms.create({ visibility: "friends", maxPlayers: 4 });// { ok: true, room: { id, you, host, players, data, maxPlayers, visibility } }const open = await Velven.rooms.list();  // { ok: true, rooms: [{ id, visibility, host, players, maxPlayers, full, createdAt }] }const joined = await Velven.rooms.join(open.rooms[0].id);Velven.rooms.leave();
visibilitypublic, friends or privateDefault public
public: listed for every player of the space. friends: listed for the host's friends only, and only they may join. private: never listed; joined through an invite or its link.
maxPlayers2 to 16Default 8
The most players in the room at once. A join past it answers full.

Velven.rooms.current is the room the player is in, or null. list() answers full rooms too, marked full, so you can show them.

Invite friends

JavaScript
await Velven.rooms.invite("mara");           // a notification to a friend, with a Join buttonconst link = Velven.rooms.inviteLink();       // your space's Velven page with ?room=<id>, or null outside a room// Velven put the player in a room: they opened an invite link, or pressed Join.Velven.rooms.on("room", ({ room }) => enterLobby(room));

Only a player in the room, or the one who opened it, invites. An invite reaches only a friend: friends are mutual, made on Velven. invite answers { ok: true } whether or not the handle is a friend's, so an invite tells your space nothing about who is one. A friend is invited only when they may join: a friends room takes the opener's friends only. The link works for anyone signed in who may join the room. Whoever opens it lands on your space's Velven page and joins the room as the page loads; the room event tells your space, even when you start listening after the join.

Send and receive

JavaScript
Velven.rooms.send({ x: 3, y: 4 });            // JSON to everyone elseVelven.rooms.send(bytes, { to: playerId });   // or bytes (an ArrayBuffer or a typed array), to one playerVelven.rooms.on("message", ({ from, data }) => apply(from, data)); // data: JSON as sent, or an ArrayBufferVelven.rooms.chat("gg");                       // to everyone, you includedVelven.rooms.on("chat", (line) => showChat(line.handle, line.text)); // { id, from, handle, text, at, sig }await Velven.rooms.report(line, "harassment"); // a chat line as on("chat") heard it, to Velven's team

send, chat, setData, kick and leave answer at once: { ok: true }, or { ok: false, error } with not_in_room, not_host, too_large or too_long. A message is 16 KB at most and a chat line 500 characters. The relay signs each chat line (sig), and a report is taken only for a line it signed, so pass the line to report as you heard it.

The host

The player who opens the room is its host whenever they are in it. For the first 30 seconds nobody else hosts, so a player who joins before the opener's page connects does not take it; after that, a room without its opener is hosted by the player in longest. When the host leaves, the player in longest takes over, and the opener takes it back on coming in; everyone hears a host event. Only the host sets the room's data and removes players:

JavaScript
Velven.rooms.setData("round", 2);              // null deletes a keyVelven.rooms.on("data", ({ data }) => drawRound(data.round)); // everyone, the host too, hears the whole mapVelven.rooms.kick(playerId);                   // they cannot come back into this room

Room data is a map of keys of 1 to 64 characters, 64 KB in all, and a player who joins gets it in current.data. Keep what every player must agree on there, such as the round or the map; send moves as messages.

Events

Velven.rooms.on(event, handler) returns a function that stops listening.

Event
join
Payload
{ player, players }
When
A player came in. player is { id, handle, avatar, joinedAt }.
Event
leave
Payload
{ player, reason, players }
When
A player went; reason is left or kicked, and player their id.
Event
host
Payload
{ host }
When
The host changed; host is a player's id.
Event
message
Payload
{ from, data }
When
Another player sent to everyone, or to you.
Event
chat
Payload
{ id, from, handle, text, at, sig }
When
A chat line, yours included; sig is the relay's signature, which report sends along.
Event
data
Payload
{ data }
When
The host changed the room's data.
Event
room
Payload
{ room }
When
Velven put the player in a room: an invite link or Join.
Event
close
Payload
{ reason }
When
You are out of the room: left, kicked, replaced (you joined again from another tab), full, wrong_space or lost (the connection dropped).
Event
error
Payload
{ code, message }
When
The relay refused something, such as rate_limited, or a join from a link failed.

A small example

A page where everyone in a room sees each other's pointer. The first player opens a room and shares the link; whoever opens it joins.

index.html
<!doctype html><html><head>  <meta name="velven" content="@handle">  <script src="https://velven.ai/sdk/v1.js"></script></head><body>  <button id="open">Open a room</button>  <p id="status"></p>  <canvas id="board" width="640" height="400"></canvas>  <script>    const status = document.getElementById("status");    const ctx = document.getElementById("board").getContext("2d");    const pointers = new Map(); // player id -> { x, y }    function draw() {      ctx.clearRect(0, 0, 640, 400);      for (const [id, p] of pointers) {        ctx.fillStyle = id === Velven.rooms.current?.you.id ? "#fff" : "#888";        ctx.fillRect(p.x - 4, p.y - 4, 8, 8);      }    }    function enter(room) {      status.textContent = `In a room with ${room.players.length} of ${room.maxPlayers}. Share: ${Velven.rooms.inviteLink()}`;    }    document.getElementById("open").onclick = async () => {      const made = await Velven.rooms.create({ visibility: "private", maxPlayers: 4 });      if (made.ok) enter(made.room);      else if (made.error === "signed_out") await Velven.signIn();      else status.textContent = `Could not open a room: ${made.error}`;    };    Velven.rooms.on("room", ({ room }) => enter(room));    Velven.rooms.on("join", () => enter(Velven.rooms.current));    Velven.rooms.on("leave", ({ player }) => { pointers.delete(player); draw(); });    Velven.rooms.on("message", ({ from, data }) => { pointers.set(from, data); draw(); });    let last = 0;    document.getElementById("board").onpointermove = (e) => {      const room = Velven.rooms.current;      if (!room || e.timeStamp - last < 50) return; // 20 a second, under the relay's 30      last = e.timeStamp;      const p = { x: e.offsetX, y: e.offsetY };      pointers.set(room.you.id, p);      Velven.rooms.send(p);      draw();    };  </script></body></html>

The status line is set with textContent, so a handle is never read as markup. A real game keeps the same shape: moves as messages, shared state in the room's data, a lobby drawn from current.players.

Test with a friend

On localhost a room holds only you: chat and setData come back to you, send reaches nobody. To test with others, publish a preview and send them its link: anyone signed in who holds it can play there, and rooms opened in a preview stay in your space's sandbox, apart from the live space's rooms.

Limits

  • 2 to 16 players a room, and one room at a time per player.
  • A message is 16 KB at most, JSON or bytes. A chat line is 1 to 500 characters. Room data is 64 KB in all.
  • 30 messages a second per player, with a burst of 60. Past that the relay drops them and sends one error a second with rate_limited; send state at a steady rate rather than on every frame.
  • Per account: 30 rooms opened an hour and 30 room passes a minute.
  • A room closes 30 seconds after its last player leaves.

Error codes

Code
in_room, not_in_room
Meaning
Leave the room first, or join one.
Code
not_found, closed, full
Meaning
No such room in this space, it has closed (a room nobody enters within two minutes closes), or it is full.
Code
friends_only
Meaning
A friends room of someone who is not your friend.
Code
kicked
Meaning
The host removed this player from the room.
Code
not_host
Meaning
Only the host sets data or kicks.
Code
too_large, too_long
Meaning
A message over 16 KB, or a chat line over 500 characters.
Code
signed_out, unavailable, banned, rate_limited, failed
Meaning
As for scores.

Presence

While a signed-in player is on your space's Velven page, their friends see “Playing” and your space's name, in their friends list and on the player's profile; once they leave, “Last seen” and when. You need do nothing for this. Only friends see it.

Add a line of your own with presence.set:

JavaScript
await Velven.presence.set({ status: "Level 3, 2 lives left" }); // 80 characters at most; null clears it
  • When the player is in a room a friend may join, that friend also gets a Join button beside it, which opens your space's page and joins the room.
  • presence.set answers { ok: true }, or { ok: false, error } with signed_out, unavailable, too_long, rate_limited or failed. Up to 30 lines a minute per account.
  • Say what the player is doing, not who they are with: friends see the line as it is.