WebXR controllers without using online repository

I’m currently working on an app that could be used to visualize proprietary molecular structures (e.g., drugs in development). I’d like to minimize the number of calls to any remote server so as to not concern my users. I’d also like my app to work in off-line mode if possible.

By default, WebXR downloads models of the WebXR controllers from a github repo (WebXR Input and Controller support - Babylon.js Documentation). I’d like to not depend on that repo. So I use BABYLON.WebXRMotionControllerManager.PrioritizeOnlineRepository = false;

Here’s an example: https://www.babylonjs-playground.com/#L88WNE#1

I’ve had success using this approach on an HTC Vive and Oculus Quest. But when I try to use it on an Oculus Go, the controller doesn’t load, and I get this error:

When I comment out BABYLON.WebXRMotionControllerManager.PrioritizeOnlineRepository = false; it works fine (see, for example, https://www.babylonjs-playground.com/#L88WNE#2).

I understand that the Oculus Go controller is not included in the local controller definitions that ship with babylonjs: WebXR Input and Controller support - Babylon.js Documentation

But “Generic-Button controller” is on that list, so I’m surprised it doesn’t default to that when it can’t find the correct controller.

It would be even better if I could include the relevant files from the github repository (GitHub - immersive-web/webxr-input-profiles: WebXR Gamepad assets, source library, and schema) with my app so I could use the many controllers listed there without having to ping their server.

Does anyone have any solutions for this kind of problem? Thanks for your help!

Hi all. I was able to answer at least part of my own question, thanks to this previous answer: Issues with current state of WebRequest.CustomRequestHeaders / CustomRequestModifiers

To avoid having to use the remote github repo to download controller models, you can get the relevant files using this bash script:

# Remove pevious version of the git repo.
rm -rf webxr-input-profiles
rm -rf profiles

# A pretty large git repo. May take a bit.
git clone https://github.com/immersive-web/webxr-input-profiles.git

# Change to the appropriate directory.
cd webxr-input-profiles/packages/assets/

# The models and specificiations are in ./profiles/, but the profilesList.json
# file is missing (presumably created on build). I struggled to build this
# part of the repo, but you can just downloaded the profilesList.json file
# directly from their online app to get around it.
curl https://immersive-web.github.io/webxr-input-profiles/packages/viewer/dist/profiles/profilesList.json > profiles/profilesList.json

# The profile.json files are also incomplete. Get each of those from the
# remote server as well.
find profiles -name "profile.json" | awk '{print "curl https://immersive-web.github.io/webxr-input-profiles/packages/viewer/dist/" $1 " > " $1}' | bash

# Move your profiles directory elsewhere.
mv profiles ../../../

# Clean up
cd ../../../
rm -rf webxr-input-profiles

You can then point to the new profiles directory on your own server like this:

BABYLON.WebXRMotionControllerManager.BaseRepositoryUrl = "path/to/directory/containing/profiles/directory/";

Note that the contents of the profiles directory take up about 68M.

One other trick in case it’s helpful. The controllers are rendered using PBR materials. If your scene doesn’t have the appropriate lighting, they will look totally black. See Start with Physically Based Rendering (PBR) - Babylon.js Documentation

You can switch all the materials to StandardMaterials like this (TypeScript):

scene.createDefaultXRExperienceAsync(params).then((vrHelper: any) => {
    vrHelper.input.onControllerAddedObservable.add((inputSource: any) => {
        inputSource.onMeshLoadedObservable.add((controllerMesh: any) => {
            // Switch all materials on controllers to be standard materials.
            let meshes = [controllerMesh];
            let meshIdx = 0;
            while (meshIdx < meshes.length) {
                let mesh = meshes[meshIdx];
                if (mesh.material && mesh.material.albedoTexture) {
                    const newMat = new BABYLON.StandardMaterial(
                        mesh.name + "Material",

                    newMat.diffuseTexture = mesh.material.albedoTexture;

                    mesh.material = newMat;

                meshes = meshes.concat(mesh.getChildren());

In terms of not defaulting to “Generic-Button controller” when BABYLON.WebXRMotionControllerManager.PrioritizeOnlineRepository = false, I think that might be a legitimate bug.

Hope this helps someone.

hey @Jacob_Durrant,

Thanks for following up like that!

A few things that might help (you, me and everybody)

  1. Regarding fallbacks - This is interesting that it didn’t fall back to the generic controllers. Better yet - the oculus go controllers are actually present there, and should have been displayed. I would love to follow up on that and understand what went wrong, because so far i haven’t experienced a problem with declared controllers falsely loading.
  2. Even if fallbacks don’t want, or if you want to define your own fallbacks, you can use the RegisterFallbacksForProfileId function of the motion controller manager:
WebXRMotionControllerManager.RegisterFallbacksForProfileId("oculus-go", ["generic-trigger-touchpad"]);

Which is actually registered already. you can have it fallback to any other profile if you want. Look at the code documentation of GetMotionControllerWithXRInput to understand how the profiles are evaluated.

Now that I think about it, this line might be the reason behind the bug you are seeing. Try emptying the _Fallbacks array (which is private, i know…) and see if it helps.

  1. If you don’t mind, I will add a similar PBR-To-Standard optional conversion on the motion controller class itself. Still need to see what the best course of action is, but this is a great suggestion.

  2. Was wondering if you came across this doc page - WebXR Input and Controller support - Babylon.js Documentation and whether or not it was helpful. I guess there are many more things to write in it, just wondering if it makes sense as a getting-started-doc.