Direct Mode for Web is the simplest way to put a Spatius avatar in a browser app. @spatius/avatarkit connects directly to Motion Server, sends avatar speech audio, and renders motion locally — no agent worker or relay backend needed beyond a Session Token endpoint.
Required: The SDK uses WASM files that need special build configuration. Configure your build tool before initializing the SDK, or .wasm requests will fail.
Vite
Next.js
Add the official plugin to vite.config.ts:
vite.config.ts
import { defineConfig } from 'vite'import { avatarkitVitePlugin } from '@spatius/avatarkit/vite'export default defineConfig({ plugins: [ avatarkitVitePlugin(), ],})
What the plugin handles
Dev server: serves .wasm files with the correct MIME type
Build: copies WASM files to dist/assets/
Cloudflare Pages: generates a _headers file
Vite options: configures optimizeDeps, assetsInclude, assetsInlineLimit
TypeScript: declare `region` (required for @spatius/[email protected])
The runtime accepts region, but the published types in @spatius/[email protected] do not yet expose it. Add a single declaration-merging file to your project so TypeScript stops complaining:
A future SDK release will add region natively and this file can be deleted.
2
Load the avatar
import { AvatarManager } from '@spatius/avatarkit'const avatar = await AvatarManager.shared.load('your-avatar-id', (progress) => { // 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}%`)})
3
Mount the view
import { AvatarView } from '@spatius/avatarkit'// Container must have non-zero width and height.const container = document.getElementById('avatar-container')!const avatarView = new AvatarView(avatar, container)
4
Initialize audio (inside a user gesture)
initializeAudioContext() must be called inside a user-gesture handler (a click, touchstart, or similar listener). Browsers reject AudioContext creation outside user gestures and the call fails silently.
import { ConnectionState } from '@spatius/avatarkit'await avatarView.controller.start()await new Promise<void>((resolve, reject) => { avatarView.controller.onConnectionState = (state) => { if (state === ConnectionState.connected) resolve() else if (state === ConnectionState.failed) reject(new Error('Connection failed')) }})// Stream PCM16 mono audio at the configured sample rate (default 16 kHz)avatarView.controller.send(audioChunk, /* end */ false)// Mark end of the conversation roundavatarView.controller.send(lastChunk, /* end */ true)
6
Cleanup
avatarView.controller.close()avatarView.dispose()
For full API signatures, types, audio format details, lifecycle management, error codes, browser compatibility, and common issues, see Web SDK Reference.