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
Configure realistic lighting, shadows, and environment maps to enhance the visual quality and realism of 3D models.
Set environment maps for realistic lighting and reflections.
/**
* URL to HDR environment image for lighting and reflections
* Supports HDR (.hdr, .exr) and LDR (.jpg, .png) formats
* Set to 'neutral' for built-in neutral lighting
*/
environmentImage: string;Usage:
<!-- HDR environment for realistic lighting -->
<model-viewer
src="models/car.glb"
environment-image="environments/studio.hdr">
</model-viewer>
<!-- Built-in neutral lighting -->
<model-viewer
src="models/product.glb"
environment-image="neutral">
</model-viewer>// Switch environments dynamically
const environments = [
'environments/studio.hdr',
'environments/outdoor.hdr',
'environments/indoor.hdr'
];
let currentEnv = 0;
function cycleEnvironment() {
modelViewer.environmentImage = environments[currentEnv];
currentEnv = (currentEnv + 1) % environments.length;
}Control the background environment visible behind the model.
/**
* URL to skybox background image
* Can be different from environmentImage for creative effects
* Set to same as environmentImage for consistent lighting
*/
skyboxImage: string;Usage:
<!-- Different skybox and environment -->
<model-viewer
src="models/spaceship.glb"
environment-image="environments/studio.hdr"
skybox-image="backgrounds/space.jpg">
</model-viewer>
<!-- Matching environment and skybox -->
<model-viewer
src="models/outdoor-scene.glb"
environment-image="environments/park.hdr"
skybox-image="environments/park.hdr">
</model-viewer>Adjust overall brightness and exposure of the scene.
/**
* Scene exposure/brightness multiplier
* Values > 1.0 increase brightness, < 1.0 decrease brightness
* Typical range: 0.1 to 3.0
*/
exposure: number;Usage:
<!-- Brighter exposure for dark models -->
<model-viewer
src="models/dark-material.glb"
exposure="1.5">
</model-viewer>// Dynamic exposure adjustment
const exposureSlider = document.getElementById('exposure');
exposureSlider.addEventListener('input', (e) => {
modelViewer.exposure = parseFloat(e.target.value);
});Control shadow rendering for increased realism.
/**
* Shadow intensity (0 = no shadows, 1 = full shadows)
* Shadows are cast onto an invisible ground plane
*/
shadowIntensity: number;
/**
* Shadow softness/blur amount
* Higher values create softer, more diffused shadows
* Range: 0.0 (sharp) to 2.0 (very soft)
*/
shadowSoftness: number;Usage:
<!-- Soft, realistic shadows -->
<model-viewer
src="models/furniture.glb"
shadow-intensity="0.8"
shadow-softness="1.2">
</model-viewer>
<!-- Sharp shadows for technical visualization -->
<model-viewer
src="models/machine.glb"
shadow-intensity="1.0"
shadow-softness="0.1">
</model-viewer>Enable simplified, consistent lighting for product visualization.
/**
* Use neutral lighting setup instead of environment-based lighting
* Provides consistent, diffused lighting ideal for product shots
* Overrides environmentImage when enabled
*/
neutralLighting: boolean;
/**
* Tone mapping method for HDR rendering
*/
toneMapping: 'auto' | 'aces' | 'agx' | 'commerce' | 'neutral' | 'reinhard' | 'cineon' | 'linear' | 'none';
/**
* Height positioning for grounded skybox
*/
skyboxHeight: string;Usage:
<!-- Consistent product lighting -->
<model-viewer
src="models/jewelry.glb"
neutral-lighting>
</model-viewer>
<!-- Compare neutral vs environment lighting -->
<button onclick="toggleLighting()">Toggle Lighting</button>
<script>
function toggleLighting() {
modelViewer.neutralLighting = !modelViewer.neutralLighting;
}
</script>Professional studio setup with controlled lighting:
<model-viewer
src="models/product.glb"
environment-image="environments/studio_small_03_1k.hdr"
exposure="1.0"
shadow-intensity="0.7"
shadow-softness="1.0">
</model-viewer>Natural outdoor lighting with soft shadows:
<model-viewer
src="models/architecture.glb"
environment-image="environments/kloofendal_48d_partly_cloudy_1k.hdr"
exposure="1.2"
shadow-intensity="0.9"
shadow-softness="1.5">
</model-viewer>High contrast lighting for artistic effects:
<model-viewer
src="models/sculpture.glb"
environment-image="environments/venice_sunset_1k.hdr"
exposure="0.8"
shadow-intensity="1.0"
shadow-softness="0.5">
</model-viewer>Clean, consistent lighting for e-commerce:
<model-viewer
src="models/product.glb"
neutral-lighting
shadow-intensity="0.3"
shadow-softness="1.8">
</model-viewer>Create interactive lighting controls:
<div class="lighting-controls">
<label>
Exposure: <input type="range" id="exposure" min="0.1" max="3.0" step="0.1" value="1.0">
</label>
<label>
Shadow Intensity: <input type="range" id="shadow" min="0" max="1" step="0.1" value="0.7">
</label>
<label>
Shadow Softness: <input type="range" id="softness" min="0" max="2" step="0.1" value="1.0">
</label>
</div>
<script>
document.getElementById('exposure').addEventListener('input', (e) => {
modelViewer.exposure = parseFloat(e.target.value);
});
document.getElementById('shadow').addEventListener('input', (e) => {
modelViewer.shadowIntensity = parseFloat(e.target.value);
});
document.getElementById('softness').addEventListener('input', (e) => {
modelViewer.shadowSoftness = parseFloat(e.target.value);
});
</script>Create preset lighting environments:
const lightingPresets = {
studio: {
environmentImage: 'environments/studio.hdr',
exposure: 1.0,
shadowIntensity: 0.7,
shadowSoftness: 1.0,
neutralLighting: false
},
outdoor: {
environmentImage: 'environments/park.hdr',
exposure: 1.2,
shadowIntensity: 0.9,
shadowSoftness: 1.5,
neutralLighting: false
},
neutral: {
environmentImage: null,
exposure: 1.0,
shadowIntensity: 0.3,
shadowSoftness: 1.8,
neutralLighting: true
}
};
function applyLightingPreset(presetName) {
const preset = lightingPresets[presetName];
if (!preset) return;
Object.keys(preset).forEach(key => {
if (preset[key] !== null) {
modelViewer[key] = preset[key];
}
});
}
// UI for preset selection
document.getElementById('preset-studio').addEventListener('click',
() => applyLightingPreset('studio'));
document.getElementById('preset-outdoor').addEventListener('click',
() => applyLightingPreset('outdoor'));
document.getElementById('preset-neutral').addEventListener('click',
() => applyLightingPreset('neutral'));Adjust lighting based on screen size or device capabilities:
function setupResponsiveLighting() {
const isMobile = window.innerWidth < 768;
const hasHDRSupport = checkHDRSupport();
if (isMobile) {
// Lighter lighting processing for mobile
modelViewer.neutralLighting = true;
modelViewer.shadowIntensity = 0.3;
modelViewer.shadowSoftness = 1.0;
} else if (hasHDRSupport) {
// Full HDR environment for capable devices
modelViewer.environmentImage = 'environments/studio_4k.hdr';
modelViewer.shadowIntensity = 0.8;
modelViewer.shadowSoftness = 1.2;
} else {
// Fallback to LDR environment
modelViewer.environmentImage = 'environments/studio.jpg';
modelViewer.shadowIntensity = 0.6;
modelViewer.shadowSoftness = 1.0;
}
}
function checkHDRSupport() {
// Simplified HDR detection
return window.screen && window.screen.colorGamut === 'p3';
}
window.addEventListener('resize', setupResponsiveLighting);
modelViewer.addEventListener('load', setupResponsiveLighting);Animate lighting changes for dynamic effects:
class LightingAnimator {
constructor(modelViewer) {
this.modelViewer = modelViewer;
this.isAnimating = false;
}
async animateToPreset(targetPreset, duration = 1000) {
if (this.isAnimating) return;
this.isAnimating = true;
const startValues = {
exposure: this.modelViewer.exposure,
shadowIntensity: this.modelViewer.shadowIntensity,
shadowSoftness: this.modelViewer.shadowSoftness
};
const startTime = performance.now();
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Smooth easing function
const eased = 1 - Math.pow(1 - progress, 3);
// Interpolate values
this.modelViewer.exposure = this.lerp(
startValues.exposure, targetPreset.exposure, eased);
this.modelViewer.shadowIntensity = this.lerp(
startValues.shadowIntensity, targetPreset.shadowIntensity, eased);
this.modelViewer.shadowSoftness = this.lerp(
startValues.shadowSoftness, targetPreset.shadowSoftness, eased);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// Set final values and environment
if (targetPreset.environmentImage) {
this.modelViewer.environmentImage = targetPreset.environmentImage;
}
this.modelViewer.neutralLighting = targetPreset.neutralLighting;
this.isAnimating = false;
}
};
requestAnimationFrame(animate);
}
lerp(start, end, t) {
return start + (end - start) * t;
}
}
// Usage
const animator = new LightingAnimator(modelViewer);
animator.animateToPreset(lightingPresets.outdoor, 2000);