SceneManager setup
Important
- Always access the class as
AvaCapoSDK.SceneManager(through the SDK).- Do not import
SceneManagerdirectly from your app code.- Do not install your own
three— the SDK bundles and manages three@0.161.0.
How SceneManager is used in the SDK
You typically won’t construct SceneManager yourself. Instead, pass SceneManager options via the scene field when creating a TalkingAvatar. Internally, TalkingAvatar merges defaults and builds the manager:
// inside TalkingAvatar (for reference)
const effectiveSceneOpts = utils.deepMerge(structuredClone(SCENE_DEFAULTS), this.opts.scene ?? {});
this.sceneManager = this.opts.sceneManager
? this.opts.sceneManager
: new AvaCapoSDK.SceneManager(this.node, effectiveSceneOpts);
You can provide a custom instance via
sceneManager, but the recommended path is to passsceneoptions.
Quick start
<div id="avatar"></div>
<script type="module">
// AvaCapoSDK should be available (global or via your bundling of the SDK)
const sdk = new AvaCapoSDK({
apiKey: 'YOUR_API_KEY',
container: '#avatar',
preload: ['talking']
});
await sdk.init();
const head = await sdk.talkingAvatar(null, {
// Configure SceneManager **via `scene`**
cameraView: 'head', // 'full' | 'mid' | 'torso' | 'head'
scene: {
fps: 30,
environment: 'room', // supported here: 'room' | 'none'
useEnvAsBackground: false, // keep IBL for lighting only
transparentBackground: false,
camera: {
cameraRotateEnable: false,
cameraPanEnable: false,
cameraZoomEnable: false
},
lighting: {
ambient: { color: 0xffffff, intensity: 1.2 },
directional:{ color: 0xffffff, intensity: 2.0, phi: 1.2, theta: 1.5 },
spot: { intensity: 0 } // off in this example
}
},
// ...your TalkingAvatar options (voice, model url, etc.)
});
</script>
Wrong approach to avoid Initializing
new SceneManager(container, { ... })directly in your app. Usesdk.talkingAvatar(..., { scene: { ... } })instead.
Environments
Only the following are in scope for this guide:
// Physically-based IBL lighting
head.sceneManager.setEnvironment('room', { useAsBackground: false });
// No IBL/background
head.sceneManager.setEnvironment('none');
environment: 'room'gives consistent PBR-friendly lighting.useEnvAsBackground: truewill also display the IBL as the 3D background (independent from the 2D image overlay).
2D background image (overlay)
Render a 2D image behind the 3D scene using an orthographic pass. This is separate from the environment:
await head.sceneManager.setBackgroundImage({
source: 'https://example.com/bg.jpg',
fit: 'cover', // 'cover' | 'contain' | 'fill' | 'none'
alignX: 'center', // 'left' | 'center' | 'right'
alignY: 'center', // 'top' | 'center' | 'bottom'
repeat: false
});
// Clear:
head.sceneManager.clearBackgroundImage();
// Check:
if (head.sceneManager.hasBackgroundImage()) {
// ...
}
Fit modes quick reference
cover(default) — fills canvas, may cropcontain— letterboxes to fit entirelyfill— stretches, ignores aspect rationone— draws at native pixel size
Camera & lighting tweaks
Camera: use built-in view presets (recommended)
The TalkingAvatar class provides convenient camera presets that automatically frame the avatar based on its height and field of view.
// Instantly switch to a preset
head.setCameraView('full'); // or 'mid' | 'torso' | 'head'
// With smooth tween
head.setCameraView('torso', { duration: 300 });
Supported presets: 'full' | 'mid' | 'torso' | 'head'. If the avatar rig is not yet ready, the selection will be stored and applied automatically later.
Important: Do not pass
opts.camerawhen callingsetCameraView(...). Camera overrides (distance,offset*,rot*) will replace the preset math and misframe the avatar.
Advanced: manual positioning
If you need complete manual control, bypass the presets and call the SceneManager directly:
head.sceneManager.setCameraPose({
target: { x: 0, y: 1.6, z: 0 },
distance: 8,
rotY: 0.3,
duration: 400 // ms
});
Light settings update
Lighting at runtime (partial updates welcome):
head.sceneManager.setLighting({
ambient: { intensity: 1.5 },
directional:{ intensity: 20, phi: 1.0, theta: 2.0 },
spot: { intensity: 0.8, angle: 0.9 }
});
Minimal TypeScript hints
type SceneEnv = 'room' | 'none';
interface CameraOptions {
distance?: number;
offsetX?: number;
offsetY?: number;
rotX?: number; rotY?: number; rotZ?: number;
cameraRotateEnable?: boolean;
cameraPanEnable?: boolean;
cameraZoomEnable?: boolean;
}
interface LightingOptions {
ambient?: { color?: number | string; intensity?: number };
directional?: { color?: number | string; intensity?: number; phi?: number; theta?: number };
spot?: { color?: number | string; intensity?: number; angle?: number; phi?: number; theta?: number };
}
interface SceneManagerOptions {
fps?: number;
modelPixelRatio?: number;
environment?: SceneEnv;
useEnvAsBackground?: boolean;
transparentBackground?: boolean;
camera?: CameraOptions;
lighting?: LightingOptions;
backgroundImage?: {
source?: string | File | Blob | HTMLImageElement | ImageBitmap | HTMLCanvasElement | OffscreenCanvas;
fit?: 'cover' | 'contain' | 'fill' | 'none';
alignX?: 'left' | 'center' | 'right';
alignY?: 'top' | 'center' | 'bottom';
repeat?: boolean;
};
}
Gotchas
- Use
AvaCapoSDK.SceneManager(viaTalkingAvatar), not a direct import. - Don’t install or pin
threeyourself — the SDK controlsthree@0.161.0. - The 2D background image is a separate overlay pass; it does not replace
environment. - Prefer configuring via
options.sceneover supplying a customsceneManagerunless you truly need to reuse an instance.