WebGL Heatmap
WebGL-rendered heatmap for large gene expression matrices with zoom, pan, and annotation bars. Based on the OrbisGL andExtendedRectplotGL classes fromperturb-seq.gl, built on top of epiviz.gl andepiviz.heatmap.gl.
Live Demo
Log2 Fold Change
Gene Category
Implementation Guide
Dual-Canvas Architecture
The heatmap uses two overlapping canvases: a WebGL canvas for the cell grid (high-performance rendering of thousands of rectangles) and a 2D canvas overlay for labels, annotations, and interaction handling.
// WebGL canvas: renders heatmap cells as triangles
// Each cell = 2 triangles = 6 vertices
const verts = new Float32Array(rows * cols * 6 * 5);
// 5 floats per vertex: x, y, r, g, b
// 2D canvas overlay: renders labels and annotations
ctx.fillText(rowLabels[r], x, y);
ctx.fillRect(annoX, annoY, ANNO_WIDTH, cellHeight);Color Scale Mapping
Values are mapped to colors using piecewise linear interpolation across a color scale array. Supports diverging (blue-red) and sequential (viridis, magma) scales.
function interpolateColor(scale, t) {
t = clamp(t, 0, 1);
const idx = t * (scale.length - 1);
const lo = Math.floor(idx);
const hi = Math.min(lo + 1, scale.length - 1);
const f = idx - lo;
return [
lerp(scale[lo][0], scale[hi][0], f),
lerp(scale[lo][1], scale[hi][1], f),
lerp(scale[lo][2], scale[hi][2], f),
];
}Viewport-Based Zoom
Zoom state is tracked as a viewport rectangle [x0, y0, x1, y1] in normalized data coordinates. The vertex shader maps this viewport to clip space, and the label renderer filters visible labels based on the same viewport.
Architecture
┌──────────────────────────────────────────────┐
│ React Component │
│ │
│ ┌──────────────────────────────────────────┐│
│ │ WebGL Canvas (bottom layer) ││
│ │ ├─ Triangle pairs per cell ││
│ │ ├─ Color from value → scale mapping ││
│ │ └─ Viewport uniforms for zoom/pan ││
│ ├──────────────────────────────────────────┤│
│ │ 2D Canvas (top layer) ││
│ │ ├─ Row labels (gene names) ││
│ │ ├─ Column labels (rotated) ││
│ │ ├─ Row annotation color bars ││
│ │ └─ Mouse event handling ││
│ └──────────────────────────────────────────┘│
│ │
│ Data: Float32Array (rows × cols) │
│ View: { x0, y0, x1, y1 } normalized rect │
└──────────────────────────────────────────────┘Production Architecture (perturb-seq.gl)
The production version uses web workers for off-main-thread rendering viaepiviz.gl, Zarr-based lazy data loading for matrices too large to fit in memory, and synchronized viewport state across multiple linked heatmap instances.
Source Reference
perturb-seq.gl/src/OrbisGL.ts- Main WebGL wrapperperturb-seq.gl/src/ExtendedRectplotGL.js- Heatmap spec generationperturbseqapp/src/components/Heatmap/Heatmap.js- React integrationperturbseqapp/src/components/Heatmap/HeatmapCanvas.js- SVG overlay
Use Cases
Perturbation Screens
Visualize gene knockout/knockdown effects across thousands of measured response genes. Identify pathway-level patterns in perturbation data.
Correlation Matrices
Display gene-gene or sample-sample correlation matrices with hierarchical clustering and annotation tracks.
Differential Expression
Show log-fold-change values across conditions with diverging color scales to highlight up- and down-regulated genes.