// ── Reproducible synthetic data (seeded PRNG, no network) ──────────────
function mulberry32(a) {
  return function () {
    a |= 0
    a = (a + 0x6d2b79f5) | 0
    var t = Math.imul(a ^ (a >>> 15), 1 | a)
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}
var rng = mulberry32(20240607)

function gauss(mean, sd) {
  var u = 0,
    v = 0
  while (u === 0) u = rng()
  while (v === 0) v = rng()
  return mean + sd * Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v)
}

// Individual API response-time readings per data-centre region. Compact
// strip-plot form: { x: 'Region', y: [reading, reading, ...] }. Every reading
// becomes its own hoverable marker, scattered horizontally within its band.
var REGIONS = [
  { name: 'Frankfurt', mean: 120, sd: 22 },
  { name: 'Mumbai', mean: 182, sd: 30 },
  { name: 'Oregon', mean: 96, sd: 18 },
  { name: 'Singapore', mean: 158, sd: 26 },
  { name: 'Virginia', mean: 82, sd: 16 },
]

function readings(region) {
  var out = []
  for (var i = 0; i < 150; i++) {
    out.push(Math.max(20, Math.round(gauss(region.mean, region.sd))))
  }
  return out
}

var options = {
  series: [
    {
      name: 'Frankfurt',
      data: [{ x: 'Frankfurt', y: readings(REGIONS[0]) }],
    },
    {
      name: 'Mumbai',
      data: [{ x: 'Mumbai', y: readings(REGIONS[1]) }],
    },
    {
      name: 'Oregon',
      data: [{ x: 'Oregon', y: readings(REGIONS[2]) }],
    },
    {
      name: 'Singapore',
      data: [{ x: 'Singapore', y: readings(REGIONS[3]) }],
    },
    {
      name: 'Virginia',
      data: [{ x: 'Virginia', y: readings(REGIONS[4]) }],
    },
  ],
  chart: {
    type: 'scatter',
    height: 460,
  },
  title: {
    text: 'API response time by region',
    align: 'center',
  },
  plotOptions: {
    scatter: {
      // Spread the readings in each band horizontally (axis units; 1 = one
      // band step). Set `distributed: true` to colour each band differently.
      jitter: {
        enabled: true,
        x: 0.35,
      },
    },
  },
  markers: {
    size: 3,
    strokeWidth: 0,
  },
  fill: {
    opacity: 0.6,
  },
  legend: { show: false },
  xaxis: {
    // strip plots read better without the vertical crosshair line
    crosshairs: { show: false },
  },
  yaxis: {
    title: { text: 'Response time (ms)' },
    labels: {
      formatter: function (v) {
        return Math.round(v)
      },
    },
  },
  tooltip: {
    // anchor the tooltip to the hovered band so it stays put instead of
    // chasing each individual marker
    shared: true,
    intersect: false,
    y: {
      formatter: function (v) {
        return Math.round(v) + ' ms'
      },
    },
  },
}

var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()
Scatter with Jitter - JavaScript Scatter Charts | ApexCharts.js | ApexCharts.js