aframe: Raycaster does not ignore `material="visible: false;"` when `raycaster="objects: ... ;"` is defined


According to the A-Frame docs (, raycasters should ignore entities that have material="visible: false;". This doesn’t seem to be the case when raycaster="objects: ... ;" is defined. In this case, all qualifying objects are raycasted regardless of visibility.

Should material.visible not still affect raycasting on these entities?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 16 (13 by maintainers)

Most upvoted comments

I hit this issue recently and found the following three.js change log item from r72:

This behavior matches what @dsinni describes and seems like a conscious design decision on three.js’s part that’s been around for a couple years.

The “Raycasters will ignore invisible materials.” line in the documentation has been there since 0.3.0, which used r76.1. But I was able to reproduce the raycaster intersecting an object with material="visible: false;" in 0.3.0 (gaze to the right of the yellow box):

    <script src=""></script>
        // Component to change to random color on click.
        AFRAME.registerComponent('cursor-listener', {
            init: function () {
                var COLORS = ['red', 'green', 'blue'];
                this.el.addEventListener('click', function (e) {
                    var randomIndex = Math.floor(Math.random() * COLORS.length);
                    this.setAttribute('material', 'color', COLORS[randomIndex]);
                    this.setAttribute('material', 'visible', true);
                    console.log('I was clicked!', e);
    <a-entity camera="userHeight: 1.6" look-controls wasd-controls>
        <a-entity raycaster="objects: .clickable" cursor="fuse: true" position="0 0 -1" scale="0.05 0.05 0.05" geometry="primitive: ring" ></a-entity>
    <a-entity material="visible: false;" class="clickable" cursor-listener geometry="primitive: box" position="1 0 -3"></a-entity>
    <a-entity material="color: yellow;"  class="clickable" cursor-listener geometry="primitive: box" position="0 0 -3"></a-entity>
    <a-sky color="#ECECEC"></a-sky>

Not anymore without hacking the component, it’s best practice and prescribed to control what is being raycasted. What I propose wasn’t showing off (I’m not a wizard), it is simple. Rather than depending on what is somewhat an arbitrary condition (visibility) and doing object3D.visible = false, do either like <a-entity data-no-raycast> or <a-entity data-raycastable> with objects: [data-raycastable] or objects: :not([data-no-raycast].

If you are depending solely on visibility, it sounds like the app has no system in place to toggle what is being raycasted, and your app is raycasting every single object, which is a killer for performance.

would it still not make the most sense for the raycaster to ignore an entity with visible=“false” regardless of whether or not it qualifies as a raycaster object?

I often have hidden raycaster targets to pad the click range (e.g., small targets). Often, you still want to trigger with invisible objects. I even have a raycaster-target helper component that drops a hidden raycaster target by width/height.

If it’s desired for the entity to not be visible while also being raycastable, material=“visible: false;” can still be used. This seems cleaner to me than having to add/remove/replace query selectors.

Regardless, you want to use query selectors, else everything will be raycastable. Some things I do is I create a raycastable component that can be mixed in. Or data-raycastable with a helper component that toggles them on / off based on like what menu you’re on. Or with the state component, bind-toggle__raycastable. I always limit exactly to what should be raycastable because it’s expensive.

Explicit > implicit in this case, especially as it involves performance and visibility of objects is not always a determining factor.

But yeah, docs should be updated with best practices. Been building complex menus and have mastered the art of the raycaster.