Skip to main content

Quick Reference

ClassPurposeKey methods
AvatarSDKSDK initialization and global configurationinitialize(), setSessionToken()
AvatarManagerAvatar asset loading and cachingload(), clearAll()
AvatarView3D rendering viewconstructor, dispose(), onFirstRendering
AvatarControllerRuntime communication and playback controlstart(), send(), pause(), interrupt(), close()
App ID is required on every integration path and identifies your Spatius application (it scopes which avatars you can load). Session Token is only required for Direct Mode (DrivingServiceMode.sdk), where it authenticates the Motion Server WebSocket; RTC / Platform Integration / Backend Mode paths do not need it. See Credentials for the per-path breakdown.
For the minimum end-to-end integration walkthrough (install, build configuration, init, load, connect), see the Direct Mode guide for Web.

AvatarSDK

Main entry point for SDK initialization and global configuration.
import { AvatarSDK } from '@spatius/avatarkit'

Static properties

PropertyTypeDescription
appIdstring | nullCurrent App ID
configurationConfiguration | nullCurrent SDK configuration
sessionTokenstring | nullCurrent Session Token
userIdstring | nullCurrent user ID (telemetry)
versionstringSDK version

Static methods

initialize(appId, configuration)

Initialize the SDK. Must be called before any other operation.
await AvatarSDK.initialize('your-app-id', {
  region: 'us-west',
  drivingServiceMode: DrivingServiceMode.sdk,    // optional, default: sdk
  logLevel: LogLevel.warning,                    // optional, default: off
  audioFormat: {                                  // optional
    channelCount: 1,
    sampleRate: 16000,
  },
})
region is accepted at runtime by @spatius/[email protected], but the published TypeScript types do not yet expose it on Configuration. To pass it without a type error, drop one declaration-merging file into your project:
avatarkit-region.d.ts
import '@spatius/avatarkit'

declare module '@spatius/avatarkit' {
  interface Configuration {
    readonly region?: string
  }
}
A future SDK release will add region natively and this augmentation can be removed. Supported regions are 'us-west' and 'ap-northeast'.

setSessionToken(token)

Set the Session Token used to authenticate the Motion Server WebSocket. Only required in Direct Mode (DrivingServiceMode.sdk); AvatarController.start() opens that WebSocket and authenticates with the token.
AvatarSDK.setSessionToken('your-session-token')
  • Required for DrivingServiceMode.sdk before calling AvatarController.start().
  • Not required for DrivingServiceMode.host (LiveKit Agents Integration, RTC Adapter path, Backend Mode). Those paths receive audio + motion through their own transport and do not open a Motion Server WebSocket from the client; AvatarManager.load() fetches avatar metadata over an App-ID-scoped public endpoint.
  • The token must be obtained from your backend (see Session token API).
  • Maximum 24-hour validity.
  • Token must be paired with the App ID used in initialize().
setSessionToken() can be called before or after initialize(). If called before, the token is applied automatically during initialization.

setUserId(userId)

Set a user identifier for logging and telemetry.
AvatarSDK.setUserId('user-123')

cleanup()

Release all SDK resources. Call when the SDK is no longer needed.
AvatarSDK.cleanup()

AvatarManager

Handles avatar asset loading and caching. Access via the singleton AvatarManager.shared.
import { AvatarManager } from '@spatius/avatarkit'

Static properties

PropertyTypeDescription
sharedAvatarManagerSingleton instance

Instance methods

load(id, onProgress?)

Load an avatar by ID. Downloads and caches the avatar’s assets.
const avatar = await AvatarManager.shared.load('avatar-id', (progress) => {
  switch (progress.type) {
    case 'downloading': {
      // progress.progress is in 0..1; multiply by 100 to render as a percentage.
      const percent = Math.round((progress.progress ?? 0) * 100)
      console.log(`Loading: ${percent}%`)
      break
    }
    case 'completed':
      console.log('Load complete')
      break
    case 'failed':
      console.error('Load failed:', progress.error)
      break
  }
})
ParameterTypeDescription
idstringAvatar ID
onProgress(progress: LoadProgressInfo) => voidOptional progress callback
Returns: Promise<Avatar | null>

clearAll()

Clear all cached avatar resources.
AvatarManager.shared.clearAll()

AvatarView

3D rendering view. Automatically creates a Canvas element and an associated AvatarController.
import { AvatarView } from '@spatius/avatarkit'

Constructor

const avatarView = new AvatarView(avatar, container)
ParameterTypeDescription
avatarAvatarLoaded avatar object
containerHTMLElementContainer element (canvas auto-fills the container)
Container requirement: the container element must have non-zero width and height. The canvas fills the container and auto-resizes via ResizeObserver.

Instance properties

PropertyTypeDescription
controllerAvatarControllerCommunication controller (read-only)
transform{ x, y, scale }Avatar position and scale (see below)
onFirstRendering() => voidCallback fired when the first frame renders
Transform coordinates
FieldRangeDescription
x-1 to 1Horizontal offset (-1 = left, 0 = center, 1 = right)
y-1 to 1Vertical offset (-1 = bottom, 0 = center, 1 = top)
scale> 0Scale factor (1.0 = original size)

Instance methods

dispose()

Release all view resources. Call when the view is no longer needed (see Lifecycle management for details).
avatarView.dispose()

AvatarController

Handles runtime communication with Motion Server and playback control.
// Accessed via AvatarView
const controller = avatarView.controller

Event callbacks

// Connection state changes
controller.onConnectionState = (state: ConnectionState) => {
  // 'disconnected' | 'connecting' | 'connected' | 'failed'
}

// Conversation state changes
controller.onConversationState = (state: ConversationState) => {
  // 'idle' | 'playing' | 'paused'
}

// Error events
controller.onError = (error: AvatarError) => {
  console.error('Error:', error.code, error.message)
}

DrivingServiceMode.sdk methods

Available when drivingServiceMode is DrivingServiceMode.sdk (the Direct Mode path).

initializeAudioContext()

Initialize the audio context. Must be called inside a user-gesture handler (e.g. a click listener).
button.addEventListener('click', async () => {
  await controller.initializeAudioContext()
})

start()

Connect to Motion Server.
await controller.start()

send(audioData, end)

Send avatar speech audio. Returns the conversationId for the current round.
const conversationId = controller.send(
  audioData,  // PCM16, mono, ArrayBuffer
  end,        // true = end of conversation round
)
ParameterTypeDescription
audioDataArrayBufferPCM16 mono audio data
endbooleanWhether this is the final chunk of the current round
send() behavior:
  • end: false — continues the current conversation round.
  • end: true — marks the end of audio input for the current round. The avatar plays the remaining animation, then returns to idle (notified via onConversationState). Sending new audio after this starts a new round and interrupts any ongoing playback.

close()

Close the Motion Server connection.
controller.close()

DrivingServiceMode.host methods

Available when drivingServiceMode is DrivingServiceMode.host (the Backend Mode path).

yieldAudioData(audioData, end)

Provide audio data when your backend owns the Motion Server connection.
const conversationId = controller.yieldAudioData(audioData, false)

yieldFramesData(frames, conversationId)

Provide encoded motion frames when using DrivingServiceMode.host. The conversationId must match the one returned by the corresponding yieldAudioData() call.
controller.yieldFramesData(motionFrames, conversationId)

Common methods

Available in both DrivingServiceMode.sdk and DrivingServiceMode.host.
controller.pause()                     // Pause audio + animation
controller.resume()                    // Resume playback
controller.interrupt()                 // Stop current playback

// Volume control (avatar audio only, not system volume)
controller.setVolume(0.5)              // 0.0 to 1.0
controller.getVolume()                 // returns current volume

Types and Enums

Configuration

interface Configuration {
  region?: string                            // Defaults to 'us-west'; 'ap-northeast' is also supported
  drivingServiceMode?: DrivingServiceMode    // Default: DrivingServiceMode.sdk
  logLevel?: LogLevel                        // Default: LogLevel.off
  audioFormat?: AudioFormat                  // Default: { channelCount: 1, sampleRate: 16000 }
}
See Regions for endpoint details and override options. See the Note in initialize() about the region type-augmentation pattern needed for @spatius/[email protected].

DrivingServiceMode

enum DrivingServiceMode {
  sdk = 'sdk',    // Client SDK handles the Motion Server connection (Direct Mode)
  host = 'host',  // Your backend handles the Motion Server connection (Backend Mode)
}

LogLevel

enum LogLevel {
  off = 'off',          // No logging (default)
  error = 'error',      // Errors only
  warning = 'warning',  // Errors + warnings
  all = 'all',          // All logs
}

ConnectionState

Reported via onConnectionState. Only emitted when drivingServiceMode is DrivingServiceMode.sdk.
enum ConnectionState {
  disconnected = 'disconnected',
  connecting = 'connecting',
  connected = 'connected',
  failed = 'failed',
}

ConversationState

Reported via onConversationState.
enum ConversationState {
  idle = 'idle',        // Breathing animation, waiting for input
  playing = 'playing',  // Active conversation playback
  paused = 'paused',    // Paused during playback
}
State transitions are notified immediately when the transition starts, not when the animation completes. For example, playing is reported as soon as the transition from idle begins.

LoadProgressInfo

type LoadProgressInfo = {
  type: 'downloading' | 'completed' | 'failed'
  progress?: number  // 0..1
  error?: Error
}
Multiply by 100 when rendering as a percentage in your UI.

AudioFormat

The SDK requires audio in mono PCM16 format.
interface AudioFormat {
  readonly channelCount: 1   // Fixed to mono
  readonly sampleRate: number // 8000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000
}
PropertyValue
FormatPCM16 (16-bit signed integer, little-endian)
ChannelsMono (1 channel)
Sample rateConfigurable: 8000 / 16000 / 22050 / 24000 / 32000 / 44100 / 48000 Hz (default: 16000)
Data typeArrayBuffer or Uint8Array
Data size: 1 second at 16 kHz = 16,000 samples × 2 bytes = 32,000 bytes.
async function mp3ToPcm16(mp3File: File, targetSampleRate: number): Promise<ArrayBuffer> {
  const arrayBuffer = await mp3File.arrayBuffer()
  const audioContext = new AudioContext({ sampleRate: targetSampleRate })
  const audioBuffer = await audioContext.decodeAudioData(arrayBuffer.slice(0))

  const length = audioBuffer.length
  const channels = audioBuffer.numberOfChannels
  const pcm16Buffer = new ArrayBuffer(length * 2)
  const pcm16View = new DataView(pcm16Buffer)

  // Mix to mono if stereo
  const mono = channels === 1
    ? audioBuffer.getChannelData(0)
    : (() => {
        const mixed = new Float32Array(length)
        const left = audioBuffer.getChannelData(0)
        const right = audioBuffer.getChannelData(1)
        for (let i = 0; i < length; i++) mixed[i] = (left[i] + right[i]) / 2
        return mixed
      })()

  // Float32 → Int16
  for (let i = 0; i < length; i++) {
    const s = Math.max(-1, Math.min(1, mono[i]))
    pcm16View.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
  }

  audioContext.close()
  return pcm16Buffer
}

Error Handling

AvatarError

import { AvatarError } from '@spatius/avatarkit'

try {
  await avatarView.controller.start()
} catch (error) {
  if (error instanceof AvatarError) {
    console.error('SDK error:', error.message, error.code)
  }
}

Error callback

avatarView.controller.onError = (error: AvatarError) => {
  console.error('Controller error:', error.code, error.message)
}

Error codes

CodeNameDescriptionSolution
1001sdkNotVerifiedSDK not verifiedCheck SDK initialization.
1002avatarIdWrongInvalid avatar IDVerify the Avatar ID.
1003avatarAssetsMissingMissing avatar assetsCheck avatar resources.
1004avatarCameraSettingsWrongInvalid camera settingsCheck camera config.
1005serviceErrorServer errorCheck server status.
1008sendDataWrongInvalid audio dataVerify PCM16 format.
1009requestTimeoutRequest timeoutCheck network.
See Error Codes for the full server-side error reference.

Lifecycle Management

Avatar switching

// 1. Dispose the current view
currentAvatarView.dispose()

// 2. Load a new avatar
const newAvatar = await AvatarManager.shared.load('new-avatar-id')

// 3. Create a new view (reuse the same container)
currentAvatarView = new AvatarView(newAvatar, container)

// 4. Reconnect
await currentAvatarView.controller.initializeAudioContext()
await currentAvatarView.controller.start()

Resource cleanup

dispose() automatically cleans up:
  • WebSocket connections
  • Audio playback data and animation resources
  • Canvas elements and the render system
  • Event listeners and callbacks
Always call dispose() when the view is no longer needed. Failing to do so may cause memory leaks.

Fallback mechanism

If the WebSocket connection fails within 15 seconds, the SDK automatically enters audio-only fallback mode — audio continues playing without animation. This keeps playback uninterrupted when Motion Server is unreachable.
  • Fallback mode is interruptible like normal playback.
  • onConnectionState reports failed when the connection times out.

Browser Compatibility

BrowserMinimum versionRendering
Chrome / Edge90+WebGPU (preferred)
Firefox90+WebGL
Safari14+WebGL
iOS Safari14+WebGL
Android Chrome90+WebGL

Common Issues

IssueCauseSolution
Audio not workinginitializeAudioContext() not in a user gestureCall it inside a click or touchstart handler.
Avatar not renderingContainer has zero dimensionsSet explicit width and height on the container.
WASM MIME type errorBuild tool misconfiguredUse the Vite plugin or Next.js wrapper. See Toolchain Setup.
Session Token invalidToken expired or not setRefresh token from backend; call setSessionToken() before start().
WebSocket connection failedNetwork or auth issueCheck network connectivity and Session Token validity.

Complete Usage Example

import {
  AvatarSDK,
  AvatarManager,
  AvatarView,
  ConnectionState,
  ConversationState,
} from '@spatius/avatarkit'

class AvatarApp {
  private avatarView: AvatarView | null = null

  async init(appId: string, sessionToken: string, avatarId: string, container: HTMLElement) {
    // Initialize the SDK
    await AvatarSDK.initialize(appId, { region: 'us-west' })
    AvatarSDK.setSessionToken(sessionToken)

    // Load the avatar
    const avatar = await AvatarManager.shared.load(avatarId)
    if (!avatar) throw new Error('Failed to load avatar')

    // Create the view
    this.avatarView = new AvatarView(avatar, container)
    this.avatarView.onFirstRendering = () => {
      console.log('First frame rendered')
    }

    // Wire up handlers
    this.avatarView.controller.onConnectionState = (state) => {
      console.log('Connection:', state)
    }
    this.avatarView.controller.onConversationState = (state) => {
      console.log('Conversation:', state)
    }
    this.avatarView.controller.onError = (error) => {
      console.error('Error:', error)
    }
  }

  // Must be called inside a user-gesture handler
  async start() {
    await this.avatarView?.controller.initializeAudioContext()
    await this.avatarView?.controller.start()
  }

  send(audioData: ArrayBuffer, isEnd: boolean) {
    this.avatarView?.controller.send(audioData, isEnd)
  }

  interrupt() {
    this.avatarView?.controller.interrupt()
  }

  dispose() {
    this.avatarView?.controller.close()
    this.avatarView?.dispose()
    this.avatarView = null
  }
}