---
sidebar_position: 7
---

# Group calls

A group call is a room on WordPlus Cloud's media servers. Everyone in it sends their camera, microphone and screen once, and gets everyone else's. The servers form a mesh across regions, so a room's people may sit on different servers. A busy server sends a newcomer to another, and a full mesh holds their place while it adds a server.

The [call screen](call-screen.md)'s `<wordplus-room>` joins one with no code. This page is for building your own interface.

## Who may join

The same callback as for [calls](calls.md), with `$with` set to `null`:

```php
wordplus_realtime()->calls( 'class-', function ( $user_id, $room, $with ) {
	if ( null !== $with ) {
		return false;                         // this plugin has no calls between two people
	}

	$class = (int) substr( $room, strlen( 'class-' ) );

	if ( my_school_teacher_of( $class ) === $user_id ) {
		return array( 'admin' => true );       // the teacher runs the room
	}

	return my_school_student_of( $class, $user_id ) ? array( 'publish' => false ) : false;   // students watch
} );
```

The array may say what the user does in the room:

| Key | Default | |
|---|---|---|
| `info` | name and picture | what the others see of them |
| `publish` | `true` | sends their camera, microphone and screen |
| `subscribe` | `true` | gets the others' |
| `admin` | `false` | may manage the room through the media server's own API |
| `hidden` | `false` | isn't listed to the others, as for a silent observer |

## Joining in the browser

```js
const { calls } = wordplusRealtimeCalls.page();

const room = await calls.room( 'class-7', { type: 'video' } );   // rejects with `forbidden` when your callback says no

room.on( 'state', ( state ) => showState( state ) );            // connecting, connected, reconnecting, ended
room.on( 'person', ( person ) => drawPerson( person ) );         // someone arrived, or what they send changed
room.on( 'person_left', ( person ) => removePerson( person.identity ) );
room.on( 'speakers', ( people ) => markSpeaking( people ) );
room.on( 'ended', ( reason ) => showEnded( reason ) );

const media = await navigator.mediaDevices.getUserMedia( { audio: true, video: true } );
await room.setTrack( 'microphone', media.getAudioTracks()[ 0 ] );
await room.setTrack( 'camera', media.getVideoTracks()[ 0 ] );

// Later:
room.leave();
```

- **`calls.room()` resolves at once** with the room connecting. Tracks set before it connects are sent once it does.
- **`room.people()`** lists everyone else in it now.
- **A person** is `{ identity, user, info, camera, microphone, screen, screenAudio, speaking }`. `user` is their user id on the site, `info` what your callback gave for them, and each track has `attach( element )` and `detach()`.
- **`room.livekit`** is the media server client's own room, for what the kit doesn't wrap: a group call's rooms are LiveKit rooms on WordPlus Cloud's own servers.

## Limits

32 people in a video room and 50 in an audio room, which the plan sets. The room's type is the one the first person joined it with.

## Where it runs

A room is `sdk_<site key>_<room name>` on the mesh, apart from the site's Better Messages rooms and live streams. The page loads the media client only when it first joins a room (`realtime-rooms.min.js`, about 580 KB), so a page that never joins one never loads it.
