8th Wall – Proyectos Inmersys
Fecha de creación del post: 18 de Noviembre de 2025
Autor: Abraham García
Guía Técnica
Introducción
En Inmersys, nuestras experiencias con 8th Wall están diseñadas para ofrecer contenidos 2D previos a experiencias 3D inmersivas, principalmente utilizados para:
- Aviso de privacidad
- Instrucciones
- Branding previo
- Pantallas iniciales / loading screens
- Información contextual del proyecto
Estas pantallas 2D funcionan como la capa previa antes de mostrar el entorno 3D completo de AR. Su objetivo es reforzar el branding, preparar al usuario y permitir cargas progresivas antes de mostrar los modelos o interacciones principales.
Estructura base de un proyecto
Un proyecto mínimo en 8th Wall + Inmersys está compuesto por tres archivos principales:
1. head.html
Contiene:
- Scripts principales de 8th Wall
- Scripts de A-Frame
- Scripts personalizados
- Configuración global (metas, estilos, libraries externas, etc.)
Ejemplos de scripts comunes:
- xrextras.js
- aframe.min.js
- three.js
- Assets loaders (GLTF, HDRI, etc.)
2. app.js
Es el archivo JavaScript principal, donde:
- Se registran componentes personalizados de A-Frame
- Se controlan eventos, lógica de interacción y flujos dinámicos
- Se crean entidades o se manipulan elementos del DOM
- Se contienen funciones comunes del proyecto (loaders, audio, UX, etc.)
3. body.html
Contiene:
- Estructura HTML del proyecto
- Elementos visuales (pantallas 2D)
- Escena de A-Frame (
<a-scene>) - Componentes de XRExtras
- Canvas principal
- Elementos UI/UX del flujo
Tecnologías utilizadas en los proyectos Inmersys
Frontend
- React.js (cuando el proyecto lo requiere)
- HTML5
- JavaScript Vanilla
- CSS / Tailwind (opcional)
3D / XR
- A-Frame
- Three.js
- 8th Wall XR Extras
- GLTF / GLB models
Creación de componentes personalizados (A-Frame)
Un componente de A-Frame encapsula la lógica necesaria para una funcionalidad concreta.
En 8th Wall, estos componentes se agregan dentro del archivo app.js y se importan desde la carpeta /files.
Estructura típica de un componente
export const audioComponent = {
init() {
// Código ejecutado una sola vez cuando inicia el componente
},
tick() {
// Código ejecutado en cada frame/render del componente
},
};
Registro del componente
import { audioComponent } from './audio'
AFRAME.registerComponent('audio', audioComponent)
**Uso del componente**
En el HTML / A-Frame:
`<a-entity audio>`
<a-entity audio></a-entity>
Creación de elementos dinámicos
Existen dos formas de agregar elementos en tiempo real:
1. Creación por HTML
Simplemente se coloca en el body.html:
<a-entity><a-image><a-gltf-model><a-text>- Etc.
8th Wall interpreta este HTML automáticamente.
2. Creación por JavaScript
Se recomienda cuando:
- El objeto depende de interacción del usuario
- Los elementos requieren coordenadas dinámicas
- Se desean spawnear entidades bajo condiciones específicas
- Se agregan o remueven elementos durante runtime
Ejemplo real (creación de modelo)
createHen() {
const hen = document.createElement('a-entity')
hen.setAttribute('id', 'hen')
hen.setAttribute('gltf-model', '#hen')
hen.setAttribute('xrextras-pinch-scale', '')
hen.setAttribute('animation-mixer', 'clip: idle; loop: repeat')
hen.setAttribute('xrextras-two-finger-rotate', '')
hen.setAttribute('xrextras-hold-drag', '')
hen.setAttribute('environment-map', 'preset: #esplanade')
hen.classList.add('cantap')
hen.setAttribute('material', 'envMap:#studio; metalness:4.0; roughness:0.0')
hen.setAttribute('scale', '12 12 12')
hen.setAttribute('rotation', '0 -23.215 0')
hen.setAttribute('shadow', 'receive: false')
hen.setAttribute('reflections', 'type: realtime')
hen.setAttribute('position', `${this.lastPoint.x} ${this.lastPoint.y} ${this.lastPoint.z}`)
return hen
}
Una vez creado, debe agregarse al DOM:
document.querySelector('a-scene').appendChild(hen)
A-Frame + 8th Wall
Todas las características de A-Frame son 100% compatibles con 8th Wall.
Por ello, es fundamental conocer o consultar la documentación oficial:
👉 Documentación completa de A-Frame
https://aframe.io/docs
Targets en 8th Wall
8th Wall soporta los siguientes tipos de targets para experiencias Image Targets:
Tipos de Targets soportados
- Planos (Flat Images)
- Cilíndricos (Curved Surfaces)
- Cónicos (Packaging tipo botella)
Esto permite experiencias webAR en:
- Latas
- Botellas
- Empaques
- Posters
- Empaques con formas no planas
Proceso para cargar Targets en 8th Wall
-
Ir a la secci ón:
Project → Image Targets -
Subir los archivos del target:
- formato recomendado: PNG/JPG de alta calidad
- contraste fuerte
- sin transparencias
-
Esperar el procesamiento automático de 8th Wall.
-
Copiar el nombre del target generado.
-
Dentro del
body.html, colocar el target así:
<a-entity mindar-image-target="targetIndex: 0"></a-entity>
ó en 8th Wall:
<a-entity xrimage="target: nombre-del-target"></a-entity>
- Se pueden usar múltiples targets en el mismo proyecto.
Buenas prácticas para Targets
-
Evitar imágenes con:
- áreas grandes blancas
- repetición de patrones
- reflejos
- baja resolución
-
Usar imágenes con:
- textura
- bordes definidos
- alto contraste
- características únicas
Peso ideal:
- Entre 300 KB y 800 KB por imagen.
Tamaño recomendado:
- 1000–2000 px de ancho.
Limitaciones de Assets en 8th Wall
Modelos 3D (GLB/GLTF)
- Peso recomendado:
- < 8 MB por archivo
- Ideal: 2–6 MB por modelo
- Los proyectos deben cargar rápido en móviles, por lo que:
- reducir polígonos
- texturas comprimidas
- usar Draco compression cuando sea posible
Texturas
- Tamaño máximo recomendable: 2048px
- Usar formato .jpg para difusas y .png solo cuando haya transparencia
- Ideal: 512px – 1024px para objetos pequeños
Audio
- Usar .mp3 o .ogg
- Evitar audios > 500 KB
Iluminación
- Evitar demasiadas luces en A-Frame (costoso en GPU móvil)
HDRI / Environment Maps
- Reducir tamaño
- Comprimir
- Usar versiones LDR siempre que sea posible
Scripts y lógica
- Evitar loops innecesarios en
tick() - Mantener componentes ligeros
- Liberar memoria al remover elementos
Recomendaciones adicionales (Inmersys)
1. Capa 2D previa
Siempre incluir:
- Logo
- Botón de comenzar
- Pantalla de instrucciones
- Aviso de privacidad
2. Orden de carga
- Cargar XRExtras
- Cargar A-Frame
- Cargar modelos
- Mostrar UI 2D
- Entrar a AR
3. Depuración
- Usar la consola de 8th Wall (muy útil)
- Habilitar XRExtras error logger
Seguimiento de Raycast para mover un elemento 3D (cursor sobre plano)
El siguiente código permite detectar intersecciones mediante raycasting y mover un objeto 3D (un cursor) sobre un plano detectado por 8th Wall/A-Frame.
Incluye un sistema de suavizado para evitar saltos bruscos.
stick() {
if (this.hasFinished) return
if (!this.raycaster) return
const { intersections } = this.raycaster
if (intersections.length === 0) return
let point = null
for (let i = 0; i < intersections.length; i++) {
const { el } = intersections[i].object
if (el && el.id === 'ground') {
point = intersections[i].point
}
}
if (!point) return
if (!this.lastPoint || !point.equals(this.lastPoint)) {
this.lastPoint = point
const cursorObj = this.cursor.object3D
// Movimiento suave
if (!this.smoothPos) {
this.smoothPos = point.clone()
}
const factor = 0.8
this.smoothPos.lerp(point, factor)
cursorObj.position.copy(this.smoothPos)
}
}
Rotación automática hacia la cámara (solo eje Y)
Este componente de A-Frame hace que un objeto 3D siempre mire hacia la cámara, únicamente sobre el eje Y, manteniendo orientación vertical estable.
AFRAME.registerComponent("scope-camera", {
init() {
this.videoObj = this.el.object3D;
},
tick() {
if (!this.videoObj) return;
const cameraEl = this.el.sceneEl.camera.el;
const camObject = cameraEl.object3D;
const dx = camObject.position.x - this.videoObj.position.x;
const dz = camObject.position.z - this.videoObj.position.z;
const angleY = Math.atan2(dx, dz);
this.videoObj.rotation.set(0, angleY, 0);
},
});
Crear un tracker de imagen (Image Target)
Este código genera dinámicamente un image target nombrado, usando el componente xrextras-named-image-target.
createImageTracker({ id, name }) {
const tracker = document.createElement('xrextras-named-image-target')
tracker.setAttribute('id', id)
tracker.setAttribute('name', name)
tracker.setAttribute('button-nav', '')
return tracker
}
Crear múltiples trackers (customizable)
Permite crear varios trackers con diferentes nombres o configuraciones.
Funciona igual que el anterior, pero se usa dentro de un generador mayor que decide cuántos crear.
createImageTracker({ id, name }) {
const tracker = document.createElement('xrextras-named-image-target')
tracker.setAttribute('id', id)
tracker.setAttribute('name', name)
tracker.setAttribute('button-nav', '')
return tracker
}
Agregar elementos dentro del tracker
Cuando el marcador (target) es detectado, se agregan elementos hijos como modelos 3D, grupos de interacción, videos, etc.
tracker.appendChild(group);
tracker.appendChild(video);
Recursos
A-Frame
8th Wall – Image Targets
https://www.8thwall.com/docs/web/#xrimage
8th Wall – XR Extras
https://www.8thwall.com/docs/web/#xrextras
Three.js (matemáticas, rotaciones, raycasting base)
GLTF Viewer (para probar modelos antes de subirlos)
Interpolación y suavizado en Three.js (lerp)
https://threejs.org/docs/#api/en/math/Vector3.lerp
Documentación esencial
- A-Frame Docs → https://aframe.io/docs
- 8th Wall Docs → https://www.8thwall.com/docs
- Three.js → https://threejs.org/docs
- GLTF Viewer → https://gltf.report
- XR Extras → https://www.8thwall.com/docs/web/#xrextras