Skip to main content
Prerequisite: This guide assumes you have already completed the Installation 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()yield(audio), yield(animations, reqId)
Host Mode requires AvatarKit’s server-side SDK on your backend. The data passed to yield() are encoded messages from the server SDK — not raw audio or animation data.

Quick Start

1

Initialize SDK

import ai.spatialwalk.avatarkit.*

AvatarSDK.initialize(
    context = applicationContext,
    appId = "your-app-id",
    configuration = Configuration(
        environment = Environment.intl,
        audioFormat = AudioFormat(sampleRate = 16000),
        drivingServiceMode = DrivingServiceMode.HOST,  // Host mode
        logLevel = LogLevel.OFF
    )
)

// No sessionToken needed for Host Mode
2

Load Avatar & Create View

val avatar = withContext(Dispatchers.IO) {
    AvatarManager.load(id = "avatar-id") { progress ->
        when (progress) {
            is AvatarManager.LoadProgress.Downloading ->
                println("Loading: ${(progress.progress * 100).toInt()}%")
            is AvatarManager.LoadProgress.Completed ->
                println("Avatar loaded!")
            is AvatarManager.LoadProgress.Failed ->
                println("Error: ${progress.error.message}")
        }
    }
}

val avatarView = AvatarView(context)
avatarView.init(avatar, lifecycleScope)
3

Set Up Callbacks

val controller = avatarView.controller

controller?.onConversationState = { state ->
    println("Conversation: $state")
}

controller?.onError = { error ->
    println("Error: ${error.name}")
}
4

Send Messages from Server SDK

lifecycleScope.launch {
    // 1. Send encoded audio message — returns conversationID
    val audioMessage: ByteArray = ... // Encoded message from AvatarKit Server SDK
    val conversationID = controller?.yield(audioMessage, end = false) ?: ""

    // 2. Send encoded animation messages with the SAME conversationID
    val animationMessages: List<ByteArray> = ... // Encoded messages from Server SDK
    controller?.yield(animationMessages, conversationID)

    // 3. Continue sending more audio messages
    controller?.yield(moreMessages, end = false)

    // 4. Mark end of conversation
    controller?.yield(byteArrayOf(), end = true)
}
5

Cleanup

controller?.interrupt()
controller?.cleanup()

API Reference

yield (Audio)

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.
suspend fun yield(
    audioData: ByteArray,
    end: Boolean = false,
    audioFormat: AudioFormat? = null
): String  // Returns conversationID
ParameterTypeDescription
audioDataByteArrayEncoded audio message from AvatarKit Server SDK
endBooleanWhether this is the last message for the current round
audioFormatAudioFormat?Optional override for audio format
ReturnsStringconversationID for this session

yield (Animation)

Sends encoded animation messages from the server SDK. Must use the conversationID (as reqId) returned by yield(audio).
fun yield(animations: List<ByteArray>, reqId: String)
ParameterTypeDescription
animationsList<ByteArray>Encoded animation messages from Server SDK
reqIdStringMust match the active conversationID

Common Methods

These methods work the same as in SDK Mode:
controller.pause()       // Pause audio + animation
controller.resume()      // Resume playback
controller.interrupt()   // Stop and terminate conversation
controller.cleanup()     // Release all resources

Event Callbacks

controller.onConversationState: ((ConversationState) -> Unit)?
controller.onError: ((AvatarError) -> Unit)?
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 yield(audioData) — it returns a conversationID
  2. Use that same conversationID as reqId when calling yield(animations, reqId)
  3. Messages with a mismatched reqId will be silently discarded
Important: Always use the conversationID returned by yield(audio) when sending animation messages. Mismatched IDs cause messages to be silently dropped.

Fallback Mechanism

If you provide empty animation data (empty list), 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 ai.spatialwalk.avatarkit.*

// Initialize in Host Mode
AvatarSDK.initialize(
    context = applicationContext,
    appId = "your-app-id",
    configuration = Configuration(
        environment = Environment.intl,
        audioFormat = AudioFormat(sampleRate = 16000),
        drivingServiceMode = DrivingServiceMode.HOST,
        logLevel = LogLevel.OFF
    )
)

// Load avatar
val avatar = withContext(Dispatchers.IO) {
    AvatarManager.load(id = "avatar-id")
}

val avatarView = AvatarView(context)
avatarView.init(avatar, lifecycleScope)

// Set up callbacks
avatarView.controller?.onConversationState = { state ->
    println("State: $state")
}
avatarView.controller?.onError = { error ->
    println("Error: ${error.name}")
}

// Send encoded messages from your server
lifecycleScope.launch {
    val audioMessage = fetchAudioMessageFromServer()
    val conversationID = avatarView.controller?.yield(audioMessage, end = false) ?: ""

    val animationMessages = fetchAnimationMessagesFromServer()
    avatarView.controller?.yield(animationMessages, conversationID)

    // End the conversation round
    avatarView.controller?.yield(byteArrayOf(), end = true)
}