2025 Spatial ComputingSafari & Web
WWDC25 · 29 min · Spatial Computing / Safari & Web
What’s new for the spatial web
Discover the latest spatial features for the web on visionOS 26. We’ll cover how to display inline 3D models with the brand new HTML model element. And we’ll share powerful features, including model lighting, interactions, and animations. Learn how to embed newly supported immersive media on your web site, such as 360-degree video and Apple Immersive Video. And get a sneak peek at adding a custom environment to your web pages.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 24 snippets
Embed 3D models - Basic syntax
<model src="teapot.usdz"></model> Embed 3D models with source element
<model>
<source src="teapot.usdz" type="model/vnd.usdz+zip">
</model> Example server configurations to add USDZ MIME type support
# Apache
```
AddType model/vnd.usdz+zip .usdz
```
# NGINX mime.types
```
types {
...
model/vnd.usdz+zip usdz;
}
```
# Python HTTP server
```
import http.server
Handler = http.server.SimpleHTTPRequestHandle
Handler.extensions_map = { ".usdz": "model/vnd.usdz+zip" }
httpd = http.server.HTTPServer(("", 8000), Handler)
httpd.serve_forever()
``` Specify a fall back image for <model> element
<model src="camera.usdz">
<img src="camera.png">
</model> Example 2D rendering fallback experience
<!-- <model-viewer> library from https://modelviewer.dev/ -->
<script type="module"
src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js">
</script>
<model src="camera.usdz">
<!-- Fallback experience for backward compatibility -->
<model-viewer src="camera.glb"></model-viewer>
</model> Detect if the model element is supported
if (window.HTMLModelElement) {
// Supported by this browser
} else {
// Not supported by this browser
} Implementing a loading indicator using .ready promise
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
if (window.HTMLModelElement) {
mymodel.ready.then(result => {
// Hide the loading indicator
// Show the model
}).catch(error => {
// Loading error occurred, show a retry button
});
}
</script> CSS example for setting the color of the virtual space
<body>
<!-- page content here -->
<model src="camera.usdz" class="my_model"></model>
</body>
<style>
:root {
--main-bg-color: rgb(240, 240, 240);
}
body {
background-color: var(--main-bg-color);
}
.my_model {
/* set the virtual space color */
background-color: var(--main-bg-color);
}
</style> CSS example for frosted glass panel on top of a <model>
<div class="container">
<model src="camera.usdz"></model>
<div class="panel"> ... </div>
</div>
<style>
.container {
position: relative;
}
.panel {
position: absolute;
left: 60%;
backdrop-filter: blur(20px);
background: linear-gradient(to right,
rgba(240, 240, 240, 0.8),
rgba(240, 240, 240, 0.5) 4px);
}
</style> Setting image-based lighting (IBL) with environmentmap
<model src="camera.usdz" environmentmap="sunset.exr"></model> Allowing inline rotation with stagemode
<model src="teapot.usdz" stagemode="orbit"></model> Customize placement with JavaScript entityTransform
<model src="teapot.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
mymodel.ready.then(result => {
const matrix = mymodel.entityTransform; // DOMMatrixReadOnly
});
</script> Make the model face right with entityTransform
<model src="teapot.usdz" id="mymodel"></model>
<a onclick="turnRight()">Right</a>
<script>
const mymodel = document.getElementById("mymodel");
function turnRight() {
const matrix = mymodel.entityTransform; // DOMMatrixReadOnly
const newMatrix = matrix.rotateAxisAngle(0, 1, 0, 90);
mymodel.entityTransform = newMatrix;
}
</script> Setting the entityTransform to an identity matrix
model.entityTransform = new DOMMatrix(); Basic animation control
<model src="toy.usdz" id="mymodel" loop autoplay></model>
<button onclick="toggleAnimation()">Play/Pause</button>
<script>
const mymodel = document.getElementById("mymodel");
function toggleAnimation() {
if (mymodel.paused) {
mymodel.play();
} else {
mymodel.pause();
}
}
</script> Jump to animation timestamp using .currentTime property
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
function openFlash() {
mymodel.currentTime = 1; // Unit is seconds
}
function openScreen() {
mymodel.currentTime = 3; // Unit is seconds
}
</script> Update .currentTime with a slider
<model src="camera.usdz" id="mymodel"></model>
<input type="range" id="slider" min="2" max="3" step="any" value="2">
<script>
const mymodel = document.getElementById("mymodel");
slider.addEventListener("input", (event) => {
mymodel.currentTime = event.target.value;
});
</script> Generate USDZ with three.js and display with <model>
import * as THREE from "three";
import { USDZExporter } from "three/examples/exporters/USDZExporter.js";
async function generateModel() {
const scene = new THREE.Scene();
// ... create a really nice scene procedurally ...
const bytes = await new USDZExporter().parseAsync(scene);
const objURL = URL.createObjectURL(new Blob([bytes]));
const mymodel = document.getElementById("mymodel");
mymodel.setAttribute("src", objURL);
} Embed immersive media
<video src="spatial_video.mov"></video> <!-- Single file -->
<video src="360_video.m3u8"></video> <!-- HTTP Live Streaming --> Going full screen with Javascript for <video> elements
<video src="360_video.m3u8" id="player" controls></video>
<script>
const player = document.getElementById("player");
player.requestFullScreen();
</script> Embed panoramas and offer full screen with Javascript
<picture>
<source media="(max-width: 799px)" srcset="thumbnail.jpg">
<source media="(min-width: 800px)" srcset="panorama.jpg">
<img src="panorama.jpg" id="pano">
</picture>
<script>
const pano = document.getElementById("pano");
pano.requestFullScreen();
</script> Embed spatial photos and offer full screen with Javascript
<img src="spatial.heic" id="img">
<script>
const img = document.getElementById("img");
img.requestFullScreen();
</script> Embed spatial photos with the new "controls" attribute
<img src="spatial.heic" id="img" controls> Provide a custom environment
<link rel="spatial-backdrop" href="office.usdz" environmentmap="lighting.hdr"> Resources
Related sessions
-
47 min -
26 min -
26 min -
9 min -
20 min -
14 min -
21 min