{"spec_id":"rug-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// rug-basic: Basic Rug Plot\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 92/100 | Created: 2026-07-25\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic fixed-seed LCG) --------------------------\nlet seed = 42;\nfunction lcg() {\n  seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n}\nfunction randNormal(mean, std) {\n  const u1 = lcg() || 1e-9;\n  const u2 = lcg();\n  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n  return mean + z * std;\n}\n\nconst groups = [\"Edge Cache Hit\", \"Regional Cache\", \"Cold Start\"];\n\nconst edgeCacheLatency = Array.from({ length: 140 }, () => randNormal(18, 3));\nconst regionalCacheLatency = Array.from({ length: 70 }, () =>\n  randNormal(42, 6),\n).concat(Array.from({ length: 70 }, () => randNormal(88, 8)));\nconst coldStartLatency = Array.from({ length: 90 }, () => randNormal(230, 55));\n\nconst seriesData = [edgeCacheLatency, regionalCacheLatency, coldStartLatency];\n\n// --- Init ---------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"rug-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 24, fontWeight: 500 },\n  },\n  grid: { left: 190, right: 60, top: 110, bottom: 90 },\n  xAxis: {\n    type: \"value\",\n    name: \"Response Time (ms)\",\n    nameLocation: \"middle\",\n    nameGap: 45,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: 0,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: true, onZero: false, lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  yAxis: {\n    type: \"category\",\n    data: groups,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: true, lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  series: groups.map((name, i) => {\n    const sorted = [...seriesData[i]].sort((a, b) => a - b);\n    const mid = Math.floor(sorted.length / 2);\n    const median =\n      sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n    return {\n      name,\n      type: \"scatter\",\n      data: seriesData[i].map((v) => [v, i]),\n      symbol: \"rect\",\n      symbolSize: [3, 130],\n      itemStyle: { color: t.palette[i], opacity: 0.5 },\n      markPoint: {\n        silent: true,\n        symbol: \"triangle\",\n        symbolSize: 12,\n        symbolOffset: [0, -75],\n        itemStyle: { color: t.palette[i] },\n        label: {\n          show: true,\n          formatter: `median ${median.toFixed(0)}ms`,\n          color: t.ink,\n          fontSize: 12,\n          position: \"top\",\n          offset: [0, -10],\n        },\n        data: [{ coord: [median, i] }],\n      },\n    };\n  }),\n});\n"}