Skip to content

Sashimi Plot from Splice Junctions

Sashimi plots, introduced by Katz et al. in Quantitative visualization of alternative exon expression from RNA-seq data, summarize exon coverage as read-density tracks and splice-junction support as arcs. This example recreates igv.js's splice-junction example with the same data, but uses GenomeSpy's declarative grammar to control filtering, arc geometry and style, and label placement.

{
  "description": [
    "Sashimi plot example using splice junction data from IGV.",
    "Data source: https://igv.org/web/release/3.8.0/examples/spliceJunctions.html"
  ],

  "assembly": "hg38",

  "params": [
    {
      "name": "minUniquelyMappedReads",
      "value": 1,
      "bind": {
        "input": "range",
        "min": 0,
        "max": 200,
        "step": 1,
        "name": "Min uniquely mapped reads"
      }
    }
  ],

  "resolve": {
    "scale": { "y": "independent" },
    "axis": { "y": "independent" }
  },

  "layer": [
    {
      "name": "coverage",

      "data": {
        "lazy": {
          "type": "bigwig",
          "url": "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/data/test/splice_junctions//splice_junction_track_test_cases_sampleA.chr15-92835700-93031800.bigWig",
          "pixelsPerBin": 1
        }
      },

      "transform": [{ "type": "filter", "expr": "datum.score > 0" }],

      "encoding": {
        "x": {
          "chrom": "chrom",
          "pos": "start",
          "type": "locus",
          "scale": {
            "domain": [
              { "chrom": "chr15", "pos": 92925000 },
              { "chrom": "chr15", "pos": 92949000 }
            ]
          }
        },
        "x2": { "chrom": "chrom", "pos": "end" },
        "y": {
          "field": "score",
          "type": "quantitative",
          "scale": { "nice": true, "zero": false },
          "title": "Coverage"
        }
      },

      "mark": {
        "type": "rect",
        "color": "lightgray",
        "minWidth": 0.5,
        "minOpacity": 1,
        "tooltip": null
      }
    },
    {
      "name": "splice-junctions",

      "data": {
        "url": "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/data/test/splice_junctions/splice_junction_track_test_cases_sampleA.chr15-92835700-93031800.SJ.out.bed",
        "format": {
          "type": "bed"
        }
      },

      "transform": [
        {
          "type": "filter",
          "expr": "datum.score >= minUniquelyMappedReads"
        },
        {
          "type": "formula",
          "expr": "datum.chromEnd - datum.chromStart",
          "as": "span"
        },
        {
          "type": "formula",
          "expr": "datum.span + (datum.span % 10 - 5) / 10 * datum.span",
          "as": "span"
        }
      ],

      "layer": [
        {
          "name": "arcs",

          "mark": {
            "type": "link",
            "linkShape": "dome"
          },

          "encoding": {
            "x": { "chrom": "chrom", "pos": "chromStart", "type": "locus" },
            "x2": { "chrom": "chrom", "pos": "chromEnd" },
            "y": {
              "field": "span",
              "type": "quantitative",
              "scale": {
                "type": "sqrt",
                "domain": {
                  "expr": "[0, span(domain('x')) * height / width * 5]"
                }
              },
              "axis": null
            },
            "size": {
              "field": "score",
              "type": "quantitative",
              "scale": { "type": "sqrt", "range": [0.1, 2.0] }
            }
          }
        },
        {
          "name": "labels",

          "transform": [
            {
              "type": "formula",
              "expr": "(datum.chromEnd + datum.chromStart) / 2",
              "as": "center"
            }
          ],

          "encoding": {
            "x": { "chrom": "chrom", "pos": "center", "type": "locus" },
            "y": { "field": "span", "type": "quantitative" },
            "text": { "field": "score", "type": "quantitative" }
          },

          "mark": {
            "type": "text",
            "dy": -8,
            "tooltip": false
          }
        }
      ]
    }
  ]
}

The example is based on igv.js's splice-junction sample data for chr15. The source files are hosted in IGV's data repository, and the original demo is available here: splice junction example.

What to notice

The height of each arc is derived from junction span, which is also used for label placement. The arc stroke thickness and label text encode uniquely mapped reads. A deterministic jitter term spreads labels and nudges arc heights to make overlap more unlikely. The junction layer uses an ExprRef-driven y-scale domain that depends on the current x-domain; zooming horizontally also changes the shared arc-and-label heights and label positions, while the coverage track stays independent.

GenomeSpy Features

This example combines several GenomeSpy capabilities in one spec:

  • Lazy data sources load the coverage track from a BigWig file.
  • Parameters bind the minimum uniquely mapped reads threshold to a slider.
  • filter removes low-support junctions.
  • formula derives junction span and the adjusted arc height and deterministic label jitter.
  • link draws the dome-shaped splice arcs.
  • text labels the arcs with their junction scores.
  • ExprRef expressions use the scale functions to couple the junction y-scale domain to the current x-domain, which keeps the shared arc height and label placement proportional to the visible genomic window.
  • layer stacks the coverage and junction tracks while resolve keeps the coverage y scale independent from the junction-layer y scale.