# Network speed (Chartist example)

> Four samples a second as touching bars, with their running average as a line on top.

`src/main.ts`:

```ts
import 'chartist/styles.css';
import './style.css';
import {
  autoScaleAxis,
  axisGrid,
  axisLabels,
  bar,
  cartesianAxes,
  chart,
  line,
  stepAxis
} from 'chartist';
import { liveDomRenderer, smilAnimations } from 'chartist/live';
import { SAMPLES, firstSamples, onSample } from './samples';

// The bars are as wide as one sample, so they touch.
let step = 0;
const container = document.querySelector<HTMLElement>('#chart')!;

const live = chart(container, {
  data: firstSamples,
  axes: cartesianAxes([
    {
      id: 'time',
      side: 'bottom',
      type: stepAxis(),
      showGrid: false,
      showLabel: false
    },
    { id: 'mbps', side: 'left', type: autoScaleAxis(), low: 0, high: 140 }
  ]),
  layers: [
    axisGrid({ axes: ['mbps'] }),
    bar({ series: ['speed'], x: 'time', y: 'mbps' }),
    line({
      series: ['average'],
      x: 'time',
      y: 'mbps',
      align: 'center',
      showPoint: false
    }),
    axisLabels()
  ],
  // Samples are known by their number, so they slide to the left.
  renderer: liveDomRenderer({ key: 'label', animations: smilAnimations() }),
  on: {
    layout: ({ area }) => void (step = area.width / SAMPLES),
    mount: () => container.style.setProperty('--ct-bar-width', `${step}px`)
  }
});

onSample(data => live.update({ data }));
```

`index.html`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script type="module" src="/src/main.ts"></script>
  </head>
  <body>
    <div id="chart" class="ct-golden-section"></div>
  </body>
</html>
```

`src/samples.ts`:

```ts
// Network speed samples, four per second, from a seeded generator, so every
// run measures the same speeds.

let seed = 11;
const random = () => {
  seed = (seed * 16807) % 2147483647;
  return (seed - 1) / 2147483646;
};

/** How many samples the chart shows. */
export const SAMPLES = 60;

let sample = 0;
let speed = 80;
const measure = () => {
  // Wanders around 80 Mbit/s, with a dip now and then.
  speed += (80 - speed) * 0.2 + (random() - 0.5) * 30;
  if (random() < 0.04) {
    speed *= 0.3;
  }
  sample += 1;
  return { label: sample, mbps: Math.max(1, Math.round(speed)) };
};
let samples = Array.from({ length: SAMPLES }, measure);

/** The samples by number, as a speed series and its running average. */
const dataOf = () => {
  const speeds = samples.map(s => s.mbps);
  const average = speeds.map((_, i) => {
    const recent = speeds.slice(Math.max(0, i - 7), i + 1);
    return recent.reduce((sum, value) => sum + value, 0) / recent.length;
  });
  return {
    labels: samples.map(s => s.label),
    series: [
      { name: 'speed', data: speeds },
      { name: 'average', data: average }
    ]
  };
};

/** The last 60 samples. */
export const firstSamples = dataOf();

/** Calls `listener` with the last 60 samples, four times a second. */
export function onSample(listener: (data: typeof firstSamples) => void) {
  setInterval(() => {
    samples = [...samples.slice(1), measure()];
    listener(dataOf());
  }, 250);
}
```

`src/style.css`:

```css
#chart {
  /* Faster than the samples come in, and steady. */
  --ct-update-duration: 200ms;
  --ct-update-easing: linear;
  --ct-line-width: 2px;
}

/* Salmon bars, and the average as a red line on top. */
#chart .ct-bar {
  stroke: var(--ct-series-b, #f05b4f);
}

#chart .ct-line {
  stroke: var(--ct-series-a, #d70206);
}
```
