Create WebXR DOM Overlays in Three.js
This article explains how to build an interactive HTML/DOM overlay that floats seamlessly over a WebXR augmented reality (AR) camera feed using Three.js. You will learn how to configure the WebXR session, structure the HTML and CSS for proper layering, and handle user interactions between standard web elements and the 3D WebXR environment.
1. Define the HTML Structure
To create a DOM overlay, you need a container element in your HTML that holds all the UI elements (buttons, text, menus) you want to display over the AR view.
<div id="overlay-container">
<div id="ui-screen">
<h1>AR Dashboard</h1>
<button id="action-button">Spawn Object</button>
</div>
</div>2. Style the Overlay with CSS
For the overlay to look correct and allow interactions to pass
through to the 3D scene, you must position it absolutely and manage the
pointer-events property.
#overlay-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none; /* Hidden until the AR session starts */
pointer-events: none; /* Allows taps to pass through to the 3D scene */
z-index: 10;
}
#ui-screen {
padding: 20px;
}
#action-button {
pointer-events: auto; /* Re-enables interaction specifically for the button */
padding: 12px 24px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
}Setting pointer-events: none on the root container
ensures that users can still tap on the physical floor or objects in AR
to perform hit-testing. Setting pointer-events: auto on the
button ensures the button remains clickable.
3. Configure the WebXR Session in Three.js
When initializing the WebXR session using Three.js’s
ARButton, you must request the dom-overlay
feature and specify the root DOM element you want to overlay.
import { ARButton } from 'three/examples/jsm/webxr/ARButton.js';
// Setup your renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.xr.enabled = true;
document.body.appendChild(renderer.domElement);
const overlayContainer = document.getElementById('overlay-container');
// Configure WebXR options
const sessionInit = {
requiredFeatures: ['hit-test'],
optionalFeatures: ['dom-overlay'],
domOverlay: { root: overlayContainer }
};
// Create and add the AR Button to the page
const arButton = ARButton.createButton(renderer, sessionInit);
document.body.appendChild(arButton);The WebXR browser agent will automatically toggle the visibility of
the overlayContainer (changing its display state) when the
AR session starts and ends.
4. Handle Interaction Events
Once the overlay is active, you can attach standard JavaScript event listeners to your DOM elements. These listeners can directly manipulate your Three.js scene.
const actionButton = document.getElementById('action-button');
actionButton.addEventListener('click', (event) => {
// Prevent WebXR select events from firing simultaneously
event.stopPropagation();
// Trigger your Three.js logic here
spawnVirtualObject();
});
function spawnVirtualObject() {
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
// Position the cube in front of the camera
cube.position.set(0, 0, -0.5).applyMatrix4(camera.matrixWorld);
scene.add(cube);
}Using event.stopPropagation() is critical. It prevents
the click event on the HTML button from bubbling down and triggering a
WebXR select event (which would otherwise register as a tap
in the 3D space).