Web component for easily displaying interactive 3D models with AR support across browsers and devices
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Interactive camera controls enabling users to orbit, zoom, and pan around 3D models with customizable bounds and behaviors.
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>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%";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";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>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>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>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>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 animationFired 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()
}));
});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;
}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);