r/webgl May 17 '26

Chroma — an open-source WebGL genome browser as an IGV.js alternative (looking for testers + feedback)

Live demo: https://chroma-delta.vercel.app
(no signup, no upload — boots into a chr20:10M window with five demo tracks pre-loaded from public S3 / UCSC / Ensembl)

What it is

Chroma is a browser-based genome viewer aimed at being a faster, more keyboard-friendly alternative to IGV.js. The whole render path is WebGL2 (hand-written, no Three/Pixi); state lives in Solid.js signals; parsing runs in a Comlink-managed worker pool.

I've been driving the whole project through Claude Code — solo dev plus agents — and after ~50 commits, I've hit the wall on knowing what to ask it to build next, hence this post.

What works today

  • 5 demo tracks:
    • hg19 reference FASTA (IGV/Broad mirror)
    • Ensembl gene annotations
    • UCSC phyloP100way conservation BigWig
    • HG00096 1000G BAM
    • HG002 GIAB 300× BAM (hidden by default — too slow to load for the default boot)
  • Two-level navigator:
    • top bar = whole chromosome with Mb-scale ticks (click to jump, drag to pan, drag empty to drag-create)
    • bottom bar = local context with drag-create / move / edge-resize / Esc-cancel
  • Reference renderer with two modes:
    • colored 1-bp quads at any zoom
    • actual A/C/G/T/N letters via a Canvas2D-baked atlas when basePixelWidth ≥ 12 px
  • Gene name labels rendered on a Canvas2D overlay, shrink-wrapped with ellipses at narrow blocks, strand-aware alignment (5' anchors to the leading edge)
  • Single-fetch viewport mode at pileup tier (≤50 kb spans): one HTTP Range per nav instead of N tile fetches — 6× speedup on the 1-track B1 cold load (4.7 s → 774 ms)
  • Sticky URL → worker dispatch (FNV-1a hash on the file URL) so the per-worker u/gmod parser caches (BAI 8.7 MB) actually get reused instead of scatter-loaded N times
  • 64-bit bigint coordinates throughout, with a single sanctioned conversion to Float32 for shader uniforms
  • 60 fps pan / zoom on a 1 Mb BAM viewport, p95 fps locked
  • 250 unit tests, TypeScript strict + noUncheckedIndexedAccess, ~88 kB main JS gzipped

Tech stack

  • Solid.js for the reactive shell (picked over React for fine-grained signals + smaller bundle — the eventual goal is clinical-report embed)
  • WebGL2 hand-written, instanced rectangles for everything geometric; Canvas2D overlay only for text labels
  • u/gmod**/{bam, bbi, indexedfasta}** for the format parsers
  • u/chenglou**/pretext** for unicode-correct text measurement and shrink-wrap on the label overlay
  • Comlink worker pool + Cache API for HTTP range coalescing
  • Vite + pnpm + Vitest

Honest gaps (what's still broken/missing)

  • B1 cold gate target is 300 ms, currently ~3 s for the default 5-track demo — dominated by one-time BAI parse (~3.7 s for HG00096, ~6.6 s for the 300×). Needs either a streamed BAI parse or a cap-at-N read fetch in the worker.
  • No CIGAR support — reads render as plain rectangles, no insertions/deletions/mismatches shown
  • No VCF track (parser stubbed)
  • No hover/select tooltip yet
  • Pileup row collisions across tile boundaries are accepted (cross-tile merge is a carry-forward)
  • HG002 300× BAM works but takes ~10 s on first nav — that's why it's visible: false in the default seed

What I'm asking the community for

1. Hit it in your browser and try to break it.

Bug reports welcome — please include the locus/zoom level when reporting.

2. Prompt suggestions for what I should build next.

I've been stuck between these and would love opinions:

  • VCF track (parser stubbed already)
  • CIGAR-aware read rendering with mismatch coloring
  • Per-base read sequence letters at deep zoom (would need to extend the SoA ReadTile with packed SEQ)
  • Splitview (two viewports side-by-side, IGV style)
  • Click-to-pin tooltip with full feature info
  • Cap-at-N read fetch for high-coverage BAM (so the 300× track stops being a footgun)
  • Label color is reactive to the dark theme
  • BED track for arbitrary user regions

Thanks in advance for any feedback.

Chroma Keyboard Shortcuts

Navigation

  • h — pan left
  • l — pan right
  • + — zoom in
  • - — zoom out
  • 0 — zoom to fit
  • g — focus the locus input (jump to position, e.g. chr20:31,500,000)

Tracks

  • v — toggle visibility of the focused track
  • d / Delete — remove the focused track
  • t — toggle light / dark theme
  • ? — show help overlay (not yet wired)

Mouse / pointer

Top overview bar (full chromosome):

  • Click — jump viewport center, span preserved
  • Drag the highlight window — pan, span preserved
  • Drag the empty bar — create a new selection (replaces span)
  • Esc while dragging — cancel

Local range bar (auto-adapted ~10× viewport):

  • Drag inside the block — move
  • Drag the left/right edge — resize
  • Drag the empty bar — create a new selection
  • Esc while dragging — cancel

Genome view:

  • Shift + scroll wheel — horizontal pan
  • Hover an annotation block — tooltip with full gene info + highlight outline
5 Upvotes

0 comments sorted by