or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

accessibility.mdanimation.mdannotation.mdar.mdcontrols.mdenvironment.mdindex.mdloading.mdscene-graph.md
tile.json

controls.mddocs/

Camera Controls

Interactive camera controls enabling users to orbit, zoom, and pan around 3D models with customizable bounds and behaviors.

Capabilities

Enable Camera Controls

The primary control for enabling interactive camera manipulation.

/**
 * Enable interactive camera controls (orbit, zoom, pan)
 * Users can click/drag to orbit, scroll to zoom, and right-click drag to pan
 */
cameraControls: boolean;

Usage:

<model-viewer src="models/car.glb" camera-controls></model-viewer>

Camera Position

Control the camera's position using spherical coordinates (theta, phi, radius).

/**
 * Camera position in spherical coordinates
 * Format: "theta phi radius" where:
 * - theta: horizontal angle (degrees or radians)
 * - phi: vertical angle from positive Y axis (degrees or radians)  
 * - radius: distance from target (absolute or percentage)
 */
cameraOrbit: string;

/**
 * Get current camera position as spherical coordinates
 */
getCameraOrbit(): SphericalPosition;

interface SphericalPosition {
  theta: number;  // horizontal angle in radians
  phi: number;    // vertical angle in radians  
  radius: number; // distance from target
  toString(): string;
}

Usage:

<!-- Start with front view, slightly elevated, at 105% of auto distance -->
<model-viewer 
  src="models/robot.glb" 
  camera-orbit="0deg 75deg 105%"
  camera-controls>
</model-viewer>
// Get current camera position
const position = modelViewer.getCameraOrbit();
console.log(`Camera at θ:${position.theta}, φ:${position.phi}, r:${position.radius}`);

// Set new position
modelViewer.cameraOrbit = "45deg 60deg 150%";

Camera Target

Set what point the camera looks at in 3D space.

/**
 * Camera target point in world coordinates
 * Format: "x y z" in model units, or "auto" for automatic
 */
cameraTarget: string;

/**
 * Get current camera target position
 */
getCameraTarget(): Vector3D;

interface Vector3D {
  x: number;
  y: number;
  z: number;
  toString(): string;
}

Usage:

<!-- Look at specific point 2 units up from model center -->
<model-viewer 
  src="models/building.glb"
  camera-target="0m 2m 0m"
  camera-controls>
</model-viewer>
// Get current target
const target = modelViewer.getCameraTarget();
console.log(`Looking at: ${target.x}, ${target.y}, ${target.z}`);

// Focus on a specific part of the model
modelViewer.cameraTarget = "1m 0.5m 0m";

Field of View

Control camera zoom and perspective.

/**
 * Camera field of view angle
 * Smaller values = more zoomed in, larger values = wider view
 * Format: degrees (e.g., "30deg") or "auto" for automatic
 */
fieldOfView: string;

/**
 * Get current field of view in degrees
 */
getFieldOfView(): number;

/**
 * Get minimum field of view in degrees
 */
getMinimumFieldOfView(): number;

/**
 * Get maximum field of view in degrees
 */
getMaximumFieldOfView(): number;

/**
 * Get ideal aspect ratio for the model
 */
getIdealAspect(): number;

/**
 * Update camera framing for the model
 */
updateFraming(): Promise<void>;

/**
 * Zoom by a specified number of key presses (positive = zoom in, negative = zoom out)
 */
zoom(keyPresses: number): void;

/**
 * Programmatically interact with the model using finger-like input
 */
interact(duration: number, finger0: Finger, finger1?: Finger): void;

Usage:

<!-- Zoomed in view for detail inspection -->
<model-viewer 
  src="models/watch.glb" 
  field-of-view="20deg"
  camera-controls>
</model-viewer>

Camera Bounds

Limit camera movement to specific ranges.

/**
 * Minimum camera orbit bounds
 * Format: "minTheta minPhi minRadius"
 */
minCameraOrbit: string;

/**
 * Maximum camera orbit bounds  
 * Format: "maxTheta maxPhi maxRadius"
 */
maxCameraOrbit: string;

/**
 * Minimum field of view (maximum zoom in)
 */
minFieldOfView: string;

/**
 * Maximum field of view (maximum zoom out)
 */
maxFieldOfView: string;

/**
 * Camera movement bounds constraint
 * Format: "box" or "sphere" to constrain target movement
 */
bounds: string;

Usage:

<!-- Constrain to front hemisphere only -->
<model-viewer 
  src="models/face.glb"
  min-camera-orbit="-90deg 0deg auto"
  max-camera-orbit="90deg 90deg auto"
  camera-controls>
</model-viewer>

<!-- Limit zoom range -->
<model-viewer 
  src="models/product.glb"
  min-field-of-view="15deg"
  max-field-of-view="45deg"
  camera-controls>
</model-viewer>

Interaction Behavior

Customize interaction prompts and sensitivity.

/**
 * Interaction prompt display mode
 * - 'auto': Show after delay when no interaction detected
 * - 'when-focused': Show only when element is focused
 * - 'none': Never show interaction prompt
 */
interactionPrompt: 'auto' | 'when-focused' | 'none';

/**
 * Time in milliseconds before showing interaction prompt
 */
interactionPromptThreshold: number;

/**
 * Control sensitivity multiplier (higher = more sensitive)
 */
orbitSensitivity: number;

/**
 * Zoom sensitivity multiplier (higher = more sensitive)
 */
zoomSensitivity: number;

/**
 * Pan sensitivity multiplier (higher = more sensitive)
 */
panSensitivity: number;

/**
 * Input sensitivity multiplier (higher = more sensitive)
 */
inputSensitivity: number;

/**
 * Reset interaction prompt to initial state
 */
resetInteractionPrompt(): void;

Usage:

<!-- Show prompt after 5 seconds, with reduced sensitivity -->
<model-viewer 
  src="models/delicate.glb"
  interaction-prompt="auto"
  interaction-prompt-threshold="5000"
  orbit-sensitivity="0.5"
  camera-controls>
</model-viewer>

Control Restrictions

Disable specific types of camera interactions.

/**
 * Disable zoom via scroll wheel or pinch gestures
 */
disableZoom: boolean;

/**
 * Disable camera panning (right-click drag or two-finger drag)
 */
disablePan: boolean;

/**
 * Disable tap-to-recenter functionality
 */
disableTap: boolean;

/**
 * Accessibility translations for screen readers
 * Can be an object with translations or string preset name or null
 */
a11y: A11yTranslationsInterface | string | null;

Usage:

<!-- Allow orbit only, no zoom or pan -->
<model-viewer 
  src="models/fixed-view.glb"
  camera-controls
  disable-zoom
  disable-pan>
</model-viewer>

Touch and Animation

Control touch behavior and camera animation.

/**
 * CSS touch-action property for touch event handling
 * - 'pan-y': Allow vertical page scrolling while model rotates
 * - 'none': Block all default touch behaviors
 */
touchAction: string;

/**
 * Camera animation interpolation decay rate
 * Higher values = snappier movement, lower = smoother
 */
interpolationDecay: number;

/**
 * Jump camera to current target position immediately (no animation)
 */
jumpCameraToGoal(): void;

Usage:

<!-- Allow page scrolling while interacting -->
<model-viewer 
  src="models/inline.glb"
  camera-controls
  touch-action="pan-y">
</model-viewer>
// Instant camera movement
modelViewer.cameraOrbit = "90deg 45deg 150%";
modelViewer.jumpCameraToGoal(); // Move immediately without animation

Events

Camera Change

Fired when camera position, target, or field of view changes.

interface CameraChangeEvent extends Event {
  type: 'camera-change';
  detail: {
    source: 'user-interaction' | 'none' | 'automatic';
  };
}

Usage:

modelViewer.addEventListener('camera-change', (event) => {
  console.log('Camera changed by:', event.detail.source);
  
  // Save current camera position
  const orbit = modelViewer.getCameraOrbit();
  const target = modelViewer.getCameraTarget();
  localStorage.setItem('cameraState', JSON.stringify({
    orbit: orbit.toString(),
    target: target.toString()
  }));
});

Advanced Usage Examples

Guided Tours

Create predefined camera positions for guided viewing:

const tourPositions = [
  { orbit: "0deg 75deg 105%", target: "0m 0m 0m" },
  { orbit: "90deg 75deg 105%", target: "0m 1m 0m" },
  { orbit: "180deg 45deg 150%", target: "0m 0.5m 0m" }
];

let currentTour = 0;

function nextTourStop() {
  const position = tourPositions[currentTour];
  modelViewer.cameraOrbit = position.orbit;
  modelViewer.cameraTarget = position.target;
  currentTour = (currentTour + 1) % tourPositions.length;
}

Responsive Camera Settings

Adjust camera behavior based on screen size:

function updateCameraForScreenSize() {
  if (window.innerWidth < 768) {
    // Mobile: more sensitive controls, constrained bounds
    modelViewer.orbitSensitivity = 1.5;
    modelViewer.maxCameraOrbit = "Infinity 90deg 300%";
    modelViewer.touchAction = "pan-y";
  } else {
    // Desktop: standard settings
    modelViewer.orbitSensitivity = 1.0;
    modelViewer.maxCameraOrbit = "Infinity 160deg 300%";
    modelViewer.touchAction = "none";
  }
}

window.addEventListener('resize', updateCameraForScreenSize);