Skip to main content
Rooms are the core of OpenTogetherTube. Each room provides a shared synchronized video experience where multiple users can watch videos together in real-time.

Room Types

OpenTogetherTube supports two types of rooms:

Temporary Rooms

Auto-generated UUID-based rooms that expire after inactivity. Perfect for quick watch sessions.

Permanent Rooms

Named rooms that persist in the database. Can be claimed by registered users for full control.

Creating Rooms

Generate a Temporary Room

Temporary rooms are created with auto-generated UUIDs and don’t require an account:
POST /api/room/generate

{
  "title": "Movie Night",
  "description": "Watching classics together",
  "visibility": "unlisted",
  "queueMode": "manual"
}
Response:
{
  "success": true,
  "room": "550e8400-e29b-41d4-a716-446655440000"
}

Create a Permanent Room

Permanent rooms have custom names and persist in the database:
POST /api/room/create

{
  "name": "my-awesome-room",
  "title": "My Awesome Room",
  "isTemporary": false,
  "visibility": "public",
  "queueMode": "vote"
}
Room names must be 3-32 characters, alphanumeric with hyphens, and globally unique.

Room Properties

Rooms maintain the following state:
PropertyTypeDescription
namestringUnique identifier for the room
titlestringDisplay title shown to users
descriptionstringRoom description
visibilityenumpublic, unlisted, or private
queueModeenumHow videos are queued (manual, vote, loop, dj)
currentSourceQueueItemCurrently playing video
queueVideoQueueUpcoming videos
isPlayingbooleanPlayback state
playbackPositionnumberCurrent position in seconds
playbackSpeednumberPlayback speed multiplier (default: 1.0)
ownerUserRoom owner (can be null)

Room Lifecycle

Loading

Rooms are loaded on-demand when users connect:
  1. Check in-memory cache - If already loaded in RoomManager
  2. Check Redis - Restore from Redis if recently active
  3. Check database - Load from permanent storage
  4. Create new - Initialize a fresh room instance
const result = await roommanager.getRoom(roomName);
if (result.ok) {
  const room = result.value;
}

Unloading

Rooms unload automatically when:
  • No users connected for the configured timeout period
  • Explicitly commanded by admin
  • Server shutdown
Before unloading, rooms save their state:
async onBeforeUnload() {
  // Save full state to Redis
  await this.saveStateToRedisDebounced.flush();
  
  // Save current queue to resume later
  if (!this.isTemporary) {
    const prevQueue = this.queue.items;
    if (this.currentSource) {
      prevQueue.unshift({
        ...this.currentSource,
        startAt: this.realPlaybackPosition
      });
    }
    await storage.updateRoom({
      name: this.name,
      prevQueue: prevQueue.length > 0 ? prevQueue : null
    });
  }
}

Room Settings

Users with appropriate permissions can modify room settings:

Visibility Options

Visible in the public room list. Anyone can join.

Queue Modes

Videos play in the order they were added. Room moderators control playback.
Queue automatically reorders based on user votes. Most voted videos play first.
Videos are re-queued after playing, creating an infinite loop.
Current video restarts when finished. Perfect for music sessions.

Room Ownership

Claiming Ownership

Rooms without owners can be claimed by registered users:
PATCH /api/room/:name

{
  "claim": true
}
Benefits of ownership:
  • Full permission control
  • Promote/demote users to roles
  • Configure advanced settings
  • Permanently delete the room
Room ownership is permanent and cannot be transferred. Choose wisely!

Persistence

Rooms use a three-tier storage strategy:

What’s Stored Where

StorageDataTTL
MemoryFull room state, users, queue, playbackUntil unloaded
RedisSerialized room stateConfigurable (default: 1 hour)
DatabaseSettings, permissions, ownershipPermanent

API Reference

Get Room Info

GET /api/room/:name
Returns complete room state including queue, users, and settings.

Update Room Settings

PATCH /api/room/:name

Content-Type: application/json
{
  "title": "New Title",
  "queueMode": "vote",
  "enableVoteSkip": true
}

Delete Room

DELETE /api/room/:name?permanent=true
Only room owners can permanently delete permanent rooms. Admin API key can unload any room.

Implementation Details

Room Class Structure

The Room class (located at server/room.ts:222) implements the core room logic:
export class Room implements RoomState {
  // Getters/setters automatically mark properties as dirty
  public set title(value: string) {
    this._title = value;
    this.markDirty("title"); // Triggers sync to clients
  }
  
  // Process user requests with permission checking
  async processRequest(request: RoomRequest, context: RoomRequestContext) {
    const permission = permissions.get(request.type);
    if (permission) {
      this.grants.check(context.role, permission);
    }
    // Handle request...
  }
}

State Synchronization

Rooms automatically sync state changes to all connected clients:
private markDirty(prop: keyof RoomStateSyncable): void {
  this._dirty.add(prop);
  this.throttledSync(); // Debounced sync (50ms)
}

public async sync(): Promise<void> {
  const msg: ServerMessageSync = {
    action: "sync",
    ..._.pick(this.syncableState(), Array.from(this._dirty))
  };
  await this.publish(msg); // Broadcast to all clients
}

Best Practices

1

Choose the Right Room Type

Use temporary rooms for one-time events, permanent rooms for recurring sessions.
2

Set Appropriate Visibility

Start with unlisted for private groups, use public for community rooms.
3

Configure Queue Mode

Match queue mode to your use case: vote for democratic control, manual for moderated content.
4

Claim Ownership Early

Claim permanent rooms immediately to prevent others from taking ownership.

Permissions

Configure who can control playback and manage the room

Video Sync

Learn how real-time synchronization works

Vote Mode

Democratic queue management

SponsorBlock

Automatic sponsor segment skipping

Build docs developers (and LLMs) love