Skip to main content
simli-client connects a web page to Simli. You send it the audio the avatar should speak, and it plays the avatar’s lip-synced video and voice in a <video> and an <audio> element.
Building a voice agent? LiveKit Agents and Pipecat run the agent and Simli on the server. Use this SDK when the avatar’s audio is already in the browser.

What you need

  • A Simli API key, from your Simli dashboard
  • A backend route that creates session tokens. Your API key stays there and never goes in the browser.

Install

1. Add video and audio elements

With React, create the elements and keep refs to them:
Without React, look the elements up in the DOM:

2. Create a session token on your server

Each session starts with a session token. Creating one takes your API key, so do it on your server and send only the token to the browser. This route is for Next.js (App Router). On other backends, POST the same config to POST /compose/token with your key in the x-simli-api-key header.
app/api/simli-token/route.ts
To use another avatar, replace the faceId with one from your dashboard or the default faces. For the video size, the avatar’s position, the starting camera and backgrounds, see Trinity-2.
Anyone who can reach your token route can start a session on your account. Put it behind your own sign-in or rate limiting before you go to production.

3. Connect

In the browser, get a token from your route, create a SimliClient and call start():
Create the client just before you call start(), because the constructor already opens the connection, and register your event handlers before start(). A SimliClient runs one session. To start another, get a new token and create a new client.

4. Send audio

Send the avatar’s speech as raw PCM16 audio: 16 kHz, mono. Chunks of about 6000 bytes work well.
To stop the avatar mid-sentence, for example when the user interrupts, drop the audio it hasn’t spoken yet:
To stream from a live source instead, pass it to listenToMediastreamTrack(track) (a MediaStreamTrack, such as a microphone or a WebRTC track from your voice bot) or listenToAudioElement(audioElement) (an <audio> element). The SDK converts the audio and sends it for you.

Choosing handleSilence

handleSilence in the session token config (default true) decides what Simli does when your audio runs out:
  • true: Simli fills the gaps with silence, so the avatar keeps moving naturally between sentences and picks up again when you send more. Keep it on when you send audio in bursts with sendAudioData, such as one TTS response at a time.
  • false: Simli plays only the audio you send, and the video freezes when you stop sending. Turn it off when you stream continuous audio with listenToMediastreamTrack or listenToAudioElement. Those already send their own silence, and Simli’s gap filling would cut into it and cause audio artifacts.

5. Stop

Errors and retries

start() resolves once the avatar’s first video frame is on screen. If it fails, what happens depends on why:
  • Simli refuses the session, for example because the session token has expired, the face ID is unknown, your account is out of minutes, or you hit a rate limit. The error event fires with Simli’s message, and start() rejects at once with the same message, a string such as "ERROR: INVALID_FACE_ID". It doesn’t retry, since a retry would fail the same way.
  • The network fails: the connection can’t be opened or drops, or an attempt takes more than 15 seconds. These are the only failures start() retries, up to 10 times, with exponential backoff: it waits 1, 2, 4 and 8 seconds before the first four retries, then 16 seconds before each one after that, each wait plus a little random jitter. Then it rejects with Error("Too Many Retry Attempts Failed to connect"). Your event handlers stay registered across retries.
  • Anything else goes wrong before the first frame, such as a message from Simli the SDK doesn’t recognise. start() rejects at once with that message as is, and the SDK logs it as Unknown error from Simli: <message>. Once the session has started, an unrecognised message doesn’t end it: it fires the unknown event and is logged as a warning.
On your server, generateSimliSessionToken throws the API’s response body if it answers with an error, for example for an invalid face ID, no minutes left or an unsupported video size (422).

Peer-to-peer mode (optional)

The default transport needs nothing from you. If you’d rather connect the browser straight to Simli over WebRTC, there’s a lower-level peer-to-peer mode. It can save a little latency, but strict firewalls can block it, and it needs ICE servers. Fetching them takes your API key, so add a second route on your server:
app/api/simli-ice/route.ts
Then pass the ICE servers and "p2p" to SimliClient:
If three peer-to-peer attempts in a row fail, start() makes its remaining retries with the default transport.

Next steps

Trinity-2

Video size, the avatar’s position, the camera, backgrounds and foregrounds.

API reference

Every function, method, event and type.

Troubleshooting

  1. Check the reason start() rejected with, or the error event’s message.
  2. Check that your API key is correct and active, and that your face ID is valid.
  3. Check that your audio is PCM16, 16 kHz, mono.
  4. Check that the page has permission to use the microphone, if you stream it.
Reach out to our team on Discord, or fork simli-client for your own use case.