Aprende a crear una tarjeta de presentacion con Realidad Aumentada
How to create an AR Business Card


Bienvenidos aumentados a este nuevo video tutorial donde aprenderemos como crear desde cero una tarjeta de presentación personal o de negocio para tus clientes usando Realidad Aumentada de manera rápida y sencilla.
Que vamos a utilizar en este tutorial:
- Un editor de código, el de tu preferencia. Yo utilizare Visual Studio Code: Link para descargar
- Instala el plugin Live Server en el Visual Studio Code: Link para descargar
- Un video en formato .mp4 donde expliques tu resume o el contenido que tu cliente prefiera.
- Las imágenes en formato .png para los botones de redes sociales. Yo descargue una versión gratuita desde https://www.flaticon.es
- Para que la app de RA reconozca tu tarjeta de presentación comercial necesitas generar un archivo llamado targets.mind y lo puedes generar desde aquí: Compilador de Marcadores
Yo utilice para este tutorial esta imagen como marcador de mi Augmented Reality App

Puedes probar la aplicación antes del tutorial en el siguiente link: Crea Tarjetas de Presentación con Realidad Aumentada | Tutorial AR
Comencemos con la explicación del código CSS
- Estilos Base y Contenedor Principal
Esta parte define la apariencia general de la página y crea un «lienzo» para la aplicación de Realidad Aumentada.
body {
margin: 0; /* Elimina los márgenes predeterminados del navegador. */
/* overflow: hidden; -> Comentado para que AdSense pueda añadir barras de scroll si es necesario. */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; /* Usa fuentes modernas y comunes en la mayoría de dispositivos. */
color: #ffffff; /* Color del texto por defecto: blanco. */
background-color: #000; /* Fondo negro para que no haya un destello blanco al cargar. */
}
/* Estilo para el contenedor que envuelve toda la experiencia AR. */
#ar-wrapper {
position: relative; /* Base para posicionar elementos internos como los botones. */
width: 100%; /* Ocupa todo el ancho disponible. */
height: 100vh; /* Ocupa el 100% de la altura de la ventana del navegador. */
overflow: hidden; /* Evita que la escena 3D se desborde y cree barras de scroll no deseadas. */
}- Superposiciones (Header y Footer)
Estos estilos crean las barras superior e inferior que se quedan fijas en la pantalla, superponiéndose a la vista de la cámara.
.page-header, .page-footer {
position: fixed; /* Los mantiene fijos en la pantalla aunque el contenido se mueva. */
left: 0; /* Los alinea al borde izquierdo. */
width: 100%; /* Hace que ocupen todo el ancho. */
text-align: center; /* Centra el texto en su interior. */
z-index: 11; /* Un número alto para asegurar que estén por encima de todo lo demás. */
background-color: rgba(0, 0, 0, 0.6); /* Fondo negro semitransparente. */
backdrop-filter: blur(8px); /* ¡Efecto clave! Crea un desenfoque tipo "vidrio esmerilado" del contenido que está detrás. */
-webkit-backdrop-filter: blur(8px); /* Versión para navegadores como Safari. */
padding: 10px 0; /* Espaciado interno (10px arriba/abajo, 0px a los lados). */
box-sizing: border-box; /* Asegura que el padding no aumente el ancho total del elemento. */
}
/* Estilos específicos para el encabezado. */
.page-header {
top: 0; /* Lo pega a la parte superior de la pantalla. */
border-bottom: 1px solid rgba(255, 255, 255, 0.1); /* Línea divisoria sutil en la parte inferior. */
}
/* Estilo del título principal (H1). */
.page-header h1 {
margin: 0; /* Elimina márgenes. */
/* Esta es una fuente responsive: su tamaño será el 3% del ancho de la ventana (3vw), pero nunca más pequeño que 1.2rem ni más grande que 1.5rem. */
font-size: clamp(1.2rem, 3vw, 1.5rem);
font-weight: 600; /* Texto en seminegrita. */
color: #ffffff; /* Color de texto blanco. */
text-shadow: 0 0 8px rgba(0, 136, 255, 0.7); /* Sombra de texto que crea un efecto de brillo azulado. */
}
/* Estilos específicos para el pie de página. */
.page-footer {
bottom: 0; /* Lo pega a la parte inferior de la pantalla. */
border-top: 1px solid rgba(255, 255, 255, 0.1); /* Línea divisoria sutil en la parte superior. */
}
/* Estilos para los enlaces (<a>) del pie de página. */
.page-footer a {
color: #5bcaff; /* Color azul claro. */
text-decoration: none; /* Quita el subrayado. */
margin: 0 15px; /* Espaciado entre los enlaces. */
transition: color 0.3s ease; /* Transición suave del color al pasar el mouse. */
}
.page-footer a:hover {
color: #ffffff; /* Cambia el color a blanco al pasar el mouse. */
text-decoration: underline; /* Añade un subrayado. */
}- Controles y Botones
Esta sección define la apariencia y posición de los botones de «Pausar» y «Silenciar».
.controls-container {
position: absolute; /* Posicionado relativo a su padre (#ar-wrapper). */
top: 85px; /* Lo baja 85px desde la parte superior para no chocar con el header. */
left: 50%; /* Lo mueve al 50% del ancho de su padre. */
transform: translateX(-50%); /* Lo desplaza hacia la izquierda el 50% de su propio ancho para centrarlo perfectamente. */
z-index: 10; /* Se asegura de que esté por encima de la escena AR pero debajo del header/footer. */
display: flex; /* Coloca los botones uno al lado del otro. */
gap: 10px; /* Crea un espacio de 10px entre los botones. */
}
.control-button {
padding: 10px 15px; /* Espaciado interno del botón. */
font-size: 16px; /* Tamaño del texto. */
background-color: rgba(0, 0, 0, 0.6); /* Fondo semitransparente. */
border: none; /* Sin borde. */
border-radius: 8px; /* Bordes redondeados. */
cursor: pointer; /* Cambia el cursor a una mano para indicar que es clickeable. */
backdrop-filter: blur(5px); /* También tiene el efecto de desenfoque. */
transition: background-color 0.3s ease; /* Transición suave del color de fondo. */
}
.control-button:hover {
background-color: rgba(0, 0, 0, 0.8); /* Oscurece el botón al pasar el mouse. */
}- Interfaz de Escaneo (Overlay)
Estos son los estilos para la pantalla de «Escanea tu marcador», incluyendo las animaciones.
Aquí en el blog tenemos el tutorial de como modificar el UI de MindAR en el siguiente link: Personaliza el UI de las apps de realidad aumentada WebAR
Tambien te puede interesar: Crea una pantalla de carga a las apps de realidad aumentada
/* La pantalla que se muestra mientras se busca el marcador. */
.ui-custom-tracking-overlay {
position: fixed; /* La fija para que ocupe toda la ventana. */
top: 0; left: 0; width: 100%; height: 100%; /* Cubre toda la pantalla. */
pointer-events: none; /* Importante: Permite que los clics "atraviesen" esta capa. */
z-index: 9; /* Se posiciona justo debajo de los controles. */
opacity: 1; /* Totalmente visible por defecto. */
transition: opacity 0.5s ease; /* Hace que su desaparición sea suave. */
}
/* Variables CSS: Definen valores reutilizables. Si cambias un valor aquí, se actualiza en todos los lugares donde se usa. */
:root {
--primary-colorUI: #0066ff; /* Color azul principal. */
--corner-size: 35px; /* Tamaño de las esquinas del cuadro. */
--square-size: 60vmin; /* El cuadro de escaneo ocupará el 60% de la dimensión más pequeña de la pantalla (alto o ancho). */
/* ...y otras variables... */
}
/* Oculta la interfaz de usuario que MindAR trae por defecto. */
.mindar-ui-overlay, .mindar-ui-target, .a-enter-vr-button {
display: none !important;
}
/* Clase que se añade con JS para ocultar el overlay. */
.ui-custom-tracking-overlay.hidden {
opacity: 0; /* La hace invisible. */
}
/* El cuadro central donde se muestra la imagen de referencia. */
.ui-tracking-square {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%); /* El mismo truco para centrarlo perfectamente. */
/* ... otros estilos para alinear su contenido ... */
}
/* Creación de las esquinas del cuadro usando "pseudo-elementos". */
/* En lugar de añadir 4 divs en el HTML para las esquinas, se crean virtualmente con CSS. */
.ui-tracking-square::before,
.ui-tracking-square::after,
.ui-tracking-square span::before,
.ui-tracking-square span::after {
content: ''; /* Necesario para que los pseudo-elementos se muestren. */
position: absolute;
width: var(--corner-size); /* Usa las variables definidas en :root. */
height: var(--corner-size);
border: var(--border-thickness) solid var(--primary-colorUI);
filter: drop-shadow(...); /* Crea el efecto de brillo alrededor de las esquinas. */
animation: ui-corner-glow 3s infinite ease-in-out; /* Aplica la animación de brillo. */
}
/* Posiciona cada una de las 4 esquinas y les quita bordes para formar una "L". */
.ui-tracking-square::before { /* Esquina superior izquierda */
top: -2px; left: -2px;
border-right: none; border-bottom: none;
}
/* ...y así para las otras 3 esquinas... */
/* La línea animada que se mueve de arriba a abajo. */
.ui-scan-line {
position: absolute;
/* ...estilos de apariencia... */
animation: ui-scan-animation 3s infinite ease-in-out; /* Aplica la animación de movimiento. */
}
/* Define la animación de la línea de escaneo. */
@keyframes ui-scan-animation {
0%, 100% { top: 0; } /* Al inicio y al final, la línea está arriba. */
50% { top: 100%; } /* A la mitad de la animación, está abajo. */
}
/* Define la animación de brillo para las esquinas. */
@keyframes ui-corner-glow {
0% { filter: drop-shadow(...); } /* Estado inicial del brillo. */
100% { filter: drop-shadow(...); } /* Estado final del brillo (más intenso). */
}Análisis del Código HTML
Este documento es la base estructural de toda tu aplicación de Realidad Aumentada. Define qué elementos existen y cómo están organizados, desde los metadatos invisibles hasta los componentes 3D interactivos.
- <head>: La Configuración Inicial
Esta sección contiene toda la información de «preparación» que el navegador necesita antes de mostrar la página. No es visible para el usuario, pero es fundamental.
<head> <!-- Define la codificación de caracteres a UTF-8. Esencial para que las tildes y la "ñ" se muestren correctamente. --> <meta charset="UTF-8"> <!-- Configura el "viewport" para que la página se vea bien en cualquier dispositivo, especialmente en móviles. `width=device-width` hace que el ancho de la página sea igual al del dispositivo, e `initial-scale=1` establece el nivel de zoom inicial. --> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- El título que aparece en la pestaña del navegador. Es importante para la identificación y el SEO. --> <title>Crea Tarjetas de Presentación con Realidad Aumentada | Tutorial AR</title> <!-- Importa la librería A-Frame, que es el framework base que permite crear escenas 3D y de Realidad Virtual/Aumentada usando HTML. --> <script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script> <!-- Importa la librería MindAR, que es un complemento para A-Frame especializado en el reconocimiento de imágenes (Image Tracking). --> <script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.5/dist/mindar-image-aframe.prod.js"></script> </head>
- <body>: El Contenido Visible
Aquí se encuentra todo lo que el usuario ve e interactúa en la pantalla.
- Estructura Principal y Superposiciones (UI)
Estos son los elementos de la interfaz de usuario que se superponen a la vista de la cámara.
<body> <!-- Contenedor principal para toda la aplicación. Ayuda a organizar el contenido y es útil para la gestión de anuncios. --> <div id="ar-wrapper"> <!-- Encabezado fijo en la parte superior. Contiene el título principal de la aplicación. --> <header class="page-header"> <h1>Tarjeta de Presentación en Realidad Aumentada</h1> <p>AR Business Card</p> </header> <!-- Contenedor para los botones que controlan el video (Pausar/Reproducir y Silenciar/Activar Audio). --> <div class="controls-container"> <button id="playPauseBtn" class="control-button">Pausar</button> <button id="muteUnmuteBtn" class="control-button">Activar Audio</button> </div> <!-- La pantalla de "escaneo" que se muestra al inicio, indicando al usuario qué hacer. --> <div id="ui-custom-tracking-overlay" class="ui-custom-tracking-overlay"> <div class="ui-tracking-square"> <!-- Imagen de referencia para que el usuario sepa qué marcador buscar. --> <img src="Bienvenida.jpg" alt="Marker Target" class="scanning-image"> <div class="scanning-message">Escanea tu marcador</div> <!-- Elementos visuales para la animación de escaneo. --> <div class="ui-scan-line"></div> <span></span> </div> </div>
- La Escena de Realidad Aumentada (<a-scene>)
Este es el componente más importante, donde ocurre toda la experiencia de RA.
<!-- Define el inicio de la escena de A-Frame y la configura para usar MindAR. --> <a-scene mindar-image="imageTargetSrc: ./targets.mind;" uiScanning="#ui-custom-tracking-overlay;" ...> <!-- La cámara virtual a través de la cual el usuario ve el mundo y los objetos 3D. --> <!-- `raycaster` se añade para poder detectar clics en los objetos con la clase ".clickable". --> <a-camera position="0 0 0" look-controls="enabled: false" ...></a-camera> <!-- `<a-assets>` es un sistema de precarga. Todo lo que pongas aquí (videos, imágenes, modelos 3D) se cargará antes de que inicie la escena para evitar tiempos de espera. --> <a-assets> <video id="miVideo" src="./video.mp4" ...></video> <img id="imagenSuperior" src="./imagen_superior.png" /> <!-- ... resto de imágenes para los íconos de redes sociales ... --> </a-assets> <!-- `<a-entity>` es el objeto base en A-Frame. Este en particular es el "ancla" para el marcador. --> <!-- `mindar-image-target="targetIndex: 0"` le dice a MindAR que este objeto debe aparecer cuando se detecte el primer marcador (índice 0) del archivo `targets.mind`. --> <a-entity id="target" mindar-image-target="targetIndex: 0"> <!-- Un plano 3D que ocupa el área del marcador y reproduce el video. --> <!-- `src="#miVideo"` lo conecta con el video precargado en <a-assets>. --> <a-plane id="videoPlane" src="#miVideo" ...></a-plane> <!-- Una imagen 3D que se posiciona encima del video. --> <a-image id="imagenSuperiorEl" src="#imagenSuperior" ...></a-image> <!-- Serie de imágenes 3D que funcionan como botones interactivos. --> <!-- `class="clickable"` las marca para que el `raycaster` de la cámara pueda detectar clics en ellas. --> <a-image id="facebookBtn" class="clickable" src="#iconoFacebook" ...></a-image> <!-- ... resto de los íconos/botones ... --> </a-entity> </a-scene>
- Cierre de la Interfaz
Finalmente, se cierra la estructura principal con el pie de página.
<!-- Pie de página fijo en la parte inferior, con enlaces externos. --> <footer class="page-footer"> <a href="https://blog.realidad-aumentada.com.co" target="_blank">Ver Tutorial</a> <a href="https://www.youtube.com/carlosreinamcr" target="_blank">Canal de YouTube</a> </footer> </div> <!-- Cierre del #ar-wrapper --> </body> </html>
Análisis del Código JavaScript
Este script se encarga de la interactividad, gestionando los eventos del usuario (clics en botones) y los eventos de la cámara (encontrar o perder el marcador).
- Inicialización y Selección de Elementos
El código primero se asegura de que la página esté completamente cargada y luego obtiene referencias a todos los elementos HTML con los que necesita trabajar.
// Este es el punto de partida. Se asegura de que el script no se ejecute hasta que todo el HTML se haya cargado y esté listo.
document.addEventListener('DOMContentLoaded', () => {
// Selecciona el componente principal de A-Frame, la escena.
const sceneEl = document.querySelector('a-scene');
// Espera a que A-Frame haya cargado completamente la escena y todos sus componentes internos. Es crucial para evitar errores al intentar manipular elementos que aún no existen.
sceneEl.addEventListener('loaded', () => {
// --- SELECCIÓN DE ELEMENTOS ---
// Se crean constantes para tener acceso rápido y fácil a los elementos clave del HTML.
const target = document.querySelector('#target'); // La entidad que representa el marcador.
const video = document.querySelector('#miVideo'); // El elemento <video>.
const playPauseBtn = document.querySelector('#playPauseBtn'); // Botón de Pausar/Reproducir.
const muteUnmuteBtn = document.querySelector('#muteUnmuteBtn'); // Botón de Silenciar.
const imagenSuperiorEl = document.querySelector('#imagenSuperiorEl'); // La imagen 3D que se desvanece.
const scanningOverlay = document.getElementById('ui-custom-tracking-overlay'); // La pantalla de escaneo.- Variables de Estado
Estas variables actúan como la «memoria» del script, recordando información importante mientras la aplicación está en uso.
// --- VARIABLES DE ESTADO --- let hideImageTimeout; // Guardará el temporizador para la animación de la imagen. Permite cancelarlo si es necesario. // Esta es una variable CRÍTICA. Actúa como un interruptor. Empieza en 'false' y cambiará a 'true' la primera vez que se encuentre el marcador. let isTargetEverFound = false;
- Lógica de los Controles del Video
Aquí se define qué sucede cuando el usuario hace clic en los botones de «Pausar» y «Silenciar».
// --- CONTROL DEL VIDEO ---
// Se añade un "escuchador de eventos" al botón de Pausar/Reproducir.
playPauseBtn.addEventListener('click', () => {
// Esta es la "barrera de seguridad". Si el marcador nunca ha sido encontrado (`isTargetEverFound` es false), la función se detiene aquí (`return`) y no hace nada.
if (!isTargetEverFound) return;
// Si la barrera se pasa, comprueba si el video está pausado.
if (video.paused) {
video.play(); // Si está pausado, lo reproduce.
playPauseBtn.textContent = 'Pausar'; // Y actualiza el texto del botón.
} else {
video.pause(); // Si se está reproduciendo, lo pausa.
playPauseBtn.textContent = 'Reproducir'; // Y actualiza el texto.
}
});
// Lógica idéntica para el botón de Silenciar.
muteUnmuteBtn.addEventListener('click', () => {
if (!isTargetEverFound) return; // Misma barrera de seguridad.
// `video.muted = !video.muted;` es un atajo para invertir el estado. Si estaba silenciado (`true`), lo pone en `false`, y viceversa.
video.muted = !video.muted;
// Actualiza el texto del botón según el nuevo estado de 'muted'.
muteUnmuteBtn.textContent = video.muted ? 'Activar Sonido' : 'Silenciar';
});- Eventos Principales de MindAR (Detección del Marcador)
Esta es la parte más importante. Aquí se define cómo reacciona la aplicación cuando la cámara encuentra o pierde de vista el marcador.
// --- EVENTOS DE MINDAR ---
// Se ejecuta automáticamente cuando la cámara PIERDE de vista el marcador.
target.addEventListener("targetLost", () => {
video.pause(); // Pausa el video inmediatamente.
playPauseBtn.textContent = 'Reproducir'; // Actualiza el botón.
scanningOverlay.classList.remove('hidden'); // Muestra de nuevo la pantalla de "Escanea tu marcador".
// Si había un temporizador para desvanecer la imagen, lo cancela para evitar que se ejecute mientras el marcador no es visible.
clearTimeout(hideImageTimeout);
// Restablece la imagen superior a su estado inicial (visible y con opacidad completa) para la próxima vez que se encuentre el marcador.
imagenSuperiorEl.setAttribute('visible', true);
imagenSuperiorEl.setAttribute('material', 'opacity', 1);
});
// Se ejecuta automáticamente cuando la cámara ENCUENTRA el marcador.
target.addEventListener("targetFound", () => {
// ¡Paso clave! Cambia la variable de estado a 'true'. A partir de este momento, los botones de control comenzarán a funcionar.
isTargetEverFound = true;
if (video.paused) {
video.play(); // Si el video estaba pausado, lo reproduce.
playPauseBtn.textContent = 'Pausar';
}
scanningOverlay.classList.add('hidden'); // Oculta la pantalla de escaneo con una transición suave.
// Restablece la opacidad y quita cualquier animación anterior de la imagen superior.
imagenSuperiorEl.setAttribute('material', 'opacity', 1);
imagenSuperiorEl.removeAttribute('animation');
// Limpia cualquier temporizador pendiente de una detección anterior.
clearTimeout(hideImageTimeout);
// Inicia un nuevo temporizador. La función dentro de `setTimeout` se ejecutará después de 5000 milisegundos (5 segundos).
hideImageTimeout = setTimeout(() => {
// Después de 5 segundos, aplica una animación a la imagen superior usando el sistema de componentes de A-Frame.
imagenSuperiorEl.setAttribute('animation', {
property: 'material.opacity', // La propiedad a animar (la opacidad del material).
from: 1, // Valor inicial (totalmente opaco).
to: 0, // Valor final (totalmente transparente).
dur: 1500, // Duración de la animación en milisegundos (1.5 segundos).
easing: 'easeInQuad' // Tipo de aceleración para un desvanecimiento suave.
});
}, 5000);
});- Lógica de los Enlaces a Redes Sociales
Esta sección final asigna de manera eficiente la funcionalidad de clic a todos los íconos de redes sociales.
// --- LÓGICA DE REDES SOCIALES ---
// Se crea un objeto que asocia el ID de cada ícono (en el HTML) con su URL correspondiente.
const links = {
facebookBtn: 'https://...',
youtubeBtn: 'https://...',
// ... etc.
};
// Este bucle es una forma moderna y eficiente de recorrer el objeto 'links'.
// Por cada entrada, obtiene el 'id' (ej: "facebookBtn") y la 'url'.
for (const [id, url] of Object.entries(links)) {
const button = document.querySelector(`#${id}`); // Selecciona el ícono usando su ID.
button.addEventListener('click', () => { // Le añade un evento de clic.
window.open(url, '_blank'); // Cuando se hace clic, abre la URL en una nueva pestaña.
});
}
});
});Compila tu aplicación uniendo el HTML, CSS y JavaScript y tu resultado debe ser algo así:

Te dejo el tutorial en video para que aprendas como hacer la aplicación desde cero:
Quieres descargar los archivos de este tutorial te invito a seguirme en la comunidad de WhatsApp en el siguiente link: Comunidad Realidad Aumentada Empezando desde cero