← Use cases

Multiplayer game rooms and lobbies

Party, quiz, board and card games: rooms with a code, seats, a host, ready flags and chat, built on presence channels and client events with no game server.

Party games, quizzes, board and card games and watch-together rooms all start the same way: a room code, a list of who is in, a ready button and a host who presses start. That part does not need a game server. A room is a presence channel, and the open-source @hibira-io/lobby package turns it into seats, a host, ready flags, kick, start and room chat in the browser.

What your players get

  • A room they join with a code or a link, with seats that fill up and a clear "room full".
  • Ready flags, a host badge and a start button that everyone sees at once.
  • Room chat with "typing…", and a new host when the old one leaves.

Built with

  • Presence channels (presence-) for the people in each room.
  • Client events (client-*) for ready flags, settings, start, chat and game moves, sent from browser to browser.
  • The HTTP API (GET /channels?filter_by_prefix=presence-room-&info=user_count) for a room list without a database.
  • @hibira-io/lobby, which puts the room logic together on top of these.

Step 1: authorize the room on your server

The auth endpoint decides who may join, signs the time each player joined, and refuses the room when it is full. Every player orders the room by that time, so all of them agree on the seats and the host:

import Pusher from 'pusher';
import { authorizeRoom, listRooms, RoomFullError } from '@hibira-io/lobby/server';

const hibira = new Pusher({ appId, key, secret, host: 'ws.hibira.io', useTLS: true });

app.post('/rooms/auth', requireLogin, express.urlencoded({ extended: false }), async (req, res) => {
  try {
    res.json(await authorizeRoom(hibira, {
      socketId: req.body.socket_id,
      channel: req.body.channel_name,
      userId: req.user.id,
      userInfo: { name: req.user.name },
      capacity: 4,
    }));
  } catch (err) {
    res.status(err instanceof RoomFullError ? 403 : 500).end();
  }
});

// Room browser: [{ id: 'KQTZ', channel: 'presence-room-KQTZ', members: 3 }]
app.get('/rooms', async (req, res) => res.json(await listRooms(hibira)));

Step 2: join the room in the browser

Turn on Allow client events in the app settings, then:

import { Room } from '@hibira-io/lobby';

const room = Room.join(pusher, 'presence-room-KQTZ', { capacity: 4 });

room.on('members', ({ players, host }) => renderSeats(players, host));
room.on('message', (m) => chat.append(m.info.name, m.text));
room.on('full', () => show('This room is full'));

readyButton.onclick = () => room.setReady(!room.me.ready);
chatForm.onsubmit = () => room.send(input.value);
input.oninput = () => room.sendTyping();

room.players lists the seats in order with each player's ready and host flags. Your own actions show up at once, and events only the host may send are ignored when anyone else sends them.

Step 3: start the game

The host starts when everyone is ready. What you pass to start() reaches every player, for example the round timings, a random seed or the address of your game server:

if (room.isHost && room.allReady) room.start({ seed: 1234, rounds: 5 });

room.on('start', ({ data }) => game.begin(data));

Moves during the game can be client events on the same channel, room.channel.trigger('client-move', move), or go through your game server if the game needs one.

The same in Laravel

Return the join time from the presence channel callback, and use Echo.join()'s channel:

// routes/channels.php
Broadcast::channel('room.{code}', function ($user, $code) {
    return ['id' => $user->id, 'name' => $user->name, 'joined_at' => (int) floor(microtime(true) * 1000)];
});
const room = new Room(Echo.join(`room.${code}`).subscription, { capacity: 4 });

Try it

The Reaction rooms example is a complete Next.js game: a room list, four seats, ready, chat and a five-round reaction game. It runs on a free app.

Tips

  • A room holds up to 100 members, the presence channel limit.
  • Each client event counts as one message, however many players receive it. A short game for four players sends a few dozen.
  • Clients are trusted once the game starts. When cheating matters, check results on your server or keep the game state on a game server, and use the room for the lobby only.
  • Building in Unity? See Unity lobbies, matchmaking and leaderboards.