2026 Spatial ComputingSafari & Web
WWDC26 · 19 min · Spatial Computing / Safari & Web
Explore immersive website environments in visionOS
Transport your website’s visitors into virtual environments in Apple Vision Pro using the new Immersive API in JavaScript. Explore how to request immersive transitions from an inline model element, create compelling immersive experiences using features like video docking, and optimize performance for rich, real-world-scale experiences — all with just a few lines of code running on your website.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 16 snippets
Basic model element
<model src="teapot.usdz">
</model> Model element with environment map
<model src="teapot.usdz"
environmentmap="kitchen.hdr">
</model> Adding the environment model on the page for inline preview
<div class="seat-preview">
<model id="theater"
src="theater-model.usdz"
environmentmap="theater-lighting.hdr">
</model>
</div> Reset the model entity transform
const theater = document.getElementById("theater");
async function updateModelTransform() {
// Make sure the model is loaded
await theater.ready;
// Create a transform matrix
const identity = new DOMMatrix();
// Apply the transform matrix to the model
theater.entityTransform = identity;
}
updateModelTransform(); Translate the model down
const theater = document.getElementById("theater");
async function updateModelTransform() {
// Make sure the model is loaded
await theater.ready;
// Create a transform matrix
const transform = new DOMMatrix();
// Translate model down, for eye level preview
transform.translateSelf(
0, // x
-1.0, // y
0 // z
);
// Apply the transform matrix to the model
theater.entityTransform = transform;
}
updateModelTransform(); Build the seat transform
function buildTransform(seat) {
const transform = new DOMMatrix();
const { x, y, z, ry } = seat;
// Rotate and translate the model to match
// the seat's origin and orientation
transform.rotateSelf(0, -ry, 0);
transform.translateSelf(-x, -y, -z);
// Translate the model down, for eye level preview
transform.translateSelf(0, -1.0, 0);
return transform;
} Detect feature availability
if (document.immersiveEnabled) {
immersiveButton.hidden = false;
} Request the immersive transition on the model
immersiveButton.addEventListener("click", async () => {
await model.requestImmersive();
}); Build immersive transform
function buildTransform(seat, immersive) {
const transform = new DOMMatrix();
// [...] Seat transform logic
if (immersive) {
// Rotate to the left
transform.rotateSelf(
0, // x
45, // y
0 // z
);
} else {
// [...] Eye level translation
}
return transform;
} Update the entity transform and the layout on immersive state updates
theater.addEventListener("immersivechange", () => {
const isImmersive = !!document.immersiveElement;
const transform = buildTransform(isImmersive, currentSeat);
theater.entityTransform = transform;
document.body.classList.toggle("immersive", isImmersive);
}); Hide the inline preview
<model id="escapeRoom"
src="escape-room.usdz"
environmentmap="room-lighting.hdr"
style="display: none">
</model> Request an immersive transition on the escape room model
const enterButton = document.getElementById("enterButton");
const escapeRoom = document.getElementById("escapeRoom");
enterButton.addEventListener("click", () => {
await escapeRoom.requestImmersive();
}); Handle the request result and show a loading animation
enterButton.addEventListener("click", async () => {
showLoadingAnimation();
try {
await escapeRoom.requestImmersive();
} catch (error) {
console.log(error);
} finally {
hideLoadingAnimation();
}
}); Dock the video in the environment with the fullscreen API
const trailerVideo = document.getElementById("trailerVideo");
const demoButton = document.getElementById("demoButton");
demoButton.addEventListener("click", async () => {
await trailerVideo.requestFullscreen();
}); Play the model animation
const trailerVideo = document.getElementById("trailerVideo");
const escapeRoom = document.getElementById("escapeRoom");
trailerVideo.addEventListener("ended", async () => {
await document.exitFullscreen();
escapeRoom.play();
}); Compress your USDZ with usdcrush
usdcrush model.usdz -o optimized.usdz Resources
- Download - Immersive model add-on for Blender
- WebKit.org - Theater Ticket Sales immersive website environment demo for Apple Vision Pro
- WebKit.org - Escape Game immersive website demo for Apple Vision Pro
- GitHub: Spatial Backdrop explainer
- WebKit.org – Report issues to the WebKit open-source project
- Submit feedback
Related sessions
-
29 min -
33 min -
16 min -
16 min -
17 min