{"spec_id":"genome-track-multi","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// genome-track-multi: Genome Track Viewer\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-25\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: chr7:2,050,000-2,075,000 (25 kb window), fictional ZNF-142 locus --\n\nconst REGION_START = 2050000;\nconst REGION_END = 2075000;\nconst GENE_STRAND = \"+\";\nconst GENE_Y = 1;\nconst strandArrow = GENE_STRAND === \"+\" ? \"▶\" : \"◀\";\n\n// Gene model — 6 exons separated by 5 introns\nconst exons = [\n  [2050400, 2050650],\n  [2053100, 2053420],\n  [2057800, 2058050],\n  [2061200, 2061600],\n  [2065400, 2065700],\n  [2070800, 2071250],\n];\nconst introns = exons.slice(0, -1).map((exon, i) => [exon[1], exons[i + 1][0]]);\n\n// Coverage — read depth sampled every 200bp, higher over exons\nfunction gaussianBump(x, center, sigma, amplitude) {\n  return amplitude * Math.exp(-((x - center) ** 2) / (2 * sigma ** 2));\n}\nconst coverage = [];\nfor (let x = REGION_START; x <= REGION_END; x += 200) {\n  let depth = 9;\n  exons.forEach(([s, e]) => {\n    depth += gaussianBump(x, (s + e) / 2, 350, 42);\n  });\n  coverage.push([x, Math.round(depth)]);\n}\n\n// Variants — SNPs and indels with a Phred-like quality score (0-60)\nconst variants = [\n  { pos: 2050900, quality: 34, type: \"SNP\" },\n  { pos: 2051700, quality: 18, type: \"indel\" },\n  { pos: 2052600, quality: 47, type: \"SNP\" },\n  { pos: 2054200, quality: 12, type: \"SNP\" },\n  { pos: 2056100, quality: 29, type: \"indel\" },\n  { pos: 2058700, quality: 55, type: \"SNP\" },\n  { pos: 2059500, quality: 22, type: \"SNP\" },\n  { pos: 2062400, quality: 41, type: \"indel\" },\n  { pos: 2063900, quality: 15, type: \"SNP\" },\n  { pos: 2066200, quality: 38, type: \"SNP\" },\n  { pos: 2067600, quality: 26, type: \"indel\" },\n  { pos: 2069300, quality: 51, type: \"SNP\" },\n  { pos: 2072100, quality: 19, type: \"SNP\" },\n  { pos: 2073400, quality: 33, type: \"indel\" },\n];\nconst snpPoints = variants.filter((v) => v.type === \"SNP\").map((v) => [v.pos, v.quality]);\nconst indelPoints = variants.filter((v) => v.type === \"indel\").map((v) => [v.pos, v.quality]);\n\n// Regulatory elements\nconst regulatory = [\n  { start: 2050050, end: 2050380, kind: \"promoter\" },\n  { start: 2054700, end: 2055300, kind: \"enhancer\" },\n  { start: 2068400, end: 2069100, kind: \"enhancer\" },\n];\n\n// --- Track layout: 4 stacked panes sharing one x-axis -----------------------\n\nconst shading = Highcharts.color(t.elevatedBg).setOpacity(0.5).get();\nconst trackTitleStyle = { color: t.inkSoft, fontSize: \"16px\", fontWeight: \"600\" };\n\nfunction trackAxis(title, top, height, extra) {\n  return Object.assign(\n    {\n      title: { text: title, align: \"high\", rotation: 0, textAlign: \"left\", x: 0, y: -6, style: trackTitleStyle },\n      top,\n      height,\n      offset: 0,\n      lineWidth: 0,\n      tickLength: 0,\n      gridLineColor: t.grid,\n      labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    },\n    extra\n  );\n}\n\nconst yAxis = [\n  trackAxis(\"Genes\", \"0%\", \"18%\", {\n    min: 0,\n    max: 2,\n    gridLineWidth: 0,\n    labels: { enabled: false },\n    plotBands: [{ from: 0, to: 2, color: shading }],\n  }),\n  trackAxis(\"Coverage\", \"24%\", \"26%\", { min: 0, max: 60, gridLineWidth: 1 }),\n  trackAxis(\"Variants\", \"55%\", \"23%\", {\n    min: 0,\n    max: 65,\n    gridLineWidth: 1,\n    plotBands: [{ from: 0, to: 65, color: shading }],\n  }),\n  trackAxis(\"Regulatory\", \"83%\", \"17%\", { min: 0, max: 2, gridLineWidth: 0, labels: { enabled: false } }),\n];\n\n// --- Series ------------------------------------------------------------\n\nconst series = [];\n\n// Gene track: thick segments = exons, thin segments = introns, arrows = strand\nexons.forEach(([s, e], i) => {\n  series.push({\n    type: \"line\",\n    yAxis: 0,\n    data: [\n      [s, GENE_Y],\n      [e, GENE_Y],\n    ],\n    color: t.palette[0],\n    lineWidth: 22,\n    linecap: \"square\",\n    marker: { enabled: false },\n    enableMouseTracking: false,\n    name: \"Exon\",\n    showInLegend: i === 0,\n  });\n});\nintrons.forEach(([s, e], i) => {\n  series.push({\n    type: \"line\",\n    yAxis: 0,\n    data: [\n      [s, GENE_Y],\n      [e, GENE_Y],\n    ],\n    color: t.palette[0],\n    lineWidth: 3,\n    linecap: \"square\",\n    marker: { enabled: false },\n    enableMouseTracking: false,\n    name: \"Intron\",\n    showInLegend: i === 0,\n  });\n});\nseries.push({\n  type: \"scatter\",\n  yAxis: 0,\n  data: introns.map(([s, e]) => ({ x: (s + e) / 2, y: GENE_Y })),\n  marker: { enabled: false },\n  enableMouseTracking: false,\n  showInLegend: false,\n  dataLabels: {\n    enabled: true,\n    format: strandArrow,\n    allowOverlap: true,\n    verticalAlign: \"middle\",\n    y: 1,\n    style: { color: t.palette[0], fontSize: \"13px\", fontWeight: \"700\", textOutline: \"none\" },\n  },\n});\n\n// Coverage track: filled area of read depth\nseries.push({\n  type: \"area\",\n  yAxis: 1,\n  name: \"Read Depth\",\n  data: coverage,\n  color: t.palette[1],\n  fillOpacity: 0.35,\n  lineWidth: 2,\n  marker: { enabled: false },\n  threshold: 0,\n  showInLegend: true,\n});\n\n// Variant track: lollipops (thin stem + marker head), quality encodes height\nseries.push({\n  type: \"column\",\n  yAxis: 2,\n  data: snpPoints,\n  color: t.palette[2],\n  pointWidth: 3,\n  borderWidth: 0,\n  enableMouseTracking: false,\n  showInLegend: false,\n});\nseries.push({\n  type: \"scatter\",\n  yAxis: 2,\n  name: \"SNP\",\n  data: snpPoints,\n  color: t.palette[2],\n  marker: { symbol: \"circle\", radius: 7, lineWidth: 0 },\n  showInLegend: true,\n});\nseries.push({\n  type: \"column\",\n  yAxis: 2,\n  data: indelPoints,\n  color: t.palette[3],\n  pointWidth: 3,\n  borderWidth: 0,\n  enableMouseTracking: false,\n  showInLegend: false,\n});\nseries.push({\n  type: \"scatter\",\n  yAxis: 2,\n  name: \"Indel\",\n  data: indelPoints,\n  color: t.palette[3],\n  marker: { symbol: \"diamond\", radius: 8, lineWidth: 0 },\n  showInLegend: true,\n});\n\n// Regulatory track: colored rectangles per element kind\nconst regColors = { promoter: t.palette[5], enhancer: t.palette[6] };\nconst regSeen = new Set();\nregulatory.forEach((r) => {\n  const first = !regSeen.has(r.kind);\n  regSeen.add(r.kind);\n  series.push({\n    type: \"line\",\n    yAxis: 3,\n    data: [\n      [r.start, GENE_Y],\n      [r.end, GENE_Y],\n    ],\n    color: regColors[r.kind],\n    lineWidth: 22,\n    linecap: \"square\",\n    marker: { enabled: false },\n    enableMouseTracking: false,\n    name: r.kind === \"promoter\" ? \"Promoter\" : \"Enhancer\",\n    showInLegend: first,\n  });\n});\n\n// --- Chart ---------------------------------------------------------------\n\nHighcharts.chart(\"container\", {\n  chart: { type: \"line\", backgroundColor: \"transparent\", animation: false, style: { fontFamily: \"inherit\" } },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"genome-track-multi · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    min: REGION_START,\n    max: REGION_END,\n    title: { text: \"Genomic Position — chr7 (bp)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    gridLineWidth: 1,\n    labels: {\n      style: { color: t.inkSoft, fontSize: \"14px\" },\n      formatter() {\n        return Highcharts.numberFormat(this.value, 0, \".\", \",\");\n      },\n    },\n  },\n  yAxis,\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    formatter() {\n      const pos = Highcharts.numberFormat(this.x, 0, \".\", \",\");\n      if (this.series.type === \"line\") return `<b>${this.series.name}</b><br/>pos ${pos}`;\n      return `<b>${this.series.name}</b><br/>pos ${pos}<br/>value ${this.y}`;\n    },\n  },\n  plotOptions: {\n    series: { animation: false, states: { hover: { enabled: true } } },\n  },\n  series,\n});\n"}