# WebXR Demos and Examples

> Check out a series of WebXR demos and examples in Babylon.js.

Source: https://doc.babylonjs.com/features/featuresDeepDive/webXR/webXRDemos

## Basic scene

This is a step-by-step guide on how to add XR features to a basic scene.

## Basic Scene with XR Support

Here we just add an environment, a sphere, and XR support.

```javascript
const xrHelper = await scene.createDefaultXRExperienceAsync();
```

- [Playground: Basic Scene With WebXR Support](https://playground.babylonjs.com/#9K3MRA) - Simple example of a basic scene with WebXR support.

### Adding teleportation

To get teleportation enabled, we want to provide the experience helper with an array of floor meshes:

```javascript
const xrHelper = await scene.createDefaultXRExperienceAsync({
  // define floor meshes
  floorMeshes: [environment.ground],
});
```

- [Playground: Basic Scene With Teleportation](https://playground.babylonjs.com/#9K3MRA#1) - Simple example of a basic scene with teleportation enabled.

### Adding a color picker to the basic scene

Add a color picker (from our GUI library) and use it to change the sphere's color.

Notice that no changes were made in the XR code, and that the scene works perfectly well outside VR as well.

```javascript
// GUI
var plane = BABYLON.MeshBuilder.CreatePlane("plane", { size: 1 });
plane.position = new BABYLON.Vector3(1.4, 1.5, 0.4);
var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(plane);
var panel = new BABYLON.GUI.StackPanel();
advancedTexture.addControl(panel);
var header = new BABYLON.GUI.TextBlock();
header.text = "Color GUI";
header.height = "100px";
header.color = "white";
header.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
header.fontSize = "120";
panel.addControl(header);
var picker = new BABYLON.GUI.ColorPicker();
picker.value = sphere.material.diffuseColor;
picker.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
picker.height = "350px";
picker.width = "350px";
picker.onValueChangedObservable.add(function (value) {
  sphere.material.diffuseColor.copyFrom(value);
});
panel.addControl(picker);
```

- [Playground: WebXR Color Picker](https://playground.babylonjs.com/#9K3MRA#2) - Simple WebXR color picker example.

## AR demo
![playground](https://doc.babylonjs.com/img/how_to/xr/ARPortal.webp)
- [Playground: AR Portal](https://playground.babylonjs.com/#M3G924) - WebXR AR Portal

## Other demos

- [Playground: Goalkeeper Training](https://playground.babylonjs.com/#PPM311#148)
- [Playground: Legacy Physics Playground](https://playground.babylonjs.com/#B922X8#19) - Physics Playground

- [Playground: A cylinder object is child of a controller](https://playground.babylonjs.com/#F41V6N#139)

- [Playground: Simply grabbing objects by controllers](https://playground.babylonjs.com/#1FTUSC#37)

## Babylon.js scenes with XR support

- [Playground: Mansion](https://playground.babylonjs.com/#JA1ND3#161) - Mansion Demo
- [Playground: Hill Valley](https://playground.babylonjs.com/#TJIGQ1#3)
- [Playground: Espilit](https://playground.babylonjs.com/#ja1nd3#942)

## WebXR with Vite

Step-by-step project setup using Vite and Babylon.js ES6 modules for
WebXR development.

### Install Vite

```bash
## Setup vite
npm create vite@latest
##> Project name: babylon-webxr-test
##> Select a framework: vanilla
##> Select a variant: vanilla-ts
```

### Install @babylonjs ES6 packages

```bash
npm install @babylonjs/core
npm install @babylonjs/loaders
```

### Enable HTTPS dev server

HTTPS is required by most VR devices. Create or modify `vite.config.ts`:

```javascript
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    port: 3443,
    https: true,
    // Uncomment to allow access from network
    // (or use `npm run dev -- -- host=0.0.0.0`)
    //host: '0.0.0.0',
  },
});
```

### Set up a basic WebXR scene

Modify the `main.ts` file:

```typescript
import "./style.css";

import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera.js";
import { Color3 } from "@babylonjs/core/Maths/math.color.js";
import { Engine } from "@babylonjs/core/Engines/engine.js";
import { EnvironmentHelper } from "@babylonjs/core/Helpers/environmentHelper.js";
import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight.js";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder.js";
import { Scene } from "@babylonjs/core/scene.js";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial.js";
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
import { WebXRDefaultExperience } from "@babylonjs/core/XR/webXRDefaultExperience.js";

// Required for EnvironmentHelper
import "@babylonjs/core/Materials/Textures/Loaders";

// Enable GLTF/GLB loader for loading controller models from WebXR Input registry
import "@babylonjs/loaders/glTF";

// Without this next import, an error message like this occurs loading controller models:
//  Build of NodeMaterial failed" error when loading controller model
//  Uncaught (in promise) Build of NodeMaterial failed: input rgba from block
//  FragmentOutput[FragmentOutputBlock] is not connected and is not optional.
import "@babylonjs/core/Materials/Node/Blocks";

// Create a canvas element for rendering
const app = document.querySelector<HTMLDivElement>("#app");
const canvas = document.createElement("canvas");
app?.appendChild(canvas);

// Create engine and a scene
const babylonEngine = new Engine(canvas, true);
const scene = new Scene(babylonEngine);

// Add a basic light
new HemisphericLight("light1", new Vector3(0, 2, 0), scene);

// Create a default environment (skybox, ground mesh, etc)
const envHelper = new EnvironmentHelper(
  {
    skyboxSize: 30,
    groundColor: new Color3(0.5, 0.5, 0.5),
  },
  scene,
);

// Add a camera for the non-VR view in browser
const camera = new ArcRotateCamera("Camera", -(Math.PI / 4) * 3, Math.PI / 4, 10, new Vector3(0, 0, 0), scene);
camera.attachControl(true);

// Add a sphere to have something to look at
const sphereD = 1.0;
const sphere = MeshBuilder.CreateSphere("xSphere", { segments: 16, diameter: sphereD }, scene);
sphere.position.x = 0;
sphere.position.y = sphereD * 2;
sphere.position.z = 0;
const rMat = new StandardMaterial("matR", scene);
rMat.diffuseColor = new Color3(1.0, 0, 0);
sphere.material = rMat;

// Setup default WebXR experience
// Use the enviroment floor to enable teleportation
WebXRDefaultExperience.CreateAsync(scene, {
  floorMeshes: [envHelper?.ground as Mesh],
  optionalFeatures: true,
});

// Run render loop
babylonEngine.runRenderLoop(() => {
  scene.render();
});
```

Corresponding git repo: [kaliatech/babylon-docs-vite-webxr](https://github.com/kaliatech/babylon-docs-vite-webxr)

### Run server and verify

Start vite dev server and make it accessible from the network:

```bash
npm run dev -- --host 0.0.0.0
```

Browsing to `https://<your-server-ip>:3443` on a desktop machine should show the basic scene. If
the [WebXR API Emulator](https://github.com/MozillaReality/WebXR-emulator-extension) is enabled, you
should also see Babylon.js's default VR headset mode icon.

If viewed from within a headset, the controller models should correspond to what is available in the global
registry for your device.

To check TypeScript types and build:

```bash
npm run build
```

Using the setup above, Vite reports a vendor.js size of ~2.4MB (520k gzipped) as of babylon-5.0.0-beta.8.
