Genome browser tracks in Python with canvasxpress

Python
canvasxpress
genome
bioinformatics
visualization
Stack coverage and gene-model tracks over genomic coordinates in Python — an interactive, zoomable browser view from one portable spec.
Author

Isaac Neuhaus

Published

October 30, 2026

A genome browser view stacks tracks that share one coordinate axis: coverage, gene models, variants. In Python this usually means pyGenomeTracks or a static matplotlib figure. Here we build an interactive, zoomable view with canvasxpress — from one portable spec.

Install

pip install canvasxpress

The data shape

A Genome figure is a list of tracks that share one coordinate axis — each with a type. Real browser views stack several: here we build four — gene models (box, multi-exon, connected), coverage across three tissues (bar), an expression heatmap (heatmap), and variant markers (triangle).

import random
random.seed(3)

# Gene models: each gene is a set of exon [start, end] intervals, drawn connected.
genes = []
for j in range(4):
    start = 40 + j * 120
    exons = [[start + k * 30, start + k * 30 + 18] for k in range(random.randint(2, 4))]
    genes.append({"id": f"GENE{j+1}", "dir": "right" if j % 2 == 0 else "left",
                  "fill": "rgb(70,130,220)", "outline": "rgb(0,0,0)", "data": exons})

# Coverage and expression: one entry per binned position, three tissues each.
offsets  = [50 + i * 22 for i in range(22)]
coverage = [{"id": f"p{i}", "offset": o,
             "data": [random.randint(8, 45), random.randint(8, 45), random.randint(8, 45)]}
            for i, o in enumerate(offsets)]
express  = [{"id": f"p{i}", "offset": o,
             "data": [round(random.gauss(0, 1), 2) for _ in range(3)]}
            for i, o in enumerate(offsets)]

# Variants: point markers at genomic positions.
snps = [{"id": f"rs{1000+k}", "offset": random.randint(45, 520),
         "fill": "rgb(200,40,40)", "outline": "rgb(0,0,0)"} for k in range(9)]

data = {"tracks": [
    {"name": "Genes (exon models)", "type": "box", "connect": True, "data": genes},
    {"name": "Coverage — Heart / Liver / Kidney", "type": "bar", "height": 120,
     "fill": "rgb(80,80,200)", "outline": "rgb(60,60,60)", "data": coverage},
    {"name": "Expression (z-score)", "type": "heatmap", "height": 90, "data": express},
    {"name": "SNPs", "type": "triangle", "data": snps},
]}

The plot

from canvasxpress.canvas import CanvasXpress
from canvasxpress.render.jupyter import CXNoteBook
from IPython.display import display

config = {
    "graphType": "Genome",
    "background": "rgb(245,245,245)",
    "featureStaggered": True,
    "title": "Multi-track genome browser: genes, coverage, expression and variants",
}

cx = CanvasXpress(render_to="genome", data=data, config=config, width=760, height=520)
for part in CXNoteBook(cx).render():
    display(part)

Scroll/drag to pan and zoom into a region; every track stays locked to the shared coordinate axis, so the gene models, coverage, expression and variants line up position-for-position. Swap each track for your own pileup, annotation and VCF-derived data.

Why “reproducible”

The tracks and config are the figure — one portable JSON spec that renders the identical browser view from R or JavaScript, and can be validated and re-rendered later.

Next