A simple static WebXR-enabled virtual reality scene built with Three.js, featuring interactive controllers and object manipulation. The entire project runs directly in the browser with no build steps required.
- Basic Three.js VR Scene
This repository provides a basic Three.js setup for WebXR, allowing you to experience a virtual reality (VR) scene directly in the browser. It uses VRButton to enter VR mode and demonstrates interaction with objects through controllers, object grabbing, and basic raycasting.
- 🌍 Immersive Environment
- Skybox (using
SphereGeometry
with a texture) - Ground plane (texture repeated over a large area)
- Multiple FBX Models (loaded via
FBXLoader
and placed at different coordinates)
- Skybox (using
- 🎮 VR Controllers using
XRControllerModelFactory
, including:- Grab nearby objects when the right controller is close enough
- Raycast with the left controller (pull objects in or relocate them)
- 🔄 Runtime Options
- Support for both WebVR and WebAR (switchable via
VRButton
orARButton
) - Cross-platform compatibility
- Support for both WebVR and WebAR (switchable via
- A WebXR-compatible browser
- For VR testing:
- A compatible VR headset, or
- WebXR Emulator Extension for desktop testing
- WebXR Viewer for iOS testing
- Clone the repository:
git clone https://github.com/usuario/basic-threejs-vr-scene.git
- Navigate to the project directory:
cd basic-threejs-vr-scene
-
Open in your browser:
- For desktop: Use Chrome with WebXR flags enabled
- For Android: Use Chrome
- For iOS: Use WebXR Viewer app
-
Click "Enter VR" or "Enter AR" to start the experience
The project uses the latest WebXR standards and provides compatibility across different platforms. For detailed setup instructions, refer to the documentation in the docs
folder.
Detailed information about the scene setup, including:
- Environment textures
- Object placement
- Lighting setup
- Performance optimizations
Implementation details for VR controllers, including:
- Controller model loading
- Event handling
- Interaction zones
- Haptic feedback
For AR implementation, the project supports:
- Device camera integration
- Surface detection
- Object placement
- Touch interactions
import { VRButton } from 'https://unpkg.com/[email protected]/examples/jsm/webxr/VRButton.js';
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.xr.enabled = true; // <-- Enable WebXR
document.body.appendChild(renderer.domElement);
document.body.appendChild(VRButton.createButton(renderer));
renderer.setAnimationLoop(render);
// Texture loader
let textureLoader = new THREE.TextureLoader();
// Sky
const textureSky = textureLoader.load('path/to/sky-texture.jpg');
const materialSky = new THREE.MeshBasicMaterial({
map: textureSky,
side: THREE.DoubleSide,
});
const sky = new THREE.Mesh(new THREE.SphereGeometry(30, 32, 32), materialSky);
scene.add(sky);
import { FBXLoader } from 'https://unpkg.com/[email protected]/examples/jsm/loaders/FBXLoader.js';
new FBXLoader().load('path/to/Tree(1).fbx', (object) => {
object.traverse((child) => {
object.scale.set(0.004, 0.004, 0.004);
createTree(object.clone(), [0, 0, -10]);
createTree(object.clone(), [5, 0, -3]);
// ...
});
});
function createTree(tree, position) {
tree.position.set(position[0], position[1], position[2]);
scene.add(tree);
}
import { XRControllerModelFactory } from 'https://unpkg.com/[email protected]/examples/jsm/webxr/XRControllerModelFactory.js';
// Controllers
const controllerModelFactory = new XRControllerModelFactory();
controllerL = renderer.xr.getControllerGrip(0);
controllerR = renderer.xr.getControllerGrip(1);
scene.add(controllerL);
scene.add(controllerR);
// Right Controller: Grab
let checkGrabbingR = false;
controllerR.addEventListener('selectstart', () => (checkGrabbingR = true));
controllerR.addEventListener('selectend', () => (checkGrabbingR = false));
function CheckObjects() {
// If within a certain radius, snap object to controllerR
objects.forEach((obj) => {
let dist = obj.position.distanceTo(controllerR.position);
if (dist <= controllerActionRadius) {
obj.position.copy(controllerR.position);
}
});
}
function render() {
if (checkGrabbingR) CheckObjects();
renderer.render(scene, camera);
}
controllerL.addEventListener('squeezestart', SelectEventRay);
function SelectEventRay() {
const intersections = getIntersections(controllerL);
intersections.forEach((intersection) => {
if (objects.includes(intersection.object)) {
intersection.object.position.copy(controllerL.position);
}
});
}
function getIntersections(controller) {
const tempMatrix = new THREE.Matrix4();
const raycaster = new THREE.Raycaster();
tempMatrix.identity().extractRotation(controller.matrixWorld);
raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
return raycaster.intersectObjects(scene.children);
}
For development, we recommend:
- Using the WebXR Emulator Extension for rapid testing
- Following the Three.js best practices
- Testing across different devices and browsers
- Using the provided debugging tools
For more details, see our Contributing Guidelines.
The project draws on the following WebXR documentation:
- WebXR Emulator Extension (to emulate VR/AR devices on a desktop):
MozillaReality/WebXR-emulator-extension - iOS App for WebXR (to run VR/AR experiences on Apple devices):
WebXR Viewer on AppStore
For a detailed explanation of the starter scene, VR/AR setup, environment textures, and controller events, you can refer to the documentation text provided in this repo (or the excerpt in the project's docs folder).
This project is licensed under the MIT License.