Convierte tu experiencia en un mini CRM con productos, leads y exportación CSV.
Antes de continuar
Descargar el proyecto base del nivel básico
Para que el estudiante no tenga que volver al curso básico ni adivinar qué archivo usar, aquí dejamos el proyecto base listo. Este ZIP contiene el punto exacto desde donde empieza el nivel intermedio: landing, cámara MindAR, panel comercial, formulario, modelo 3D GLB y carga con Meshopt.
Proyecto base para iniciar el intermedio
Descarga este archivo, descomprímelo y ábrelo en VS Code. Trabajaremos sobre index.html, styles.css y app.js.
Qué debes tener antes de escribir código intermedio
index.html con commercial-panel, lead-form y a-scene.
app.js con prepareCompressedModel(), targetFound, targetLost y guardado básico de lead.
assets/Modelo3D-Helado.glb dentro del proyecto.
targets/campana.mind creado desde el compilador.
Proyecto intermedio
Convertir la experiencia en un mini embudo comercial móvil
En el intermedio partimos del proyecto básico. Ya tienes cámara, marcador, modelo 3D GLB del helado, panel y formulario. Ahora agregamos varios productos, almacenamiento de leads y exportación CSV, pero con una decisión importante de experiencia de usuario: el producto se elige antes de abrir la cámara.
Por qué cambiamos el flujo: en teléfono es incómodo hacer scroll en un menú mientras el usuario apunta al marcador. Por eso el selector de producto vive en la pantalla inicial. Durante la cámara solo mostramos un panel compacto con información, WhatsApp y un botón para abrir el formulario si el usuario realmente quiere dejar sus datos.
Esto se parece más a un CRM ligero: cada lead queda relacionado con una oferta, un producto y una fecha. El modelo 3D del helado se mantiene como contenido aumentado principal, cargado desde assets/Modelo3D-Helado.glb con escala 1 1 1. Como el GLB usa compresión Meshopt, el template conserva la carga con GLTFLoader y meshopt_decoder.module.js.
Flujo nuevo para móvil
El usuario abre la página desde un QR.
Elige la oferta o producto en tarjetas pequeñas antes de activar la cámara.
Toca Abrir experiencia AR.
Apunta al marcador y ve el modelo 3D.
El panel inferior aparece compacto, sin selector pesado ni botones administrativos.
Si quiere avanzar, toca WhatsApp o abre el formulario.
HTML intermedio
Agregar selección de producto antes de abrir cámara
En lugar de poner un selector dentro del panel de cámara, crearemos un contenedor de productos en la pantalla inicial. Esto es más cómodo en móvil porque el usuario elige con calma antes de sostener el teléfono frente al marcador.
Dónde va: abre index.html. Busca <section id="intro" class="intro">. Dentro de esa sección, coloca <div id="product-picker"> antes del botón start-ar. Luego deja el botón de exportar CSV también en la pantalla inicial, no en el panel de cámara.
HTML móvil recomendadoindex.html
<section id="intro" class="intro">
<p class="tag">WebAR comercial</p>
<h1>Elige una oferta, escanea y captura leads.</h1>
<p class="intro-copy">Selecciona el producto antes de abrir la cámara.</p>
<div id="product-picker" class="product-picker" aria-label="Selecciona un producto"></div>
<div class="intro-actions">
<button id="start-ar" type="button">Abrir experiencia AR</button>
<button id="export-csv" class="secondary-button" type="button">Exportar leads CSV</button>
</div>
</section>
<section id="commercial-panel" class="commercial-panel" hidden>
<h2 id="offer-title">Oferta detectada</h2>
<p id="offer-copy">Escanea el marcador para ver esta oferta en realidad aumentada.</p>
<div class="ar-actions">
<button id="toggle-lead-form" type="button">Dejar mis datos</button>
<a id="whatsapp-link" href="#" target="_blank" rel="noopener">WhatsApp</a>
</div>
<form id="lead-form" hidden>...</form>
<p id="form-status"></p>
</section>
Por qué este HTML funciona mejor en móvil
product-picker: queda en la pantalla inicial. JavaScript llenará este contenedor con botones de producto.
intro-actions: agrupa acciones que ocurren antes de la cámara: iniciar AR y exportar leads.
export-csv: se mueve fuera de la cámara porque es una acción administrativa. El usuario no debería exportar mientras apunta al marcador.
commercial-panel: queda más compacto. Solo muestra la oferta detectada y acciones rápidas.
toggle-lead-form: evita mostrar el formulario completo todo el tiempo. El formulario se abre solo cuando el usuario quiere dejar datos.
lead-form hidden: empieza oculto para que el panel no tape la cámara en pantallas pequeñas.
CSS para selección previa y panel compactostyles.css
.product-picker: crea una cuadrícula para elegir producto antes de abrir cámara. En escritorio aparecen dos columnas; en móvil cambia a una sola columna.
.product-option: convierte cada producto en una tarjeta táctil. El usuario no tiene que abrir un selector pequeño, solo toca una tarjeta grande.
.product-option.active: muestra visualmente cuál producto está seleccionado. Esto evita confusión antes de escanear.
.intro-actions: organiza los botones previos a la cámara: iniciar AR y exportar CSV.
.ar-actions: organiza las acciones rápidas durante la cámara: dejar datos y WhatsApp.
.commercial-panel con 42vh: limita la altura del panel para que no tape todo el video de la cámara.
.commercial-panel.form-open: aumenta la altura solo cuando el formulario se abre. Así el panel normal se mantiene compacto.
#lead-form[hidden]: oculta el formulario por completo hasta que JavaScript quite el atributo hidden.
@media: adapta la interfaz a pantallas pequeñas. Las columnas pasan a una sola columna y el panel reduce su altura inicial.
Datos dinámicos
Crear productos con arrays y objetos
Ahora agregamos una lista de productos en JavaScript. Cada objeto representa una oferta comercial. Esto permite cambiar textos, precio y mensaje de WhatsApp sin duplicar HTML.
Este bloque convierte los leads guardados en el navegador en un archivo .csv. Ese archivo se puede abrir en Excel, Google Sheets o importar a un CRM. No estamos usando backend todavía: todo ocurre desde JavaScript en el navegador del estudiante.
Línea 1: escucha el clic del botón exportButton. Cuando el estudiante presiona “Exportar CSV”, se ejecuta todo el código que está dentro de la función.
Línea 2: crea la primera fila del archivo. Esta fila no contiene datos de clientes; contiene los nombres de las columnas: producto, nombre, email, WhatsApp y fecha.
Línea 3: recorre state.leads con map(). Cada lead se transforma en una fila de texto separada por comas.
lead.productTitle: guarda qué producto interesó al usuario. Esto es importante porque un lead sin contexto comercial sirve menos.
lead.name, lead.email y lead.whatsapp: son los datos que el formulario capturó dentro de la experiencia WebAR.
lead.createdAt: registra cuándo ocurrió la conversión. Este dato ayuda a revisar campañas, horarios y rendimiento.
Línea 4: une la cabecera y todas las filas usando saltos de línea. Así el navegador entiende el contenido como una tabla.
Línea 5: crea una URL temporal en memoria con URL.createObjectURL(). Esa URL representa el archivo CSV que todavía no existe físicamente en el computador.
new Blob(): empaqueta el texto CSV como archivo descargable. El tipo text/csv le dice al navegador qué clase de archivo estamos generando.
Líneas 6 a 8: crean un enlace HTML temporal, le asignan la URL del CSV y definen el nombre del archivo: leads-webar.csv.
Línea 9: simula un clic sobre ese enlace. Esa acción dispara la descarga sin que el usuario tenga que ver el enlace en pantalla.
Línea 10: libera la URL temporal con URL.revokeObjectURL(). Es una buena práctica para no dejar memoria ocupada innecesariamente.
Analítica básica
Preparar eventos comerciales medibles
En una experiencia comercial debemos medir acciones, no solo visitas. Puedes registrar eventos locales y luego conectarlos con GA4 en otro curso o en una versión avanzada.
Una experiencia WebAR comercial no se evalúa solo por verse bonita. También debe responder preguntas de negocio: cuántas personas escanearon, qué producto vieron, cuántas dejaron datos y cuántas tocaron WhatsApp.
trackEvent(name, params = {}): es una función reutilizable. Recibe el nombre del evento y un objeto con datos adicionales.
name: describe la acción que ocurrió. Por ejemplo: target_found cuando aparece el marcador o lead_submit cuando alguien envía el formulario.
params = {}: crea un objeto vacío por defecto. Así la función puede llamarse con datos extra o sin datos extra.
console.log(): por ahora muestra el evento en la consola. Esto permite probar la lógica antes de conectar herramientas externas.
target.addEventListener("targetFound"): dispara medición cuando MindAR reconoce el marcador. Aquí sabemos que el usuario sí llegó a la parte aumentada.
{ productId: state.selectedProduct.id }: guarda qué producto estaba seleccionado en el momento del escaneo.
leadForm.addEventListener("submit"): mide una conversión más fuerte: el usuario dejó sus datos.
Conexión futura: esta misma función puede cambiarse después para enviar eventos a GA4, Firebase, MySQL o un CRM sin reescribir toda la experiencia.
Template final
Descarga el proyecto completo
Este ZIP contiene el proyecto final del nivel, con estructura de carpetas, código completo, modelo 3D GLB incluido y notas para publicarlo.
Template Nivel intermedio
Proyecto WebAR comercial listo para estudiar, adaptar y subir a un servidor HTTPS.