Atlas
Realtime lip-synced video avatars by North Model Labs.
AtlasRenderer connects to North Model Labs Atlas for realtime lip-synced video avatars. Atlas manages its own LiveKit room — you only need an Atlas API key (no separate LiveKit credentials).
Two modes are supported:
- Passthrough — you provide TTS audio via
speak(blob), Atlas renders lip-synced video. Use your own LLM + TTS pipeline. - Conversation — Atlas handles STT, LLM, TTS, and avatar rendering end-to-end. Use
speakText(text)for text input andautoEnableMicfor voice input.
Installation
npm install livekit-clientUsage — passthrough mode
import { AtlasRenderer } from "avatarlayer/renderers";
const renderer = new AtlasRenderer({
createSession: async () => {
const resp = await fetch("/api/atlas", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
atlasApiKey: "...",
faceUrl: "https://...", // optional face image
mode: "passthrough",
}),
});
const data = await resp.json();
return {
sessionId: data.session_id,
livekitUrl: data.livekit_url,
token: data.token,
};
},
deleteSession: async (sessionId) => {
await fetch(`/api/atlas/${sessionId}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ atlasApiKey: "..." }),
});
},
});Usage — conversation mode
import { AtlasRenderer } from "avatarlayer/renderers";
const renderer = new AtlasRenderer({
createSession: async () => {
const resp = await fetch("/api/atlas", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
atlasApiKey: "...",
faceUrl: "https://...",
mode: "conversation",
}),
});
const data = await resp.json();
return {
sessionId: data.session_id,
livekitUrl: data.livekit_url,
token: data.token,
};
},
deleteSession: async (sessionId) => {
await fetch(`/api/atlas/${sessionId}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ atlasApiKey: "..." }),
});
},
autoEnableMic: true,
onTranscript: (msg) => console.log(`[${msg.role}] ${msg.text}`),
});Constructor options
| Option | Type | Description |
|---|---|---|
createSession | () => Promise<AtlasSessionInfo> | Required. Returns LiveKit connection info from your backend proxy. |
deleteSession | (sessionId: string) => Promise<void> | Recommended. Tears down the session to stop billing. |
autoEnableMic | boolean | Enable microphone after connecting. Default false. Set true for conversation mode. |
onTranscript | (msg: AtlasTranscriptMessage) => void | Called when a transcript segment arrives (conversation mode). |
onVideoStream | (stream: MediaStream | null) => void | Called when video track is received/lost. |
onAudioStream | (stream: MediaStream | null) => void | Called when audio track is received/lost. |
onStateChange | (state: string, detail?: string) => void | Called on connection state changes. |
AtlasSessionInfo
Your createSession callback must return this shape:
interface AtlasSessionInfo {
sessionId: string;
livekitUrl: string;
token: string;
}Your backend proxy calls POST /v1/realtime/session on the Atlas API and maps the response (session_id → sessionId, livekit_url → livekitUrl).
AtlasTranscriptMessage
interface AtlasTranscriptMessage {
id: string;
role: "user" | "agent";
text: string;
final: boolean;
timestamp: number;
}How it works
Passthrough mode
Your TTS audio drives the avatar:
mount()callscreateSessionto get LiveKit credentials- Connects to the LiveKit room
- On the first
speak(audio)call, a persistentAudioContext,MediaStreamDestination, andLocalAudioTrack(named"tts-audio") are created and published to the room — one-time setup - Each subsequent
speak()decodes the audio blob and plays it through the same persistent track, avoiding per-sentence WebRTC renegotiation - Atlas receives the audio and renders lip-synced video in realtime
interrupt()stops playback, unpublishes the track, and closes theAudioContext
If the mic was enabled, it is automatically muted when the persistent track is created and restored when the track is torn down.
Conversation mode
Atlas handles the full pipeline:
mount()callscreateSessionand connects to LiveKit- If
autoEnableMicistrue, the microphone is enabled - Atlas performs STT on the mic audio, runs LLM inference, generates TTS, and streams lip-synced video back
- Transcript segments arrive via
onTranscript - Use
speakText(text)to send text to the agent via the LiveKit data channel
speakText vs sendChat
In conversation mode (autoEnableMic: true), the renderer exposes speakText on the instance. AvatarSession detects this and routes text directly to Atlas via the data channel instead of running external TTS → speak(blob).
In passthrough mode (autoEnableMic: false, the default), speakText is intentionally absent so AvatarSession uses the normal TTS → speak(blob) pipeline to publish audio to the room.
If you need to send data-channel messages in either mode (e.g. from outside AvatarSession), use sendChat:
const renderer = new AtlasRenderer({ ... });
renderer.sendChat("Hello!"); // always availableSession lifecycle
Always delete sessions
Atlas sessions are billed while active ($10/hr conversation, $7/hr passthrough). Always call deleteSession when done, or let unmount() handle it automatically.
When unmount() is called, the renderer:
- Cancels any in-flight TTS audio playback
- Disconnects from the LiveKit room
- Removes video elements from the container and audio elements from the document
- Calls
deleteSessionif a session ID exists