Skip to main content
Prerequisite: This guide assumes you have already completed the Installation and Build Tool Configuration from the SDK Mode guide. If not, complete those steps first.

How Host Mode Differs from SDK Mode

In Host Mode, your application manages the network connection to AvatarKit’s server-side SDK. The client SDK receives encoded messages and decodes them internally for playback — it does not connect to AvatarKit servers directly.
AspectSDK ModeHost Mode
Session TokenRequiredNot required (client-side)
NetworkClient SDK connects directlyYour app relays messages from Server SDK
Message DecodingInternalInternal (same)
Server SDKNot neededRequired on your backend
Key Methodsstart(), send(), close()yieldAudioData(), yieldFramesData()
Host Mode requires AvatarKit’s server-side SDK on your backend. The data passed to yieldAudioData() and yieldFramesData() are encoded messages from the server SDK — not raw audio or animation data.

Quick Start

1

Initialize SDK

import {
  AvatarSDK,
  Environment,
  DrivingServiceMode,
} from '@spatialwalk/avatarkit'

await AvatarSDK.initialize('your-app-id', {
  environment: Environment.intl,
  drivingServiceMode: DrivingServiceMode.host,  // Host mode
})

// No setSessionToken() needed for Host Mode
2

Load Avatar & Create View

import { AvatarManager, AvatarView } from '@spatialwalk/avatarkit'

const avatar = await AvatarManager.shared.load('avatar-id', (progress) => {
  console.log(`Loading: ${progress.progress}%`)
})

// Container MUST have non-zero width and height
const container = document.getElementById('avatar-container')!
const avatarView = new AvatarView(avatar, container)
3

Initialize Audio Context

Critical: initializeAudioContext() must be called inside a user gesture handler (e.g., click, touchstart).
button.addEventListener('click', async () => {
  await avatarView.controller.initializeAudioContext()
})
4

Send Messages from Server SDK

// 1. Send encoded audio message — returns conversationId
const audioMessage: ArrayBuffer = /* encoded message from AvatarKit Server SDK */
const conversationId = avatarView.controller.yieldAudioData(audioMessage, false)

// 2. Send encoded animation messages with the SAME conversationId
const animationMessages: (Uint8Array | ArrayBuffer)[] = /* from Server SDK */
avatarView.controller.yieldFramesData(animationMessages, conversationId)

// 3. Continue sending more audio messages
avatarView.controller.yieldAudioData(moreAudioMessages, false)

// 4. Mark end of conversation
avatarView.controller.yieldAudioData(lastMessage, true)
5

Cleanup

avatarView.controller.clear()
avatarView.dispose()

API Reference

yieldAudioData

Sends an encoded audio message from the server SDK to the client SDK for decoding and playback. Returns a conversationId that links audio and animation messages.
const conversationId = controller.yieldAudioData(
  data: Uint8Array,        // Encoded audio message from Server SDK
  isLast: boolean = false  // true = end of conversation round
)
ParameterTypeDescription
dataUint8ArrayEncoded audio message from AvatarKit Server SDK
isLastbooleanWhether this is the last message for the current round
ReturnsstringconversationId for this session

yieldFramesData

Sends encoded animation messages from the server SDK. Must use the conversationId returned by yieldAudioData().
controller.yieldFramesData(
  keyframesDataArray: (Uint8Array | ArrayBuffer)[],  // Encoded animation messages
  conversationId: string                              // From yieldAudioData()
)
ParameterTypeDescription
keyframesDataArray(Uint8Array | ArrayBuffer)[]Encoded animation messages from Server SDK
conversationIdstringMust match the active conversation

getCurrentConversationId

Retrieves the current active conversation ID.
const id = controller.getCurrentConversationId()  // string | null

Common Methods

These methods work the same as in SDK Mode:
controller.pause()       // Pause audio + animation
controller.resume()      // Resume playback
controller.interrupt()   // Stop current playback
controller.clear()       // Clear all data and resources

// Volume control
controller.setVolume(0.5)
controller.getVolume()

Event Callbacks

// Conversation state changes
controller.onConversationState = (state: ConversationState) => {
  console.log('Conversation:', state)
}

// Error handler
controller.onError = (error: Error) => {
  console.error('Error:', error)
}
onConnectionState is not available in Host Mode — there is no WebSocket connection to monitor.

ConversationId Management

The conversationId links audio and animation messages for synchronized playback:
  1. Call yieldAudioData() — it returns a conversationId
  2. Use that same conversationId when calling yieldFramesData()
  3. Messages with a mismatched conversationId will be silently discarded
  4. Use getCurrentConversationId() to retrieve the current active session ID
Important: Always use the conversationId returned by yieldAudioData() when sending animation messages. Mismatched IDs cause messages to be silently dropped.

Fallback Mechanism

If you provide empty animation data (empty array or undefined), the SDK automatically enters audio-only mode for that session:
  • Audio continues to play normally
  • Once in audio-only mode, any subsequent animation data for that session is ignored
  • The fallback mode is interruptible like normal playback

Complete Example

import {
  AvatarSDK,
  AvatarManager,
  AvatarView,
  Environment,
  DrivingServiceMode,
} from '@spatialwalk/avatarkit'

// Initialize in Host Mode
await AvatarSDK.initialize('your-app-id', {
  environment: Environment.intl,
  drivingServiceMode: DrivingServiceMode.host,
})

// Load avatar and create view
const avatar = await AvatarManager.shared.load('avatar-id')
const container = document.getElementById('avatar-container')!
const avatarView = new AvatarView(avatar, container)

// Set up callbacks
avatarView.controller.onConversationState = (state) => {
  console.log('State:', state)
}
avatarView.controller.onError = (error) => {
  console.error('Error:', error)
}

// User interaction handler
button.addEventListener('click', async () => {
  // Initialize audio (must be in user gesture)
  await avatarView.controller.initializeAudioContext()

  // Receive and forward encoded messages from your server
  const audioMessage = await fetchAudioMessageFromServer()
  const conversationId = avatarView.controller.yieldAudioData(audioMessage, false)

  const animationMessages = await fetchAnimationMessagesFromServer()
  avatarView.controller.yieldFramesData(animationMessages, conversationId)

  // End the conversation round
  avatarView.controller.yieldAudioData(new Uint8Array(), true)
})

// Cleanup when done
function cleanup() {
  avatarView.controller.clear()
  avatarView.dispose()
}