aframe icon indicating copy to clipboard operation
aframe copied to clipboard

Hand / Controller models disapear which switching modes.

Open KooIaIa opened this issue 5 months ago • 4 comments

This is happening in my site and another unrelated persons site so it appears to be a bug in latest A-frame.

Example: https://strata-design-demo.vercel.app/examples/1/view

Controls look like this when its working and you switch from hands to controllers: Working

The bug is that the hands or controllers disappear frequently when doing this switching:

Image Image

Notice how the laser attached to the hand is still there and tracking is happening. It appears to just be the visual logic hiding the hands or controllers that is breaking.

The problem resolves itself on Quest when going in and out of the Oculus menu.

KooIaIa avatar Sep 01 '25 01:09 KooIaIa

Up 😆

electrum-bowie avatar Oct 18 '25 21:10 electrum-bowie

Your site is down. Can you please create a simple test case with https://krabbel.dev so we see what components you're using and please tell us exactly what was the aframe version and components that was used when you saw that issue. I'm also having sometimes that issue with handy-controls and just hiding my hand behind my back and putting it back in front fixes the issue generally. I'm not sure if the issue is in the handy-controls component, in XRControllerModelFactory or threejs or a Meta browser issue with the disconnected/connected events, I don't currently have time to spend hours debugging that, that's really low on my priority list. My guess is that's probably a race condition with a quick succession of controller disconnected, hand connected, hand disconnected controller connected that toggles the visibility of the models.

vincentfretin avatar Oct 19 '25 09:10 vincentfretin

https://krabbel.dev/p/pretty-cougars-smash

I just did this minimal code on the default Krabbel sample.

  <a-entity id="rightController" oculus-touch-controls="hand: right"></a-entity>
  <a-entity id="leftController" oculus-touch-controls="hand: left"></a-entity>
  <a-entity id="rightHand" hand-tracking-controls="hand: right; modelStyle: mesh"></a-entity>
  <a-entity id="leftHand" hand-tracking-controls="hand: left; modelStyle: mesh"></a-entity>

I added cubes so you can see them when they turn invisible.

Image Image

I agree it seems like a racing condition.

I was not able to reproduce it with this Three.js example. https://threejs.org/examples/webxr_vr_handinput_profiles

The controllers motion is still correctly being tracked. On the Three example, the moment you move your hand a small distance they appear correctly no matter what even if it seems like the transition broke.

KooIaIa avatar Oct 19 '25 19:10 KooIaIa

I'm also having sometimes that issue with handy-controls and just hiding my hand behind my back and putting it back in front fixes the issue generally. I'm not sure if the issue is in the handy-controls component, in XRControllerModelFactory or threejs or a Meta browser issue with the disconnected/connected events, I don't currently have time to spend hours debugging that, that's really low on my priority list.

Unsure if that's the same bug, but the one we're having is critical.

Not having correct controller rendering is a huge problem for anyone using it...

electrum-bowie avatar Oct 20 '25 13:10 electrum-bowie

https://github.com/aframevr/aframe/commit/fa0cc7481653f955f7189c53f624854c80908eb7 changed a bit how to visibility is toggled but had a flaw. Claude Opus 4.5 found the issue in one shot

● Looking at the code, I can see a potential race condition. Let me check the checkControllerPresentAndSetup utility to understand the full picture.

● Read(src/utils/tracked-controls.js)
  ⎿  Read 140 lines

● I found the race condition. Here's the issue:

  The Bug

  In meta-touch-controls.js:185-192:

  checkIfControllerPresent: function () {
    var controllerObject3D = this.controllerObject3D;
    if (controllerObject3D) { controllerObject3D.visible = false; }  // <-- ALWAYS sets to false
    checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, {
      hand: this.data.hand,
      iterateControllerProfiles: true
    });
  },

  And in src/utils/tracked-controls.js:22-23:

  // Nothing changed, no need to do anything.
  if (isPresent === component.controllerPresent) { return isPresent; }

  The Race Condition

  1. Meta controller is connected → controllerPresent = true, model visible
  2. User switches to hand tracking, which fires controllersupdated
  3. meta-touch-controls.onControllersUpdate() calls checkIfControllerPresent()
  4. checkIfControllerPresent() unconditionally sets controllerObject3D.visible = false
  5. checkControllerPresentAndSetup() is called
  6. If the controller is still momentarily detected (during the transition), isPresent = true
  7. Since isPresent === controllerPresent (both true), early return at line 23
  8. injectTrackedControls() is never called → loadModel() is never called
  9. Model stays invisible!

  The same happens when switching back - if there's a moment where the previous state matches the new state (due to rapid updates or timing), the model gets stuck invisible.

  Why hand-tracking-controls doesn't have this issue

  Compare with hand-tracking-controls.js:157-163 - it does NOT set any model visibility before calling checkControllerPresentAndSetup():

  checkIfControllerPresent: function () {
    var data = this.data;
    var hand = data.hand ? data.hand : undefined;
    checkControllerPresentAndSetup(
      this, '',
      {hand: hand, iterateControllerProfiles: true, handTracking: true});
  },

  The Fix

  The model visibility should only be set to false when the controller is actually disconnected, not on every check. Here's a suggested fix for meta-touch-controls.js:

  checkIfControllerPresent: function () {
    var controllerObject3D = this.controllerObject3D;
    checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, {
      hand: this.data.hand,
      iterateControllerProfiles: true
    });
    // Set visibility based on actual presence after the check
    if (controllerObject3D) {
      controllerObject3D.visible = this.controllerPresent;
    }
  },

  This ensures the model visibility reflects the actual controller state after the check completes, rather than unconditionally hiding it before.

I confirm that fixed the issue. Fixed by #5777

vincentfretin avatar Dec 15 '25 10:12 vincentfretin

I also fixed similar issues I was seeing with handy-controls https://github.com/AdaRoseCannon/handy-work/releases/tag/3.1.13

vincentfretin avatar Dec 15 '25 14:12 vincentfretin