Handle WebXR Session End Event in Three.js

When developing Virtual Reality (VR) or Augmented Reality (AR) experiences with Three.js, properly managing the lifecycle of a WebXR session is crucial for a seamless user experience. This article provides a direct, step-by-step guide on how to listen for and handle the end event of an active WebXR session in Three.js, ensuring your application gracefully transitions back to a standard 2D web interface when a user exits immersive mode.

Three.js simplifies WebXR management through the WebXRManager, which is accessible via your WebGL renderer (renderer.xr). The most straightforward and idiomatic way to detect when a session ends is to listen for the sessionend event directly on this manager.

// Set up your listener on the WebXRManager
renderer.xr.addEventListener('sessionend', onSessionEnd);

function onSessionEnd() {
    console.log('The WebXR session has ended.');
    
    // 1. Re-enable standard screen controls (e.g., OrbitControls)
    controls.enabled = true;
    
    // 2. Show HTML UI elements hidden during VR/AR
    document.getElementById('ui-overlay').style.display = 'block';
    
    // 3. Reset camera positions if necessary
    camera.position.set(0, 1.6, 3);
}

This method is highly recommended because Three.js internally manages the lifecycle binding, ensuring that your callback fires reliably regardless of how the session was terminated (e.g., user exiting via headset UI, browser crash, or programmatically).

Method 2: Listening to the Native WebXR Session Event

If your application requires access to the raw WebXR session object, you can listen to the native end event. To do this, you first listen for the Three.js sessionstart event to retrieve the native session, and then attach an event listener to it.

renderer.xr.addEventListener('sessionstart', () => {
    // Retrieve the active native WebXR session
    const session = renderer.xr.getSession();
    
    // Listen for the native 'end' event
    session.addEventListener('end', () => {
        console.log('Native WebXR session terminated.');
        // Perform native WebXR cleanup here
    });
});

This approach is useful if you are integrating custom WebXR APIs or external libraries alongside Three.js that require direct interaction with the XRSession object.

Essential Cleanup Actions on Session End

When a WebXR session ends, the browser stops rendering to the headset and resumes rendering to the standard 2D canvas. To prevent visual glitches or broken controls, you should perform these actions inside your event handler:

  1. Re-enable 2D Controls: If you disabled camera controllers like OrbitControls or PointerLockControls during the XR session, turn them back on.
  2. Toggle HTML Overlays: Bring back any 2D buttons, navigation bars, or textual overlays that were hidden during the immersive experience.
  3. Reset Camera Transform: Immersive sessions modify the Three.js camera matrix based on headset tracking. Resetting the camera’s position and rotation ensures your 2D fallback view behaves predictably.