<video> and an <audio> element.
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: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 toPOST /compose/token with your key in the x-simli-api-key header.
app/api/simli-token/route.ts
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.
3. Connect
In the browser, get a token from your route, create aSimliClient and call start():
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.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 withsendAudioData, 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 withlistenToMediastreamTrackorlistenToAudioElement. 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
errorevent fires with Simli’s message, andstart()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 withError("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 asUnknown error from Simli: <message>. Once the session has started, an unrecognised message doesn’t end it: it fires theunknownevent and is logged as a warning.
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
"p2p" to SimliClient:
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
- Check the reason
start()rejected with, or theerrorevent’s message. - Check that your API key is correct and active, and that your face ID is valid.
- Check that your audio is PCM16, 16 kHz, mono.
- Check that the page has permission to use the microphone, if you stream it.