How do you switch out the default WebXR experience controller mesh?

I am testing on the Oculus Quest. Following the getting started playground example using the default webXR experience, I can see a sphere in VR! Looking down at my controllers in VR, they are rendering smaller controllers for some other headset. Is there a way I can switch them to the Oculus quest controller looking mesh? Or my own custom mesh? How do I go about doing that? Thanks.

Ping our friend @RaananW master of VR :slight_smile: He ll definitely be able to help.

Sure! you can do whatever you want :slight_smile:

Basic documentation is here - WebXR Input and Controller support - Babylon.js Documentation

If you want to force a profile, you can pass it to the input options when initializing either the default webxr experience, or the xr input source (if you are creating it yourself). Code-doc is here -

Thanks for the code reference!

I’m trying this:

// here we add XR support
const xr = await this._scene.createDefaultXRExperienceAsync({
floorMeshes: [env?.ground as BABYLON.AbstractMesh],
inputOptions: {
forceInputProfile: ‘oculus-touch-v2’

But the hand controllers still show up as the default ones. Am I missing something?

Is this a project you build youself (webpack etc’)?
When you say the default ones, do you mean the platform’s, or babylon’s generics?

Yes, I’m building the project myself with webpack. By “default” I merely meant that in the Oculus Quest when I look down at my controllers I see little gray controllers that I don’t recognize (I assumed those were some default webxr controller), vs meshes that resemble those of an oculus quest controller. I do not know what you mean by “platform’s, or babylon’s generics”.

You are probably missing the babylon-loaders package. check the log, you will probably see a warning about it.

The models require the GLTF loader

1 Like

I have these dependencies in my package.json

@babylonjs/loaders”: “^4.1.0”,
“babylonjs”: “^4.1.0”

do I have to load them in my code in some way? I didn’t see any warnings in my browser console.log output, but I can’t simulate webxr in my browser, I need to use the headset, and in my headset I can’t inspect the logs.

Those two sadly don’t mix. If you want to use the bablonjs package (and not @babylonjs/core) you will need the babylonjs-loaders package, and you will need to include it in your code. Here is the docs:

For es6 - ES6 - Babylon.js Documentation
For umd - NPM - Babylon.js Documentation and babylonjs-loaders - npm

1 Like

Oh… I see that there are so many ways to do things in javascript land, which I’m no expert.

I’ve finally got a basic webXR experience working locally (THANKS!) using the es6 libraries and the following:

    import {
    } from "@babylonjs/core/Engines/engine";
    import {
    } from "@babylonjs/core/scene";
    import {
    } from "@babylonjs/core/Maths/math";
    import {
    } from "@babylonjs/core/Cameras/freeCamera";
    import {
    } from "@babylonjs/core/Lights/hemisphericLight";
    import {
    } from "@babylonjs/core/Lights/light";
    import {
    } from "@babylonjs/core/Meshes/mesh";
    import {
    } from "@babylonjs/core/Meshes/abstractMesh";
    import {
    } from "@babylonjs/core/XR/webXRDefaultExperience";

    //import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader";
    import "@babylonjs/loaders"

    // Required side effects to populate the Create methods on the mesh class. Without this, the bundle would be smaller but the createXXX methods from mesh would not be accessible.
    // import "@babylonjs/core/Meshes/meshBuilder";
    import "@babylonjs/core/Helpers/sceneHelpers";

    export class Game {
      private _canvas: HTMLCanvasElement;
      private _engine: Engine;
      public _scene?: Scene;
      private _camera?: FreeCamera;
      private _light?: Light;
      private _xrHelper?: WebXRDefaultExperience

      constructor(canvasElement: string) {
        // Create canvas and engine.
        this._canvas = document.getElementById(canvasElement) as HTMLCanvasElement;
        this._engine = new Engine(this._canvas, true);

      async createScene() {
        this._scene = new Scene(this._engine);
        this._camera = new FreeCamera("camera1", new Vector3(0, 5, -10), this._scene);
        this._camera.attachControl(this._canvas, true);
        this._light = new HemisphericLight("light1", new Vector3(0, 1, 0), this._scene);
        this._light.intensity = 0.3;
        var sphere = Mesh.CreateSphere("sphere1", 16, 2, this._scene);
        sphere.position.y = 1;

        const env = this._scene.createDefaultEnvironment();

        // here we add XR support
        this._xrHelper = await this._scene.createDefaultXRExperienceAsync({
          floorMeshes: [env?.ground as AbstractMesh],
          inputOptions: {
            forceInputProfile: 'oculus-touch-v2'


        return this._scene;


      doRender(): void {
        // Run the render loop.
        this._engine.runRenderLoop(() => {

        // The canvas/window resize event handler.
        window.addEventListener('resize', () => {

While on the subject: the bundled js for the above takes a surprisingly long time to download. Are the above dependencies imported as optimally to get to fast loading times and small bundle size?

Do you mean compile, or really the package download time? Compile should not take too long, not should the download of the package. But we don’t control both :blush:

I’m happy you managed to get it to work!

(this is a new subject so maybe I’ll ask in a new thread), but to answer your question: the webpack compiling time is a few seconds, not too bad. The bundled asset is 1.7 mb, and I think my code is already using minimal references to things it needs and thus we can use “tree shaking” right (1.7 is that typical for a small example like this)? Gzipped this would be 390kb, which is still large but acceptable.

If all your imports are above 1.7 sounds a bit large indeed. Could you share all the imports you have in your code ?

Those are all the imports. Here is the repo babylon_phoenix/assets at v0.2 · homanchou/babylon_phoenix · GitHub

(I’m using phoenix/elixir framework.)

webpack gives some good output, looks like scene helpers and scene loaders is quite big:

    webpack --mode production

    Hash: eea71a7286f30d7d04c6
    Version: webpack 4.41.5
    Time: 27950ms
    Built at: 06/27/2020 8:16:24 PM
                    Asset       Size  Chunks                    Chunk Names
          ../css/app.css   9.67 KiB       0  [emitted]         app
          ../favicon.ico   1.23 KiB          [emitted]
    ../images/phoenix.png   13.6 KiB          [emitted]
            ../robots.txt  202 bytes          [emitted]
                  app.js   1.62 MiB       0  [emitted]  [big]  app
      app.js.LICENSE.txt  808 bytes          [emitted]
    Entrypoint app [big] = ../css/app.css app.js
      [9] ./node_modules/@babylonjs/core/Engines/engine.js + 2 modules 86.6 KiB {0} [built]
          |    3 modules
    [10] ./node_modules/@babylonjs/core/Misc/tools.js + 1 modules 58.5 KiB {0} [built]
          |    2 modules
    [14] ./node_modules/@babylonjs/core/Meshes/mesh.js + 1 modules 177 KiB {0} [built]
          |    2 modules
    [21] ./node_modules/@babylonjs/core/scene.js + 5 modules 214 KiB {0} [built]
          |    6 modules
    [25] ./node_modules/@babylonjs/core/Engines/thinEngine.js + 5 modules 186 KiB {0} [built]
          |    6 modules
    [131] multi ./js/app.js 28 bytes {0} [built]
    [132] ./js/app.js 1.9 KiB {0} [built]
    [133] ./css/app.scss 39 bytes {0} [built]
    [134] ./js/socket.js 2.3 KiB {0} [built]
    [135] ../deps/phoenix/priv/static/phoenix.js 35.6 KiB {0} [built]
    [136] ../deps/phoenix_html/priv/static/phoenix_html.js 2.21 KiB {0} [built]
    [137] ./js/game.ts 5.75 KiB {0} [built]
    [138] ./node_modules/@babylonjs/core/Helpers/sceneHelpers.js + 93 modules 961 KiB {0} [built]
          |    94 modules
    [139] ./node_modules/@babylonjs/loaders/index.js + 53 modules 625 KiB {0} [built]
          |    54 modules
    [140] ./node_modules/@babylonjs/core/Maths/math.js + 1 modules 2.73 KiB {0} [built]
          |    2 modules
        + 128 hidden modules

    WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
    This can impact web performance.
      app.js (1.62 MiB)