webxr icon indicating copy to clipboard operation
webxr copied to clipboard

Confusion around actual and internal visiblity

Open cabanier opened this issue 1 year ago • 5 comments

An external developer asked us to send better visibility events. They want to pause video as soon as the user takes off the headset but currently, the visibility event doesn't happen until the headset sits idle for a short while.

This is because we tie the WebXR session visibility to the visibility state of the internal OpenXR session and not to if the headset is on the user's head.. The WebXR and OpenXR specs both state that visibility is tied to if the user can see the scene, but that is not the case for Quest. In addition, Quest can still report poses even if the display is turned off but the session is still active.

The WebXR spec states: A state of hidden indicates that imagery rendered by the XRSession cannot be seen by the user. requestAnimationFrame() callbacks will not be processed until the visibility state changes. Input is not processed by the XRSession.

so if I tie visibility to the mounted state of the headset, that would violate the spec (because we keep firing raf calls). I also worry that changing this will also break some sites.

Alternatively, I can add a boolean on the session that states if the headset is currently worn and update the normative text that says that visible or visible-blurred frames may not be shown to the user (which is Quest's current behavior). Thoughts, @AdaRoseCannon or @toji

cabanier avatar Oct 23 '24 18:10 cabanier

My gut says that the headset not being mounted feels like an appropriate signal to switch to the hidden visibility state. XRSession RaFs and input could be suppressed if needed to ensure spec compat. I'd be interesting in hearing under what conditions you feel like that would break content.

Separate from that, I think any language which explicitly states "shown to the user" might be worth reconsidering the phrasing for. Perhaps something like "displayed by the device" is more appropriate, since it doesn't imply that the user is actually seeing the content. (Mostly I don't want to give anyone ideas to get too clever and start firing "hidden" state changes because their fancy eye-tracking device could tell that the user has their eyes closed...)

toji avatar Oct 23 '24 20:10 toji

My gut says that the headset not being mounted feels like an appropriate signal to switch to the hidden visibility state. XRSession RaFs and input could be suppressed if needed to ensure spec compat. I'd be interesting in hearing under what conditions you feel like that would break content.

If the browser stops submitting frames when it's not mounted, the openxr machinery won't transition the openxr session to visible if the user puts the headset back. I guess I could work around that on the browser side but all the assumptions in the blink code about not submitting frames when invisible would have to be deleted or updated.

If the site makes an early return in its raf call (if !visible-> return) and doesn't submit frames, the site will never transition to visible since that is still tied to the openxr state. I don't believe I can fix that.

Separate from that, I think any language which explicitly states "shown to the user" might be worth reconsidering the phrasing for. Perhaps something like "displayed by the device" is more appropriate, since it doesn't imply that the user is actually seeing the content.

Yes, that would be a good change.

cabanier avatar Oct 23 '24 20:10 cabanier

/agenda do we need an event for headset donning/doffing?

cabanier avatar Oct 26 '24 14:10 cabanier

The distinction between “user-visible” vs. “device-active” states is becoming increasingly relevant, especially as more XR platforms blur the lines between display state and head presence (e.g., passthrough AR modes, sensor-driven transitions, etc.).

If the site makes an early return in its raf call (if !visible -> return) and doesn't submit frames, the site will never transition to visible… This seems like a subtle but critical edge case. Perhaps an interim solution could be:

Continue allowing requestAnimationFrame even when headset isn’t mounted, but Add a new XRSession.headsetMounted boolean (or similar) and a dedicated headsetchange event That way, sites can choose to pause content (like video) more responsively without breaking the assumptions around visibility state that tie into session lifecycle and OpenXR.

Also +1 to the language clarification , “displayed by the device” seems much safer and implementation-neutral than “shown to the user.”

Curious to hear how others have worked around these transitions in WebXR apps. This might be a good case for a cross-vendor best practice doc or spec note, even ahead of any formal changes.

HussainAther avatar Jun 02 '25 00:06 HussainAther

The distinction between “user-visible” vs. “device-active” states is becoming increasingly relevant, especially as more XR platforms blur the lines between display state and head presence (e.g., passthrough AR modes, sensor-driven transitions, etc.).

If the site makes an early return in its raf call (if !visible -> return) and doesn't submit frames, the site will never transition to visible… This seems like a subtle but critical edge case.

the transition would still happen but then the first frame would be slower because usually the first one has extra processing (ie creating texture, compiling shaders) so the experience would look janky

Curious to hear how others have worked around these transitions in WebXR apps. This might be a good case for a cross-vendor best practice doc or spec note, even ahead of any formal changes.

I believe the Quest browser lies and reports that the experience is visible even tough it isn't. The same happens when the headset is taken off (except on Quest 3s which has no proximity sensor). My request was to give the developer an indication that the headset was unmounted but the experience was still required to produce frames; mostly to pause gameplay or media playback.

cabanier avatar Jun 02 '25 14:06 cabanier