{"spec_id":"volcano-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// volcano-basic: Volcano Plot for Statistical Significance\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-09\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\nconst inkMuted = window.ANYPLOT_THEME === \"dark\" ? \"#A8A79F\" : \"#6B6A63\";\n\n// --- Data: simulated RNA-seq differential expression (deterministic LCG) ----\nlet seed = 42;\nfunction rand() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\nfunction randNormal() {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nconst GENE_STEMS = [\"TP53\", \"BRCA1\", \"EGFR\", \"MYC\", \"KRAS\", \"PTEN\", \"AKT1\", \"PIK3CA\", \"STAT3\", \"NFKB1\", \"VEGFA\", \"CDKN2A\"];\nconst FC_THRESHOLD = 1; // log2 fold change cutoff (2-fold)\nconst P_THRESHOLD = 1.3; // -log10(0.05)\n\nconst notSignificant = [];\nconst upRegulated = [];\nconst downRegulated = [];\n\nfor (let i = 0; i < 650; i += 1) {\n  const log2FoldChange = randNormal() * 1.7;\n  const negLog10Pvalue = Math.max(0, Math.abs(log2FoldChange) * 1.5 + randNormal() * 1.2 + rand() * 0.3);\n  const gene = `${GENE_STEMS[i % GENE_STEMS.length]}${Math.floor(i / GENE_STEMS.length) + 1}`;\n  const point = { name: gene, value: [log2FoldChange, negLog10Pvalue] };\n\n  if (negLog10Pvalue < P_THRESHOLD || Math.abs(log2FoldChange) < FC_THRESHOLD) {\n    notSignificant.push(point);\n  } else if (log2FoldChange > 0) {\n    upRegulated.push(point);\n  } else {\n    downRegulated.push(point);\n  }\n}\n\n// Label the top 3 most significant genes by name to add storytelling focus.\n[...upRegulated, ...downRegulated]\n  .sort((a, b) => b.value[1] - a.value[1])\n  .slice(0, 3)\n  .forEach((point) => {\n    point.label = {\n      show: true,\n      formatter: \"{b}\",\n      position: \"top\",\n      distance: 6,\n      color: t.ink,\n      fontSize: 12,\n      fontWeight: 600,\n    };\n  });\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: \"volcano-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  legend: {\n    top: 56,\n    data: [\"Not significant\", \"Up-regulated\", \"Down-regulated\"],\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n    itemWidth: 14,\n    itemHeight: 14,\n  },\n  tooltip: {\n    trigger: \"item\",\n    formatter: (p) => `${p.data.name}<br/>log2FC: ${p.data.value[0].toFixed(2)}<br/>-log10(p): ${p.data.value[1].toFixed(2)}`,\n  },\n  grid: { left: 90, right: 70, top: 150, bottom: 90 },\n  xAxis: {\n    type: \"value\",\n    name: \"log2(Fold Change)\",\n    nameLocation: \"middle\",\n    nameGap: 42,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"-log10(p-value)\",\n    nameLocation: \"middle\",\n    nameGap: 60,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Not significant\",\n      type: \"scatter\",\n      data: notSignificant,\n      symbolSize: 8,\n      itemStyle: { color: inkMuted, opacity: 0.5, borderColor: t.pageBg, borderWidth: 0.5 },\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        label: { show: false },\n        lineStyle: { color: t.inkSoft, type: \"dashed\", width: 1.5, opacity: 0.6 },\n        data: [{ yAxis: P_THRESHOLD }, { xAxis: -FC_THRESHOLD }, { xAxis: FC_THRESHOLD }],\n      },\n    },\n    {\n      name: \"Down-regulated\",\n      type: \"scatter\",\n      data: downRegulated,\n      symbolSize: 9,\n      itemStyle: { color: t.palette[2], opacity: 0.75, borderColor: t.pageBg, borderWidth: 0.5 },\n    },\n    {\n      name: \"Up-regulated\",\n      type: \"scatter\",\n      data: upRegulated,\n      symbolSize: 9,\n      itemStyle: { color: t.palette[4], opacity: 0.75, borderColor: t.pageBg, borderWidth: 0.5 },\n    },\n  ],\n});\n"}