Clicking 3D Objects in A-Frame with the Raycaster
Use A-Frame's raycaster to make 3D entities respond to gaze and pointer clicks, with performance limits and verification steps.
29 Jul 2025, 02:34 UTC

Why a regular click listener misses 3D objects
In an A-Frame scene, a box or sphere is not a normal HTML element. It is a Three.js mesh drawn with WebGL, so adding an onclick attribute to its HTML tag does nothing. To make 3D content clickable, A-Frame uses raycasting: it projects an invisible line from an entity—usually the camera or a motion controller—into the scene and reports which meshes that line touches.
Set up gaze-based clicking
The raycaster component attaches to any entity. For desktop and mobile gaze input, pair it with the cursor component on the camera. The cursor shows a reticle and the raycaster checks what it points at.
Save this as an HTML file and open it in a browser. No build step is required.
<html>
<head>
<script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
<a-camera
position="0 1.6 0"
raycaster="objects: .clickable; far: 10"
cursor="rayOrigin: mouse">
</a-camera>
<a-box
class="clickable"
position="0 1 -5"
color="tomato"
width="1" height="1" depth="1">
</a-box>
<a-sky color="#ececec"></a-sky>
</a-scene>
<script>
const box = document.querySelector('.clickable');
box.addEventListener('click', (event) => {
console.log('Box clicked', event.detail.intersection);
});
box.addEventListener('mouseenter', () => {
box.setAttribute('color', 'orange');
});
box.addEventListener('mouseleave', () => {
box.setAttribute('color', 'tomato');
});
</script>
</body>
</html>
Where to run: any local file or static host. Required permissions: none beyond opening an HTML file. Placeholders: replace the CDN URL with a local A-Frame build if you prefer. Expected check: open the browser console, look at the box, and click or press the trigger; you should see Box clicked plus intersection details. Risk: if you omit objects, the ray tests every mesh in the scene, which can lower frame rate on standalone headsets.
Configure what the ray can hit
objects: a CSS selector string that limits intersection tests to matching entities. Use this to ignore floors, walls, or decorative geometry.far: maximum ray distance in meters. Set this to the farthest distance a user should be able to interact.interval: how often, in milliseconds, the raycaster checks for intersections. Raising it reduces CPU work but makes input feel slower.
A-Frame emits click, mouseenter, and mouseleave on the intersected A-Frame entity, not on the raw Three.js object. That means your event listener can read and modify A-Frame component state directly.
Gaze versus tracked controllers
Gaze input works without extra hardware, but it is imprecise: the user must look directly at a target, and small objects are hard to hit. A tracked controller emits its own ray and usually feels more accurate for fine interaction. The same raycaster component can attach to a controller entity instead of the camera, so the event listeners on your 3D objects do not need to change.
Limitations and how to verify
Raycasting only hits visible meshes. An invisible entity, a placeholder without geometry, or an entity behind another mesh will not receive a click. Complex geometry also costs more per frame because the engine must test more triangles.
Practical verification:
- Open browser developer tools and select the scene canvas.
- Confirm the target entity has
geometryandmaterialcomponents and is not set tovisible="false". - Click the entity and watch the console for your log message.
- If nothing fires, temporarily remove
objectsfrom the raycaster to see whether the selector is too narrow.
Component names and event behavior can differ between A-Frame major versions. Code written for 0.x may need updates for 1.x. Inspect src/components/raycaster.js in your installed A-Frame package to confirm property names and emitted events for your version.
Closing
Add raycaster to the camera, use cursor for gaze input, scope intersections with objects and far, and verify against visible meshes in the browser console. If you need precise input, consider tracked controllers instead of gaze; if you need performance, keep the intersection list small and the ray distance short.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.