--- name: aframe-webxr description: Declarative web framework for building browser-based 3D, VR, and AR experiences using HTML and entity-component architecture. Use this skill when creating WebXR applications, VR experiences, AR experiences, 360-degree media viewers, or immersive web content with minimal JavaScript. Triggers on tasks involving A-Frame, WebXR, VR development, AR development, entity-component-system, declarative 3D, or HTML-based 3D scenes. Built on Three.js with accessible HTML-first approach. --- # A-Frame WebXR Skill ## When to Use This Skill - Build VR/AR experiences with minimal JavaScript - Create cross-platform WebXR applications (desktop, mobile, headset) - Prototype 3D scenes quickly with HTML primitives - Implement VR controller interactions - Add 3D content to web pages declaratively - Build 360° image/video experiences - Develop AR experiences with hit testing ## Core Concepts ### 1. Entity-Component-System (ECS) A-Frame uses an entity-component-system architecture where: - **Entities** are containers (like `
` in HTML) - **Components** add functionality/appearance to entities - **Systems** provide global functionality ```html ``` **Primitives** are shortcuts for common entity + component combinations: ```html ``` ### 2. Scene Setup Every A-Frame app starts with ``: ```html ``` The scene automatically injects: - Default camera (position: `0 1.6 0`) - Look controls (mouse drag) - WASD controls (keyboard movement) ### 3. Camera Systems **Default Camera** (auto-injected if none specified): ```html ``` **Custom Camera**: ```html ``` **Camera Rig** (for independent movement and rotation): ```html ``` **VR Camera Rig with Controllers**: ```html ``` ### 4. Lighting **Ambient Light** (global illumination): ```html ``` **Directional Light** (like sunlight): ```html ``` **Point Light** (radiates in all directions): ```html ``` **Spot Light** (cone-shaped beam): ```html ``` ### 5. Materials and Textures **Standard Material**: ```html ``` **Textured Material**: ```html ``` **Flat Shading** (no lighting): ```html ``` ### 6. Animations **Property Animation**: ```html ``` **Multiple Animations** (use `animation__*` naming): ```html ``` **Event-Based Animation**: ```html ``` ### 7. Assets Management Preload assets for better performance: ```html ``` ### 8. Custom Components Register custom components to encapsulate logic: ```javascript AFRAME.registerComponent('rotate-on-click', { // Component schema (configuration) schema: { speed: {type: 'number', default: 1} }, // Lifecycle: called once when component attached init: function() { this.el.addEventListener('click', () => { this.rotating = !this.rotating; }); }, // Lifecycle: called every frame tick: function(time, timeDelta) { if (this.rotating) { var rotation = this.el.getAttribute('rotation'); rotation.y += this.data.speed; this.el.setAttribute('rotation', rotation); } } }); ``` ```html ``` ## Common Patterns ### Pattern 1: VR Controller Interactions **Problem**: Enable object grabbing and manipulation in VR **Solution**: Use hand-controls and custom grab component ```html ``` ### Pattern 2: 360° Image Gallery **Problem**: Create an interactive 360° photo viewer **Solution**: Use sky primitive and clickable thumbnails ```html ``` ### Pattern 3: AR Hit Testing (Place Objects in Real World) **Problem**: Place virtual objects on detected real-world surfaces **Solution**: Use ar-hit-test component ```html

Tap to enter AR mode

``` ### Pattern 4: Mouse/Gaze Interactions **Problem**: Enable click interactions with desktop mouse or VR gaze **Solution**: Use cursor component and raycaster ```html ``` ### Pattern 5: Dynamic Scene Generation **Problem**: Programmatically create and manipulate entities **Solution**: Use JavaScript DOM manipulation ```html ``` ### Pattern 6: Environment and Skybox **Problem**: Create immersive environments quickly **Solution**: Use community components and 360 images ```html ``` ### Pattern 7: GLTF Model Loading **Problem**: Load and display 3D models **Solution**: Use gltf-model component with asset management ```html ``` ## Integration Patterns ### With Three.js Access underlying Three.js objects: ```javascript // Get Three.js scene const scene = document.querySelector('a-scene').object3D; // Get entity's Three.js object const box = document.querySelector('a-box'); const threeObject = box.object3D; // Direct Three.js manipulation threeObject.position.set(1, 2, 3); threeObject.rotation.y = Math.PI / 4; // Add custom Three.js objects const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshStandardMaterial({ color: 0xff0000 }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); ``` ### With GSAP (Animation) Animate A-Frame entities with GSAP: ```html ``` ### With React Integrate A-Frame in React components: ```jsx import React, { useEffect, useRef } from 'react'; import 'aframe'; function VRScene() { const sceneRef = useRef(null); useEffect(() => { const scene = sceneRef.current; // Create entities dynamically const entity = document.createElement('a-sphere'); entity.setAttribute('position', '0 1.5 -3'); entity.setAttribute('color', '#EF2D5E'); scene.appendChild(entity); // Listen to events scene.addEventListener('enter-vr', () => { console.log('Entered VR mode'); }); }, []); return ( ); } export default VRScene; ``` ## Performance Best Practices ### 1. Use Asset Management Preload assets to avoid blocking: ```html ``` ### 2. Pool Entities Reuse entities instead of creating/destroying: ```javascript AFRAME.registerComponent('bullet-pool', { init: function() { this.pool = []; this.used = []; // Pre-create bullets for (let i = 0; i < 20; i++) { const bullet = document.createElement('a-sphere'); bullet.setAttribute('radius', 0.1); bullet.setAttribute('visible', false); this.el.sceneEl.appendChild(bullet); this.pool.push(bullet); } }, getBullet: function() { if (this.pool.length > 0) { const bullet = this.pool.pop(); bullet.setAttribute('visible', true); this.used.push(bullet); return bullet; } }, returnBullet: function(bullet) { bullet.setAttribute('visible', false); const index = this.used.indexOf(bullet); if (index > -1) { this.used.splice(index, 1); this.pool.push(bullet); } } }); ``` ### 3. Optimize Geometry Use low-poly models and LOD: ```html ``` ### 4. Limit Draw Calls Use instancing for repeated objects: ```javascript AFRAME.registerComponent('instanced-trees', { init: function() { // Use Three.js InstancedMesh for repeated geometry const scene = this.el.sceneEl.object3D; const geometry = new THREE.ConeGeometry(0.5, 2, 8); const material = new THREE.MeshStandardMaterial({ color: 0x228B22 }); const mesh = new THREE.InstancedMesh(geometry, material, 100); // Position instances for (let i = 0; i < 100; i++) { const matrix = new THREE.Matrix4(); matrix.setPosition( Math.random() * 20 - 10, 0, Math.random() * 20 - 10 ); mesh.setMatrixAt(i, matrix); } scene.add(mesh); } }); ``` ### 5. Throttle tick() Functions Don't update every frame if unnecessary: ```javascript AFRAME.registerComponent('throttled-update', { init: function() { this.lastUpdate = 0; this.updateInterval = 100; // ms }, tick: function(time, timeDelta) { if (time - this.lastUpdate >= this.updateInterval) { // Expensive operation here this.lastUpdate = time; } } }); ``` ### 6. Use Stats Component for Monitoring ```html ``` ## Common Pitfalls and Solutions ### Pitfall 1: Entities Not Appearing **Problem**: Entity added but not visible **Causes**: - Entity positioned behind camera - Scale is 0 or very small - Material opacity is 0 - Entity outside camera frustum **Solution**: ```javascript // Wait for scene to load const scene = document.querySelector('a-scene'); scene.addEventListener('loaded', () => { const entity = document.createElement('a-box'); entity.setAttribute('position', '0 1.5 -3'); // In front of camera entity.setAttribute('color', 'red'); scene.appendChild(entity); }); // Debug: Check entity position console.log(entity.getAttribute('position')); // Debug: Check if entity is in scene console.log(entity.parentNode); // Should be ``` ### Pitfall 2: Events Not Firing **Problem**: Click/mouseenter events don't trigger **Cause**: Missing raycaster or cursor **Solution**: ```html ``` ### Pitfall 3: Performance Degradation **Problem**: Low FPS with many entities **Causes**: - Too many draw calls - Complex geometries - Unoptimized textures - Too many tick() updates **Solutions**: ```javascript // 1. Use object pooling (see Performance section) // 2. Simplify geometry // 3. Optimize textures (reduce size, use compression) // 4. Throttle updates AFRAME.registerComponent('optimize-far-entities', { tick: function() { const camera = this.el.sceneEl.camera; const entities = document.querySelectorAll('[geometry]'); entities.forEach(el => { const distance = el.object3D.position.distanceTo(camera.position); // Hide distant entities el.object3D.visible = distance < 50; }); } }); ``` ### Pitfall 4: Z-Fighting (Overlapping Surfaces) **Problem**: Flickering when surfaces overlap **Cause**: Two surfaces at same position **Solution**: ```html ``` ### Pitfall 5: Mobile VR Performance **Problem**: Low performance on mobile VR **Solutions**: ```html ``` ### Pitfall 6: Asset Loading Issues **Problem**: Assets not loading or CORS errors **Solutions**: ```html ``` ## Resources - [A-Frame Documentation](https://aframe.io/docs/) - [A-Frame GitHub](https://github.com/aframevr/aframe) - [A-Frame School](https://aframe.io/school/) - [A-Frame Community Components](https://github.com/c-frame) - [WebXR Device API](https://www.w3.org/TR/webxr/) - [Three.js Documentation](https://threejs.org/docs/) (A-Frame built on Three.js) ## Related Skills - **threejs-webgl**: For advanced Three.js control beyond A-Frame's declarative API - **babylonjs-engine**: Alternative 3D engine with different architecture - **gsap-scrolltrigger**: For animating A-Frame entities with GSAP - **react-three-fiber**: React approach to Three.js (compare with A-Frame's HTML approach)