Live sparkline
Live sparkline — new points stream in, old ones slide out
Techniques: fixed-window data buffer · points attribute rewrite · transition retrigger · prefers-reduced-motion
<!--
============================================================
SVGarden — https://www.svgarden.dev
Author : Simon-Pierre Boucher
Contact: contact@spboucher.ai
File : snippets/charts/chart-sparkline-live.html
Desc : Live sparkline — new points stream in, old ones slide out
============================================================
-->
<div class="sg-chart-sparkline-live" style="--sg-color:#7F77DD; --sg-size:220px; --sg-duration:0.7s;">
<svg viewBox="0 0 220 60" role="img" aria-label="Live-updating sparkline of streaming values">
<title>Live sparkline</title>
<line class="sg-chart-sparkline-live-base" x1="5" y1="58" x2="215" y2="58" />
<g class="sg-chart-sparkline-live-win">
<polygon class="sg-chart-sparkline-live-area" points="" />
<polyline class="sg-chart-sparkline-live-line" points="" />
</g>
<circle class="sg-chart-sparkline-live-dot" cx="215" cy="30" r="3" />
</svg>
</div>
<script>
document.querySelectorAll('.sg-chart-sparkline-live:not([data-sg-init])').forEach(function (root) {
root.setAttribute('data-sg-init', '');
var line = root.querySelector('.sg-chart-sparkline-live-line');
var area = root.querySelector('.sg-chart-sparkline-live-area');
var win = root.querySelector('.sg-chart-sparkline-live-win');
var dot = root.querySelector('.sg-chart-sparkline-live-dot');
var N = 22, data = [30];
for (var i = 1; i < N; i++) data.push(Math.max(8, Math.min(52, data[i - 1] + Math.random() * 14 - 7)));
function render() {
var pts = data.map(function (v, i) { return (5 + i * 10) + ',' + (58 - v).toFixed(1); }).join(' ');
line.setAttribute('points', pts);
area.setAttribute('points', pts + ' 215,58 5,58');
dot.setAttribute('cy', (58 - data[N - 1]).toFixed(1));
}
render();
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
function tick() {
data.push(Math.max(8, Math.min(52, data[N - 1] + Math.random() * 14 - 7)));
data.shift();
win.style.transition = 'none';
render();
win.style.transform = 'translateX(10px)';
requestAnimationFrame(function () {
var s = parseFloat(getComputedStyle(root).getPropertyValue('--sg-duration')) || 0.7;
win.style.transition = 'transform ' + s + 's linear';
win.style.transform = 'translateX(0)';
setTimeout(tick, s * 1000);
});
}
tick();
});
</script>
<style>
.sg-chart-sparkline-live {
width: var(--sg-size);
}
.sg-chart-sparkline-live svg {
display: block;
width: 100%;
overflow: hidden;
}
.sg-chart-sparkline-live-base {
stroke: rgba(128, 128, 128, 0.3);
stroke-width: 1;
}
.sg-chart-sparkline-live-line {
fill: none;
stroke: var(--sg-color);
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.sg-chart-sparkline-live-area {
fill: var(--sg-color);
opacity: 0.12;
}
.sg-chart-sparkline-live-dot {
fill: var(--sg-color);
transform-box: fill-box;
transform-origin: center;
animation: sg-chart-sparkline-live-pulse 1.4s ease-in-out infinite;
}
@keyframes sg-chart-sparkline-live-pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.7); opacity: 0.55; }
}
</style>