helloworld
The baselineThe smallest complete VibeLive app: two people on live video in about 55 lines, including all CSS. The first visitor becomes the host and a room is created automatically; guests join by opening the same link (the room code is carried in the URL). This app establishes the lifecycle every later app builds on.
Load and initialize the SDK
A single ESM import plus init() with your project ID is the only
configuration required. STUN/TURN, ICE negotiation, and signaling are all
handled invisibly — your app never touches a socket.
Sign up, create or join a room, then enter
signup() creates an anonymous identity — no email or password.
The host calls createRoom() to get a shareable room_code;
everyone enters the same room with enterByRoomCode(), which fires
channelSelected once you are safely inside.
Go live inside channelSelected
Always do setup in channelSelected, not after
enterByRoomCode(). Camera and mic are off by default:
you must call setVideo(true) / setAudio(true), then
startLive() to open the WebRTC connection.
Render remote video with registerTile()
A tile is a plain <div> holding a <video>.
registerTile() wires the stream to it — without that call nothing
renders. The SDK auto-removes the tile when the member exits, so there's no
teardown code. One addTile() helper serves both
remoteJoined and remoteStreamStart; an id guard makes
double-calls safe.
Exit and rejoin
exitRoom() leaves the room and releases the camera; the SDK removes
every registered tile for you. To rejoin, just call enterByRoomCode()
again — it re-fires channelSelected, so the same setup handler runs
and you're back live. One button toggles between the two.
helloworld-media
Toggles & screen share
Adds camera/mic on-off controls and screen share. Button labels reflect live SDK
state (Cam: ON / Mic: MUTED / Screen: OFF),
and the layout splits into a screenshare stage plus a camera strip whenever someone
is sharing. The key new idea: read media state from the SDK, don't track it
yourself.
Toggle camera, mic, and screen share
Each toggle reads the current value off VibeLive.mediaState /
VibeLive.screenState and flips it. No local boolean to keep in
sync — the SDK is the single source of truth.
Drive button labels from memberStateChange
memberStateChange fires for any member whose media state changes —
including yourself. Update your button labels here so they always match reality,
even when a toggle takes a moment to apply. Values are 'ON',
'MUTED', or 'OFF'.
Screen share is a second stream type
A member can publish both 'camera' and 'screenshare'
streams. Register each in its own tile. Show or hide the screenshare tile based
on state.screenVideo, and listen for remoteStreamEnd to
re-flow the layout when a share stops. getMemberState(id) lets you
catch a share that started before its state event arrived.
| New API | Purpose |
|---|---|
| setScreenshare(bool) | Start or stop publishing your screen |
| mediaState | Live { video, audio } state for self |
| screenState | Live { video } screenshare state for self |
| memberStateChange | Fires when any member's media state changes |
| remoteStreamEnd | A remote camera or screenshare stream stopped |
| getMemberState(id) | Read a member's current media state on demand |
helloworld-members
Roster & presence
Adds a member-list sidebar showing each person's live status, and replaces the
automatic go-live with explicit Go Live / Stop Live buttons —
so members sit in PRE-LIVE until they choose to broadcast. The roster
is seeded once, then kept current by presence events.
Enter PRE-LIVE, go live on demand
Skip startLive() in channelSelected — turn the camera on
but stay in PRE-LIVE. The user clicks Go Live (calls
startLive()) or Stop Live (calls stopLive()).
Track readiness with VibeLive.isLive plus the
localJoined / localLeft events.
Seed the roster with getMembers()
Once inside the room, getMembers() returns everyone currently present.
Each member carries a displayName and a displayStatus —
a friendly label: LIVE, PRE-LIVE, or EXITED.
Keep the roster current with presence events
memberStateChange updates a member's status (and signals
new arrivals); memberUpdate signals a renamed member — re-read it with
getMember(id). Re-render on each event and the sidebar always matches
the room.
| New API | Purpose |
|---|---|
| getMembers() | Snapshot of everyone currently in the room |
| getMember(id) | Look up one member's current record |
| displayStatus | Friendly status: LIVE / PRE-LIVE / EXITED |
| startLive() / stopLive() | Enter or leave LIVE without leaving the room |
| isLive | Whether you are currently broadcasting |
| memberUpdate | A member's profile (e.g. name) changed |
| localJoined / localLeft | Your own connection went live / dropped |
helloworld-chat
Messaging & DMs
Adds a text-chat panel with both room-wide messages and private direct messages.
A [DM] button on each remote tile opens a one-to-one thread. The same
sendMessage() call serves both — the difference is one optional argument.
Load history, then listen for new messages
Inside channelSelected, fetch recent messages with
getMessages(channelId, { count }) (newest-first — reverse for display),
then register setOnMessage() for everything that arrives afterward.
The channel id is VibeLive.channel.id.
Room message vs. direct message
sendMessage(channelId, text) broadcasts to the room. Add a
memberId third argument and the same call becomes a private DM to that
member. The app stores a dmTarget; clearing it returns to room-wide.
Route incoming messages by target_type
Each incoming message reports a target_type: 'channel' for
room messages, 'member' for DMs. Combine that with
sender_member_id to label each line (To Room / PRIVATE to … / From …).
De-dupe with the message pid and always escape text before rendering.
| New API | Purpose |
|---|---|
| getMessages(channelId, opts) | Fetch recent history (newest-first) |
| setOnMessage(cb) | Register a handler for incoming messages |
| sendMessage(ch, text) | Broadcast a room-wide message |
| sendMessage(ch, text, id) | Send a private DM to one member |
| channel.id | The active channel id, used by all message calls |
| target_type | Incoming routing: 'channel' or 'member' |
helloworld-sessions
Past sessions & AI summaries
Adds a left-hand panel that lists every past session in the room — each one is a
single member's stretch of time inside, with an AI-generated summary
and tags. Click a session to see its full detail, then load the room
transcript on request. A session and its summary are produced automatically a couple
of minutes after a member leaves, so the list fills in as people come and go.
List past sessions with summaries and tags
Inside channelSelected, call getSessions(channelId).
Each returned object carries title, summary,
tags, start_time, duration_seconds and the
other_users who were present. Visibility is requestor-aware on the
server — hosts see every session, members see only the ones they were in.
Refresh when a new summary is ready
A session's summary is generated asynchronously, shortly after a member exits.
Subscribe to sessionSummaryReady and re-fetch the list when it fires —
no polling. Until the AI finishes, a fresh session shows up with its summary still
pending.
Load the transcript on request
The summary is the headline; the full transcript is fetched only when asked, with
getTranscript(channelId). It returns { status, text, segments }.
Note the transcript is retrieved per room (it covers the whole channel), while
summaries and tags are per session.
| New API | Purpose |
|---|---|
| getSessions(channelId) | List past sessions with summary + tags (requestor-aware) |
| getSummaries(channelId) | Alias for getSessions |
| getTranscript(channelId) | Fetch the room transcript on request |
| sessionSummaryReady | Event — a new session summary is ready to load |
| session.summary / .tags | AI-generated headline and topic labels per session |
| session.other_users | The other members present during that session |
Where to go next
- The full feature catalog — every capability of the SDK, organized by tier, lives in the Feature Overview
- Run the apps — each section links to a live, runnable version; open two browser tabs to see both sides of a room
- Read the source — every app is one self-contained file; the "View source" link shows it exactly as deployed
- Use your own project ID — append
?pid=YOUR_IDto any app link to run it against your own project