Skip to main content

Shared Core Compact Call Tutorial

Build the headless compact-call core with mediasfu-shared@1.1.0. Your app owns the renderer and its socket/transport lifecycle; this controller owns the published participant, media, screen-share, and personal-leave operations.

Prerequisites

  • Node.js 18 to 20 and a TypeScript application that owns its room socket and transports.
  • A renderer that can display participants, audio/video tracks, alerts, and navigation.
  • An authenticated backend for hosted room authority; no MediaSFU credential goes in the client.

Project files

package.json
{"private":true,"type":"module","scripts":{"typecheck":"tsc --noEmit"},"dependencies":{"mediasfu-shared":"1.1.0","mediasoup-client":"3.20.0","socket.io-client":"4.8.3"},"devDependencies":{"typescript":"5.9.3"}}
tsconfig.json
{"compilerOptions":{"strict":true,"target":"ES2022","module":"ESNext","moduleResolution":"bundler","lib":["DOM","ES2022"],"skipLibCheck":true,"noEmit":true},"include":["src/**/*.ts"]}
src/room-core.ts
import { allMembers, clickAudio, clickScreenShare, clickVideo, confirmExit, processConsumerTransports } from 'mediasfu-shared';
export type RoomRenderer={renderParticipants():void;renderMedia():void;reportFailure(message:string):void;clearRoom():void};
export type SharedRoomRuntime={participants:Parameters<typeof allMembers>[0];microphone:Parameters<typeof clickAudio>[0];camera:Parameters<typeof clickVideo>[0];remoteMedia:Parameters<typeof processConsumerTransports>[0];screenShare:Parameters<typeof clickScreenShare>[0];leave:Parameters<typeof confirmExit>[0]};
export class SharedRoomController {
constructor(private readonly runtime:SharedRoomRuntime,private readonly renderer:RoomRenderer){}
async refreshParticipants(){await allMembers(this.runtime.participants);this.renderer.renderParticipants()}
async toggleMicrophone(){await clickAudio(this.runtime.microphone)}
async toggleCamera(){await clickVideo(this.runtime.camera)}
async receiveMedia(){await processConsumerTransports(this.runtime.remoteMedia);this.renderer.renderMedia()}
async toggleScreenShare(){await clickScreenShare(this.runtime.screenShare)}
leaveRoom(){confirmExit(this.runtime.leave);this.renderer.clearRoom()}
report(error:unknown){this.renderer.reportFailure(error instanceof Error?error.message:'The room action failed.')}
}
src/app-boundaries.ts
import type { RoomRenderer, SharedRoomRuntime } from './room-core';
export type AuthorizedRoom={roomName:string;member:string};
export type RoomAuthority={join(input:{meetingId:string;displayName:string}):Promise<AuthorizedRoom>};
export type RoomApplication={authority:RoomAuthority;runtime:SharedRoomRuntime;renderer:RoomRenderer};

RoomApplication is the explicit boundary: after your authenticated backend authorizes entry, your existing socket/transport lifecycle fills SharedRoomRuntime; your renderer mounts the tracks and controls.

Run

npm install
npm run typecheck

Expected result

Your application can construct SharedRoomController from its live room state. Refreshing participants updates your renderer; microphone, camera, remote media, screen share, and Leave call the matching shared-core operation.

Recovery

Keep device denial, a missing device, transport failure, paused remote media, autoplay blocking, and screen-selection cancellation distinct in the renderer. Rebuild live runtime state after reconnecting before retrying an operation.

Cleanup

Call leaveRoom, stop application-owned tracks and transports, unmount the renderer, and clear application state. This is personal leave, not a host-wide end operation.

Release boundary

The authenticated backend creates or authorizes hosted rooms and returns only the room information your application needs. mediasfu-shared is a core library, not a rendered-room or secure client-side room-creation SDK; do not call its hosted create/join helper with a browser API key.