Walk a scene in VR
Let a visitor stand in a scene with a headset such as a Meta Quest 3, starting at a view you chose and moving between views with the trigger.
The three.js renderer can show a scene in a VR headset through WebXR. It is off unless you ask
for it: pass xr to ThreeView (or xr: true to createThreeRenderer), and nothing about a
page that does not ask changes.
"use client";
import { useEffect, useRef, useState } from "react";
import { KloradProvider, SceneObject } from "@klorad/react";
import { vrSupported, type CameraView } from "@klorad/engine-three";
import { ThreeView, type ThreeViewHandle } from "@klorad/engine-three/react";
// Each stop is an ordinary camera view: the visitor stands where its camera stands and faces its heading.
const stops: CameraView[] = [
{ target: { east: 0, north: 0, up: 1.6 }, heading: 0, tilt: 5, distance: 6 },
{ target: { east: 0, north: 0, up: 1.2 }, heading: 135, tilt: 15, distance: 2.5 },
];
export default function Exhibit() {
const view = useRef<ThreeViewHandle>(null);
const [canVR, setCanVR] = useState(false);
const [stop, setStop] = useState(0);
useEffect(() => void vrSupported().then(setCanVR), []);
const next = () => {
const i = (stop + 1) % stops.length;
setStop(i);
view.current?.moveVR(stops[i]!);
};
return (
<KloradProvider options={{ coordinateSystem: { origin: { lat: 40.632, lon: 22.951, height: 12 } } }}>
<SceneObject id="piece" name="Scanned piece" position={{ east: 0, north: 0 }} geometry={{ kind: "model", uri: "/models/piece.stl" }} />
{canVR ? <button onClick={() => void view.current?.enterVR(stops[stop])}>Enter VR</button> : null}
<div style={{ height: 480 }}>
<ThreeView ref={view} xr onXRSelect={next} />
</div>
</KloradProvider>
);
}Where the visitor stands
A VR stop is an ordinary CameraView, the same value view() returns and setView takes.
enterVR(view) starts an immersive-vr session on the headset's floor and places the visitor
so their eyes are where that view's camera is (an eye height of 1.6 m, EYE_HEIGHT) and they
face its heading. The tilt of the view does not tilt the visitor: they look up and down with
their head. Called without a view, it starts from the view on the screen.
moveVR(view) moves the visitor to another stop while the session runs, by moving the floor
under them; nothing in the scene moves. Outside a session it does nothing, so you can call it
next to setView in the same handler.
The trigger
onXRSelect fires when either controller's trigger is pressed. The renderer does not decide
what the trigger means: the example walks the stops in order.
Which browsers
vrSupported() resolves to true in a browser that can start an immersive VR session (the
Meta Quest browser can) and false elsewhere, including a desktop without a headset. Show the
button only when it is true. A browser asks the visitor before a session starts, and
enterVR only works from a click or a tap.
WebXR runs only on a secure page: https, or localhost while you develop. To try a page from
your machine on a headset, open it over https or forward the port to the headset.
When the visitor leaves VR, the page's camera comes back to where it was.
Run it liveSandbox: VRA room of exhibits walked in a headset, starting at the first stop and moving on with the trigger.Something wrong or unclear? Tell us.