HTML
<div class="ecg-stage">
<div class="ecg-card">
<div class="ecg-header">
<div class="ecg-system">SRV-PROD-01 · Heartbeat</div>
<div class="ecg-status">Normal</div>
</div>
<div class="ecg-viewport">
<svg class="ecg-svg" width="880" height="68" viewBox="0 0 880 68" style="animation-duration: 0.833s" aria-hidden="true">
<polyline
fill="none"
stroke="#00e5a0"
stroke-width="2"
stroke-linejoin="round"
stroke-linecap="round"
opacity="0.9"
points="
0,48 58,48
64,42 70,38 76,42 82,48
132,48 136,53 139,10 143,60 147,48
170,48 185,40 200,36 215,40 230,48
300,48
358,48
364,42 370,38 376,42 382,48
432,48 436,53 439,10 443,60 447,48
470,48 485,40 500,36 515,40 530,48
600,48
658,48
664,42 670,38 676,42 682,48
732,48 736,53 739,10 743,60 747,48
770,48 785,40 800,36 815,40 830,48
880,48
"/>
<line x1="0" y1="48" x2="880" y2="48" stroke="#00e5a0" stroke-width="0.3" opacity="0.2"/>
</svg>
</div>
<div class="ecg-metrics">
<div class="ecg-bpm">
<div class="ecg-bpm-val" id="ecg-bpm">72</div>
<div class="ecg-bpm-unit">BPM</div>
</div>
<div class="ecg-secondary">
<div class="ecg-sec-val">P–R 160ms</div>
<div class="ecg-sec-label">Interval</div>
</div>
<div class="ecg-secondary">
<div class="ecg-sec-val">QRS 88ms</div>
<div class="ecg-sec-label">Duration</div>
</div>
</div>
</div>
</div> CSS
.ecg-stage {
background: #050c12;
padding: 48px 32px;
display: flex;
flex-direction: column;
gap: 20px;
justify-content: center;
align-items: center;
min-height: 360px;
}
.ecg-card {
background: #0a1520;
border: 1px solid #0f2030;
border-radius: 8px;
padding: 20px 24px;
width: 100%;
max-width: 440px;
}
.ecg-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
}
.ecg-system {
font-family: ui-monospace, "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.24em;
text-transform: uppercase;
color: #00e5a0;
opacity: 0.7;
}
.ecg-status {
font-family: system-ui, "Bricolage Grotesque", sans-serif;
font-size: 10px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: #00e5a0;
padding: 3px 10px;
border: 1px solid rgba(0,229,160,0.3);
border-radius: 20px;
animation: ecg-glow 3s ease-in-out infinite;
}
@keyframes ecg-glow {
0%, 100% { box-shadow: 0 0 0 rgba(0,229,160,0.3); }
50% { box-shadow: 0 0 8px rgba(0,229,160,0.3); }
}
.ecg-viewport {
width: 100%;
height: 68px;
overflow: hidden;
position: relative;
margin-bottom: 14px;
}
.ecg-svg {
display: block;
animation: ecg-scroll 0.833s linear infinite;
}
@keyframes ecg-scroll {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
.ecg-svg, .ecg-status { animation: none; }
}
.ecg-metrics {
display: flex;
align-items: flex-end;
gap: 24px;
padding-top: 12px;
border-top: 1px solid #0f2030;
}
.ecg-bpm {
display: flex;
align-items: baseline;
gap: 5px;
}
.ecg-bpm-val {
font-family: ui-monospace, "JetBrains Mono", monospace;
font-size: 42px;
font-weight: 700;
color: #00e5a0;
line-height: 0.9;
letter-spacing: -0.02em;
}
.ecg-bpm-unit {
font-family: system-ui, "Bricolage Grotesque", sans-serif;
font-size: 12px;
letter-spacing: 0.12em;
color: rgba(0,229,160,0.6);
text-transform: uppercase;
}
.ecg-secondary {
display: flex;
flex-direction: column;
gap: 4px;
margin-left: auto;
text-align: right;
}
.ecg-sec-val {
font-family: ui-monospace, "JetBrains Mono", monospace;
font-size: 14px;
color: rgba(0,229,160,0.8);
letter-spacing: 0.04em;
}
.ecg-sec-label {
font-family: system-ui, "Bricolage Grotesque", sans-serif;
font-size: 9px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: rgba(0,229,160,0.4);
} JS
// BPM drifts slightly around 72 so the badge feels alive. Animation
// duration is tied to BPM literally — 60 / bpm seconds per cycle.
(function () {
var bpmEl = document.getElementById('ecg-bpm');
var ecgSvg = document.querySelector('.ecg-svg');
var bpm = 72;
setInterval(function () {
bpm += (Math.random() - 0.5) * 2;
bpm = Math.max(68, Math.min(78, bpm));
var rounded = Math.round(bpm);
bpmEl.textContent = rounded;
ecgSvg.style.animationDuration = (60 / bpm).toFixed(3) + 's';
}, 4000);
})();