Skip to content

Gallery / charts / Live sparkline

Live sparkline

Live sparkline — new points stream in, old ones slide out

charts advanced chartsparklinelivestreamingjs
Live preview

Techniques: fixed-window data buffer · points attribute rewrite · transition retrigger · prefers-reduced-motion

Customizeupdates preview, code & clipboard
#7F77DD
220px
0.7s
Code snippets/charts/chart-sparkline-live.html
<!--
  ============================================================
  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>

More charts

\n","slug":"chart-sparkline-live","vars":[{"name":"--sg-color","unit":"","initial":"#7F77DD"},{"name":"--sg-size","unit":"px","initial":"220px"},{"name":"--sg-duration","unit":"s","initial":"0.7s"}]}