La Realidad Aumentada (AR) ha transformado la manera en que interactuamos con el contenido digital, permitiéndonos superponerlo en el mundo real. Sin embargo, la verdadera magia ocurre cuando estos objetos 3D no son estáticos, sino que cobran vida.
En este artículo, desglosaremos un código completo que crea una experiencia de AR interactiva, permitiendo al usuario controlar todas las animaciones de un modelo 3D directamente desde el navegador.

Cómo Controlar Animaciones 3D en Realidad Aumentada Web con MindAR, A-Frame y Three.js
- MindAR: Un motor de AR web ligero y eficiente, especializado en el reconocimiento de imágenes.
- A-Frame: Un framework de Mozilla que simplifica enormemente la creación de escenas 3D y de Realidad Virtual/Aumentada usando HTML.
- Three.js: La librería de gráficos 3D por excelencia en la web, que funciona como el motor de renderizado bajo el capó de A-Frame.
El Objetivo del Proyecto
El código que analizaremos logra lo siguiente:
- Inicia una vista de cámara a través del navegador.
- Utiliza MindAR para detectar un marcador de imagen específico (targets.mind – Puedes utilizar el nombre que quieras solo debes de cambiarlo en el archivo y en el codigo).
- Al detectar el marcador, muestra un modelo 3D en formato GLB (RobotExpressive.glb) – Ruta de la libreria Three.js si la descargas (models/gltf/RobotExpressive/RobotExpressive.glb).
- Dinámicamente, lee todas las pistas de animación contenidas en el archivo del modelo.
- Crea una interfaz de usuario con un botón para cada animación encontrada.
- Permite al usuario cambiar entre las animaciones del modelo con un solo clic, con transiciones suaves.
Análisis Detallado del Código
El proyecto se estructura en un único archivo HTML, lo que facilita su despliegue. Vamos a dividirlo en sus partes fundamentales.
Estructura HTML y Configuración de la Escena.
La base de la aplicación es una escena de A-Frame configurada para MindAR.
<a-scene mindar-image="imageTargetSrc: ./targets.mind;" ...> <a-assets> <a-asset-item id="robotModel" src="./RobotExpressive.glb"></a-asset-item> </a-assets> <a-entity mindar-image-target="targetIndex: 0"> <a-gltf-model src="#robotModel" animation-controller></a-gltf-model> </a-entity> </a-scene>
- <a-scene>: Es el contenedor principal. El atributo mindar-image lo inicializa como una escena de MindAR y le indica dónde encontrar el archivo del marcador (imageTargetSrc).
- <a-assets>: Es una buena práctica para precargar recursos pesados como los modelos 3D, asegurando que estén listos antes de que la escena intente mostrarlos.
- <a-entity mindar-image-target>: Esta entidad actúa como un «ancla». Su contenido solo se hará visible cuando el marcador con targetIndex: 0 sea detectado por la cámara.
- <a-gltf-model>: Carga y muestra nuestro modelo 3D. El atributo clave aquí es animation-controller, que invoca nuestro componente de JavaScript personalizado.
La Interfaz de Usuario (HTML y CSS).
Para controlar las animaciones, necesitamos botones. La UI se define con un simple div y se estiliza con CSS para que aparezca en la parte inferior de la pantalla.
<div id="animation-buttons"></div>
El CSS asegura que los botones sean visibles, accesibles y no interfieran con la vista de AR. Lo interesante es que este div está vacío inicialmente; los botones se crearán y añadirán con JavaScript.
El Cerebro: El Componente animation-controller
Aquí reside toda la lógica interactiva. Creamos un componente de A-Frame llamado animation-controller.
AFRAME.registerComponent('animation-controller', {
init: function () { /* ... */ },
playAnimation: function (name) { /* ... */ },
tick: function (time, timeDelta) { /* ... */ }
});init(): Esta función se ejecuta una vez cuando el componente se carga.
- Espera a que el modelo se cargue (model-loaded): No podemos acceder a las animaciones hasta que el archivo GLB esté completamente procesado.
- Obtiene el objeto de Three.js: A-Frame nos permite bajar a un nivel más profundo y acceder al objeto de Three.js con model.getObject3D(‘mesh’).
- Crea el AnimationMixer: Esta es la herramienta fundamental de Three.js para gestionar y reproducir animaciones. Se asocia al modelo 3D.
- Itera y crea botones: El código recorre el array gltf.animations (que contiene todos los clips de animación del modelo), y por cada uno, crea un botón en la UI y guarda la «acción» de animación correspondiente para poder activarla después.
- playAnimation(name): Esta función se llama cuando un usuario hace clic en un botón. Realiza una transición suave: detiene la animación activa con un fadeOut y comienza la nueva con un fadeIn y play.
- tick(): Esta función es el corazón del bucle de renderizado. Se ejecuta en cada fotograma. Su única, pero crucial, tarea es actualizar el AnimationMixer (mixer.update()). Sin esto, las animaciones se quedarían congeladas en su primer fotograma.
El Flujo de la Animación: ¿Cómo trabajan juntas las librerías?
Es importante entender el rol de cada tecnología en el manejo de las animaciones:
- A-Frame y el Formato GLTF/GLB: A-Frame sabe cómo cargar modelos gltf o glb. Este formato es el estándar para la web y tiene la capacidad de empaquetar en un solo archivo la geometría del modelo, los materiales, las texturas y, por supuesto, múltiples pistas de animación.
- Three.js y AnimationMixer: Aunque A-Frame tiene un componente básico de animación, para un control avanzado (como tener múltiples clips, mezclarlos o hacer transiciones suaves), necesitamos acceder directamente a las capacidades de Three.js. El AnimationMixer es el objeto que recibe los clips de animación y los procesa. Nosotros solo le decimos qué clip reproducir y él se encarga del resto.
- MindAR y A-Frame como Orquestadores: MindAR no gestiona las animaciones directamente. Su trabajo es proporcionar el contexto de Realidad Aumentada. A-Frame actúa como el pegamento: utiliza MindAR para saber dónde y cuándo mostrar la entidad, y luego nuestro componente personalizado utiliza la potencia de Three.js para darle vida al modelo dentro de esa entidad.
Información Clave para Realizar este tipo de Aplicaciones
Si quieres empezar a crear tus propias experiencias de AR con animaciones, ten en cuenta lo siguiente:
- El Modelo es la Clave: Tu archivo gltf o glb debe tener las animaciones ya integradas. Estas se crean en software de modelado 3D como Blender, Maya o 3ds Max.
- La Necesidad de un Servidor Local: Por razones de seguridad del navegador (políticas CORS), no puedes simplemente abrir el archivo index.html en tu navegador. Debes servirlo a través de un servidor web local. Herramientas como la extensión «Live Server» para Visual Studio Code son perfectas para esto.
- Calidad del Marcador: El rendimiento del seguimiento de MindAR depende en gran medida de tu marcador. Usa imágenes con buen contraste, muchos detalles y patrones no repetitivos.
- Optimización: Los dispositivos móviles tienen recursos limitados. Usa modelos 3D con un número de polígonos razonable (low-poly) y texturas optimizadas para la web para asegurar una experiencia fluida.
Conclusión
Este proyecto es un ejemplo fantástico de cómo las librerías web modernas pueden colaborar para crear experiencias de Realidad Aumentada ricas e interactivas. Al combinar la simplicidad declarativa de A-Frame con el poder de bajo nivel de Three.js, hemos construido un sistema reutilizable que puede dar vida a cualquier modelo 3D con animaciones, directamente en el navegador de cualquier smartphone.
Ahora te toca a ti. ¡Intenta cambiar el modelo, crea tus propios marcadores y experimenta con el fascinante mundo de la Realidad Aumentada web!
Quieres el código completo de la aplicación, aquí lo tienes:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.jsdelivr.net/gh/aframevr/aframe@1.5.0/dist/aframe-master.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.5/dist/mindar-image-aframe.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.138.0/build/three.min.js"></script>
<style>
/* Estilos para los botones de animación */
#animation-buttons {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
#animation-buttons button {
padding: 10px 15px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: 2px solid white;
border-radius: 5px;
cursor: pointer;
font-family: sans-serif;
}
#animation-buttons button:hover {
background-color: rgba(255, 255, 255, 0.9);
color: black;
}
</style>
<script>
// Componente de A-Frame para controlar las animaciones
AFRAME.registerComponent('animation-controller', {
init: function () {
this.mixer = null;
this.actions = {};
this.activeAction = null;
const model = this.el;
const uiContainer = document.querySelector('#animation-buttons');
// 1. Esperar a que el modelo 3D se cargue
model.addEventListener('model-loaded', (e) => {
const gltf = model.getObject3D('mesh');
if (!gltf) return;
// 2. Crear el AnimationMixer de Three.js
this.mixer = new THREE.AnimationMixer(gltf);
const animations = gltf.animations;
// 3. Crear un botón y una acción para cada animación
animations.forEach((clip) => {
const action = this.mixer.clipAction(clip);
this.actions[clip.name] = action;
// Crear el botón en la UI
const button = document.createElement('button');
button.innerText = clip.name;
button.addEventListener('click', () => {
this.playAnimation(clip.name);
});
uiContainer.appendChild(button);
});
// Opcional: Iniciar la primera animación por defecto
if (animations.length > 0) {
this.playAnimation(animations[0].name);
}
});
},
// Función para cambiar de animación
playAnimation: function (name) {
const newAction = this.actions[name];
if (this.activeAction === newAction) return; // No hacer nada si ya se está reproduciendo
// Detener suavemente la animación anterior
if (this.activeAction) {
this.activeAction.fadeOut(0.5);
}
// Reproducir la nueva animación
newAction.reset().fadeIn(0.5).play();
this.activeAction = newAction;
},
// 4. Actualizar el mixer en cada frame
tick: function (time, timeDelta) {
if (this.mixer) {
this.mixer.update(timeDelta / 1000); // delta está en milisegundos
}
},
});
</script>
</head>
<body>
<div id="animation-buttons"></div>
<a-scene mindar-image="imageTargetSrc: ./targets.mind;" color-space="sRGB" renderer="colorManagement: true, physicallyCorrectLights" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false">
<a-assets>
<a-asset-item id="robotModel" src="./RobotExpressive.glb"></a-asset-item>
</a-assets>
<a-camera position="0 0 0" look-controls="enabled: false"></a-camera>
<a-entity mindar-image-target="targetIndex: 0">
<a-gltf-model
src="#robotModel"
position="0 0 0"
scale="0.4 0.4 0.4"
rotation="0 0 0"
animation-controller>
</a-gltf-model>
</a-entity>
</a-scene>
</body>
</html>También te puede interesar: