{"spec_id":"density-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// density-basic: Basic Density Plot\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: server response latency (ms), right-skewed --------------------\nfunction mulberry32(seed) {\n  return function () {\n    seed |= 0;\n    seed = (seed + 0x6d2b79f5) | 0;\n    let x = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n    x = (x + Math.imul(x ^ (x >>> 7), 61 | x)) ^ x;\n    return ((x ^ (x >>> 14)) >>> 0) / 4294967296;\n  };\n}\nconst rand = mulberry32(42);\n\nfunction randNormal() {\n  let u = 0;\n  let v = 0;\n  while (u === 0) u = rand();\n  while (v === 0) v = rand();\n  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);\n}\n\nconst sampleCount = 400;\nconst logMean = Math.log(120); // median latency ~120ms\nconst logStd = 0.35;\nconst latencies = [];\nfor (let i = 0; i < sampleCount; i++) {\n  latencies.push(Math.exp(logMean + logStd * randNormal()));\n}\n\n// --- Gaussian KDE with Silverman's rule-of-thumb bandwidth ----------------\nconst mean = latencies.reduce((a, b) => a + b, 0) / sampleCount;\nconst variance =\n  latencies.reduce((a, b) => a + (b - mean) ** 2, 0) / (sampleCount - 1);\nconst std = Math.sqrt(variance);\nconst bandwidth = 1.06 * std * Math.pow(sampleCount, -1 / 5);\n\nfunction gaussianKernel(u) {\n  return Math.exp(-0.5 * u * u) / Math.sqrt(2 * Math.PI);\n}\n\nconst vmin = Math.min(...latencies);\nconst vmax = Math.max(...latencies);\nconst pad = bandwidth * 3;\nconst gridStart = Math.max(0, vmin - pad);\nconst gridEnd = vmax + pad;\nconst gridCount = 200;\nconst step = (gridEnd - gridStart) / (gridCount - 1);\n\nconst densityCurve = [];\nlet peakDensity = 0;\nfor (let i = 0; i < gridCount; i++) {\n  const x = gridStart + i * step;\n  let sum = 0;\n  for (let j = 0; j < sampleCount; j++) {\n    sum += gaussianKernel((x - latencies[j]) / bandwidth);\n  }\n  const density = sum / (sampleCount * bandwidth);\n  peakDensity = Math.max(peakDensity, density);\n  densityCurve.push([x, density]);\n}\n\n// Rug plot: individual observations as ticks below the curve\nconst rugY = -peakDensity * 0.06;\nconst rugData = latencies.map((v) => [v, rugY]);\n\n// Round the x-axis to clean bounds instead of the raw KDE grid extent\nconst xMax = Math.ceil(gridEnd / 50) * 50;\n\n// Detect the secondary shoulder in the tail: not a true local maximum (the\n// curve keeps descending overall), but a flattening of the descent — a local\n// maximum in the curve's slope. Look for the point in the tail (past the\n// global peak) where the descent visibly slows before continuing downward.\nlet globalPeakIdx = 0;\nfor (let i = 1; i < densityCurve.length; i++) {\n  if (densityCurve[i][1] > densityCurve[globalPeakIdx][1]) globalPeakIdx = i;\n}\n// Widened second derivative (concavity): a shoulder is the point of\n// strongest local \"bulge\" (concave-up interruption of the otherwise\n// concave-down decline). A window of several grid steps rides over the\n// sample-noise wobble that a point-to-point derivative would chase.\nconst w = 8;\nlet bulgeStartIdx = -1;\nlet bestBulge = 0;\nfor (let i = globalPeakIdx + w + 2; i < densityCurve.length - w - 2; i++) {\n  const bulge =\n    densityCurve[i - w][1] - 2 * densityCurve[i][1] + densityCurve[i + w][1];\n  if (bulge > bestBulge) {\n    bestBulge = bulge;\n    bulgeStartIdx = i;\n  }\n}\n// The bulge marks where the descent starts to flatten; walk forward a bit\n// further to land the label on the flattest part of the shelf itself.\nlet shoulderIdx = bulgeStartIdx;\nif (bulgeStartIdx >= 0) {\n  let flattest = Infinity;\n  const scanEnd = Math.min(bulgeStartIdx + 30, densityCurve.length - 2);\n  for (let i = bulgeStartIdx; i <= scanEnd; i++) {\n    const localSlope = Math.abs(\n      densityCurve[i + 1][1] - densityCurve[i - 1][1],\n    );\n    if (localSlope < flattest) {\n      flattest = localSlope;\n      shoulderIdx = i;\n    }\n  }\n}\nconst shoulderPoint = shoulderIdx >= 0 ? densityCurve[shoulderIdx] : null;\n\n// Vertical gradient fill: fuller near the curve, fading toward the baseline.\nfunction hexToRgba(hex, alpha) {\n  const n = parseInt(hex.slice(1), 16);\n  return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;\n}\nconst areaGradient = new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n  { offset: 0, color: hexToRgba(t.palette[0], 0.4) },\n  { offset: 1, color: hexToRgba(t.palette[0], 0.06) },\n]);\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -----------------------------------------------------------------\nconst title =\n  \"Server Response Latency · density-basic · javascript · echarts · anyplot.ai\";\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 30,\n    textStyle: { color: t.ink, fontSize: 20, fontWeight: 500 },\n  },\n  grid: { left: 110, right: 70, top: 110, bottom: 100 },\n  xAxis: {\n    type: \"value\",\n    name: \"Response Latency (ms)\",\n    nameLocation: \"middle\",\n    nameGap: 45,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: 0,\n    max: xMax,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Density\",\n    nameLocation: \"middle\",\n    nameGap: 70,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: rugY * 1.6,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 14,\n      formatter: (v) => (v < 0 ? \"\" : v.toFixed(3)),\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Density\",\n      type: \"line\",\n      data: densityCurve,\n      symbol: \"none\",\n      smooth: false,\n      lineStyle: { color: t.palette[0], width: 3.5 },\n      areaStyle: { color: areaGradient },\n      markPoint: shoulderPoint\n        ? {\n            silent: true,\n            symbol: \"circle\",\n            symbolSize: 8,\n            itemStyle: {\n              color: t.palette[0],\n              borderColor: t.pageBg,\n              borderWidth: 2,\n            },\n            label: {\n              show: true,\n              formatter: `Secondary shoulder\\n~${Math.round(shoulderPoint[0])}ms`,\n              color: t.ink,\n              fontSize: 13,\n              fontWeight: 500,\n              align: \"left\",\n              position: [12, -36],\n              lineHeight: 16,\n            },\n            data: [{ coord: shoulderPoint, name: \"shoulder\" }],\n          }\n        : undefined,\n      z: 2,\n    },\n    {\n      name: \"Observations\",\n      type: \"scatter\",\n      data: rugData,\n      symbol: \"rect\",\n      symbolSize: [1, 10],\n      itemStyle: { color: t.inkSoft, opacity: 0.32 },\n      z: 1,\n    },\n  ],\n});\n"}