Access and manipulate the loaded 3D model's scene graph, including materials, variants, transformations, and export capabilities.
Switch between different material configurations defined in the glTF model.
/**
* Name of the current material variant to display
* Must be one of the available variants from the model
*/
variantName: string;
/**
* List of all material variants available in the loaded model
* Empty array if model has no variants defined
*/
readonly availableVariants: Array<string>;
/**
* The loaded model object (readonly)
* Provides access to the scene graph structure
*/
readonly model?: Model;
/**
* Original glTF JSON data (readonly)
* Access to the raw glTF specification data
*/
readonly originalGltfJson: GLTF | null;Usage:
<!-- Model with multiple material variants -->
<model-viewer
src="models/car.glb"
variant-name="red_paint">
</model-viewer>// Check available variants after model loads
modelViewer.addEventListener('load', () => {
console.log('Available variants:', modelViewer.availableVariants);
// ['red_paint', 'blue_paint', 'silver_metallic', 'carbon_fiber']
});
// Switch variants dynamically
function switchToBlue() {
if (modelViewer.availableVariants.includes('blue_paint')) {
modelViewer.variantName = 'blue_paint';
}
}
// Create variant selector UI
function createVariantSelector() {
const select = document.createElement('select');
modelViewer.availableVariants.forEach(variant => {
const option = document.createElement('option');
option.value = variant;
option.textContent = variant.replace(/_/g, ' ').toUpperCase();
select.appendChild(option);
});
select.addEventListener('change', (e) => {
modelViewer.variantName = e.target.value;
});
return select;
}Control the overall position, rotation, and scale of the model.
/**
* Model orientation in 3D space
* Format: "x y z" where each component is rotation in degrees
*/
orientation: string;
/**
* Model scaling factor
* Format: "x y z" for non-uniform scaling, or single value for uniform
* Can use units like "2m 2m 2m" or percentage "200% 200% 200%"
*/
scale: string;Usage:
<!-- Rotate model 45 degrees around Y axis -->
<model-viewer
src="models/building.glb"
orientation="0deg 45deg 0deg">
</model-viewer>
<!-- Scale model to 2x size -->
<model-viewer
src="models/miniature.glb"
scale="2 2 2">
</model-viewer>
<!-- Non-uniform scaling with units -->
<model-viewer
src="models/logo.glb"
scale="3m 1m 1m">
</model-viewer>// Animate rotation
let rotation = 0;
function rotateModel() {
rotation += 1;
modelViewer.orientation = `0deg ${rotation}deg 0deg`;
requestAnimationFrame(rotateModel);
}
// Interactive scaling
const scaleSlider = document.getElementById('scale');
scaleSlider.addEventListener('input', (e) => {
const scale = e.target.value;
modelViewer.scale = `${scale} ${scale} ${scale}`;
});Export the current model state as glTF data for saving or processing.
/**
* Export the current scene as glTF/GLB data
* @param options - Export configuration options
* @returns Promise resolving to Blob containing glTF data
*/
exportScene(options?: ExportOptions): Promise<Blob>;
interface ExportOptions {
/** Export as binary GLB format (true) or text glTF (false) */
binary?: boolean;
/** Include transformation matrices (translation, rotation, scale) */
trs?: boolean;
/** Export only visible objects */
onlyVisible?: boolean;
/** Truncate draw ranges to visible geometry */
truncateDrawRange?: boolean;
/** Embed images in glTF instead of external references */
embedImages?: boolean;
/** Maximum texture resolution in pixels */
maxTextureSize?: number;
}Usage:
// Basic export as GLB
async function exportModel() {
try {
const blob = await modelViewer.exportScene({ binary: true });
// Create download link
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'exported-model.glb';
a.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error('Export failed:', error);
}
}
// Export with optimization
async function exportOptimized() {
const blob = await modelViewer.exportScene({
binary: true,
onlyVisible: true,
embedImages: true,
maxTextureSize: 1024,
truncateDrawRange: true
});
return blob;
}
// Export current variant as separate file
async function exportCurrentVariant() {
const variant = modelViewer.variantName || 'default';
const blob = await modelViewer.exportScene({
binary: true,
trs: true
});
const fileName = `model-${variant}.glb`;
downloadBlob(blob, fileName);
}Create and manage textures dynamically.
/**
* Create a texture from image URI for use in materials
* @param uri - Image URL or data URI
* @param type - Optional texture type specification
* @returns Promise resolving to ModelViewer texture or null if failed
*/
createTexture(uri: string, type?: string): Promise<ModelViewerTexture | null>;
/**
* Create a texture from Lottie animation file
* @param uri - Lottie JSON file URL
* @param quality - Rendering quality (default: 1)
* @returns Promise resolving to animated texture
*/
createLottieTexture(uri: string, quality?: number): Promise<ModelViewerTexture | null>;
/**
* Create a texture from video element for dynamic content
* @param uri - Video file URL
* @returns Video texture object
*/
createVideoTexture(uri: string): ModelViewerTexture;
/**
* Create a texture from HTML5 canvas for procedural content
* @returns Canvas texture object for custom drawing
*/
createCanvasTexture(): ModelViewerTexture;
/**
* Find material at specific screen coordinates via raycasting
* @param pixelX - X coordinate on screen
* @param pixelY - Y coordinate on screen
* @returns Material at that point or null if no intersection
*/
materialFromPoint(pixelX: number, pixelY: number): Material | null;Usage:
// Create texture from uploaded image
async function applyCustomTexture(imageFile) {
const dataURL = await fileToDataURL(imageFile);
const texture = await modelViewer.createTexture(dataURL);
if (texture) {
console.log('Texture created successfully');
// Apply to material (requires accessing scene graph)
applyTextureToMaterial(texture);
}
}
// Create texture from URL
async function loadEnvironmentTexture() {
const texture = await modelViewer.createTexture('textures/environment.jpg');
return texture;
}
function fileToDataURL(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.readAsDataURL(file);
});
}For advanced users who need direct access to the Three.js scene graph:
// Access the Three.js scene (requires knowledge of internals)
function getThreeScene() {
// Note: This accesses private symbols and may break in updates
return modelViewer[$scene];
}
// Find specific objects in the scene
function findObjectByName(name) {
const scene = getThreeScene();
return scene.getObjectByName(name);
}
// Modify materials directly
function modifyMaterial(materialName, properties) {
const scene = getThreeScene();
scene.traverse((child) => {
if (child.material && child.material.name === materialName) {
Object.assign(child.material, properties);
child.material.needsUpdate = true;
}
});
}Create custom material variants programmatically:
class VariantCreator {
constructor(modelViewer) {
this.modelViewer = modelViewer;
this.customVariants = new Map();
}
async createVariant(name, materialConfig) {
// Store variant configuration
this.customVariants.set(name, materialConfig);
// Apply variant
await this.applyVariant(name);
}
async applyVariant(name) {
if (this.customVariants.has(name)) {
const config = this.customVariants.get(name);
await this.applyMaterialConfig(config);
} else if (this.modelViewer.availableVariants.includes(name)) {
this.modelViewer.variantName = name;
}
}
async applyMaterialConfig(config) {
const scene = this.modelViewer[$scene];
for (const [materialName, properties] of Object.entries(config)) {
scene.traverse(async (child) => {
if (child.material && child.material.name === materialName) {
// Apply properties
Object.assign(child.material, properties);
// Handle texture properties
if (properties.map && typeof properties.map === 'string') {
const texture = await this.modelViewer.createTexture(properties.map);
child.material.map = texture;
}
child.material.needsUpdate = true;
}
});
}
}
}
// Usage
const variantCreator = new VariantCreator(modelViewer);
// Create custom gold variant
variantCreator.createVariant('custom_gold', {
'body_material': {
color: 0xffd700,
metalness: 1.0,
roughness: 0.1
},
'trim_material': {
color: 0xffffff,
metalness: 0.0,
roughness: 0.8
}
});Perform operations on multiple scene objects efficiently:
class SceneBatch {
constructor(modelViewer) {
this.modelViewer = modelViewer;
this.operations = [];
}
// Queue operations
setMaterialProperty(materialName, property, value) {
this.operations.push({
type: 'material',
target: materialName,
property,
value
});
return this;
}
setObjectVisibility(objectName, visible) {
this.operations.push({
type: 'visibility',
target: objectName,
visible
});
return this;
}
// Execute all queued operations
execute() {
const scene = this.modelViewer[$scene];
scene.traverse((child) => {
this.operations.forEach(op => {
switch (op.type) {
case 'material':
if (child.material && child.material.name === op.target) {
child.material[op.property] = op.value;
child.material.needsUpdate = true;
}
break;
case 'visibility':
if (child.name === op.target) {
child.visible = op.visible;
}
break;
}
});
});
// Clear operations
this.operations = [];
return this;
}
}
// Usage - batch multiple changes for performance
const batch = new SceneBatch(modelViewer);
batch
.setMaterialProperty('wheel_material', 'color', 0xff0000)
.setMaterialProperty('body_material', 'roughness', 0.2)
.setObjectVisibility('interior', false)
.execute();