Scene setup guide

SceneManager setup

Important

  • Always access the class as AvaCapoSDK.SceneManager (through the SDK).
  • Do not import SceneManager directly 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 pass scene options.


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. Use sdk.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: true will 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 crop
  • contain — letterboxes to fit entirely
  • fill — stretches, ignores aspect ratio
  • none — 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.camera when calling setCameraView(...). 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 (via TalkingAvatar), not a direct import.
  • Don’t install or pin three yourself — the SDK controls three@0.161.0.
  • The 2D background image is a separate overlay pass; it does not replace environment.
  • Prefer configuring via options.scene over supplying a custom sceneManager unless you truly need to reuse an instance.