Como hicimos que el landing de Pulso tuviera pulso de verdad
La historia tecnica detras de la animacion ECG en tiempo real del landing de Pulso: canvas, ondas PQRST y una narrativa que cuenta sola por que necesitas monitoreo.
Si entras a Pulso, lo primero que ves es un widget de "signos vitales" con una linea verde que se dibuja en tiempo real — como un monitor de hospital. No es un GIF. No es un video. Es un canvas que genera cada pixel de la onda cardiaca en vivo.
Este post explica como lo construimos y por que tomamos las decisiones que tomamos.
El problema con la version anterior
La primera version era un SVG con picos triangulares que se movia con CSS translateX. Funcionaba, pero tenia dos problemas:
Se veia falso. Los picos eran triangulos perfectos que no se parecen a nada medico. Cualquiera que haya visto un monitor de hospital notaba que era decorativo.
El loop saltaba. Cuando la animacion CSS llegaba al final y reiniciaba, habia un salto visible. No importa cuanto ajustaras el timing — el cerebro nota la discontinuidad.
No contaba una historia. Era ruido visual de fondo. No comunicaba nada sobre lo que hace Pulso.
La decision: canvas en tiempo real
En vez de mover un dibujo pre-hecho, decidimos dibujar la onda pixel por pixel con <canvas> y requestAnimationFrame. Igual que un monitor de hospital real.
La diferencia es fundamental: no es una animacion que se repite, es una simulacion que corre continuamente. El cursor avanza, dibuja, y borra lo que tiene enfrente. No hay "loop" — hay un proceso continuo.
La onda PQRST
Un latido real en un electrocardiograma tiene 5 componentes. La onda P es un bump suave que representa la activacion de las auriculas. El complejo QRS es el pico dramatico — una caida rapida (Q), un spike agudo hacia arriba (R), y otra caida (S). Es la contraccion de los ventriculos. La onda T es otro bump suave al final, la recuperacion del musculo.
En codigo, esto es un array de puntos normalizados entre 0 y 1:
const BEAT = [
{ x: 0.00, y: 0.55 }, // baseline
{ x: 0.14, y: 0.45 }, // P wave (bump suave)
{ x: 0.18, y: 0.55 }, // vuelta a baseline
{ x: 0.26, y: 0.62 }, // Q dip
{ x: 0.30, y: 0.05 }, // R peak (spike agudo)
{ x: 0.34, y: 0.80 }, // S dip
{ x: 0.38, y: 0.55 }, // baseline
{ x: 0.52, y: 0.38 }, // T wave (bump suave)
{ x: 0.58, y: 0.55 }, // baseline final
{ x: 1.00, y: 0.55 }, // trail hasta el siguiente beat
];
El canvas samplea esta forma a medida que el cursor avanza. Interpola linealmente entre puntos para que la curva sea continua. Un beat dura 0.85 segundos — que corresponde a ~72 BPM, un ritmo cardiaco normal en reposo.
El cursor que borra
Si has visto un monitor Philips o GE en un hospital, el cursor no deja un rastro infinito. Tiene un "gap" — un espacio en negro justo adelante del punto donde esta dibujando. Eso crea la ilusion de que esta "limpiando" la pantalla vieja para escribir datos nuevos.
Lo implementamos con un gap de 20 pixeles: cualquier columna que este dentro de ese rango simplemente no se dibuja. El efecto es sutil pero marca toda la diferencia entre "animacion web" y "esto parece un monitor de verdad".
La narrativa: normal, alerta, recuperacion
Un monitor que solo muestra que todo esta bien no es muy interesante. El objetivo del widget es mostrar el flujo completo de Pulso: monitoreo normal, deteccion de una caida, y alerta automatica.
La secuencia corre en un loop de ~10 segundos:
Segundos 0-6: Todo bien. El ECG verde dibuja ondas PQRST normales. El BPM muestra "72 BPM". Los pacientes estan UP.
Segundo 6: Algo falla. El template del canvas cambia de PQRST a flatline — una linea plana. El color pasa a rojo. El BPM cae a "0". Uno de los pacientes (cliente-saas.io) cambia a "DOWN". El borde del widget pulsa rojo.
Segundo 6.5: La alerta. Aparece un toast: "Alerta enviada a tu@email.com". Esto es lo que Pulso hace por ti — te avisa antes de que tu cliente se entere.
Segundo 8.5: Recuperacion. El ECG vuelve a verde. El BPM sube. El paciente vuelve a estar online. El toast desaparece.
Lo que nos gusta de esta implementacion es que el momento exacto del "paro" varia en cada ciclo. Como el timer de 6 segundos es independiente de la posicion del cursor, a veces el flatline arranca despues de un pico R, a veces en medio del baseline, a veces en la onda T. Se siente organico.
La transicion flatline es instantanea (y eso esta bien)
Cuando el mode cambia de "normal" a "flatline", el canvas no hace un fade ni una transicion suave. Simplemente empieza a dibujar linea plana desde donde estaba el cursor. El resultado es que ves el ultimo latido incompleto y luego... nada. Linea plana.
Esto es exactamente lo que pasa en un monitor real cuando un paciente entra en asistolia. No hay transicion elegante. Es abrupto. Y en el contexto de marketing, esa brusquedad comunica urgencia mejor que cualquier easing curve.
Motion One para todo lo demas
El canvas maneja la onda ECG, pero el resto de las transiciones (BPM, colores de los dots, el toast, el box-shadow del widget) usan Motion One — una libreria de animaciones ligera que funciona con selectores CSS.
La API es simple: animate(selector, keyframes, options) devuelve una promesa (bueno, un thenable — usa .then(), no .finished). Encadenamos las transiciones secuencialmente. El toast de alerta usa un easing custom [.22, 1.2, .36, 1] que le da un ligero bounce al entrar — como si la notificacion llegara con urgencia.
Por que importa esto para un producto de monitoreo
El landing de un SaaS tiene ~5 segundos para comunicar que hace el producto. En vez de explicarlo con texto, lo mostramos con una simulacion que corre sola.
Un visitante que llega al landing ve, sin hacer click en nada: un monitor cardiaco, una caida, una alerta automatica, y una recuperacion. En 10 segundos entiende el flujo completo de Pulso. Eso es mejor que cualquier parrafo de copy.
Si quieres ver la animacion en vivo, entra a pulso.easybits.cloud. Y si quieres que tu infraestructura tenga su propio monitor de signos vitales, el plan Hobby es gratis por 90 dias.