# Custom track and display types

**TL;DR:** a new way to visualize data in an existing view is a display type; a
track type is a new conceptual category of track.

A track owns the high-level identity (an ID, a name, a default set of displays);
a display shows that track inside a particular view and owns the drawing.

```
Track  ─owns→  Display(s)  ─draw→  canvas
```

Tracks are deliberately thin. Every in-tree registration, with the view each
display renders in — `SyntenyTrack` and `VariantTrack` are the two that reach
past `LinearGenomeView`:

<!-- DISPLAY_VIEW_TYPES START -->

<!-- prettier-ignore -->
| Track type | Display type | Renders in |
| --- | --- | --- |
| [](https://jbrowse.org/jb2-staging/docs/config/alignmentstrack) | [](https://jbrowse.org/jb2-staging/docs/config/linearalignmentsdisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/featuretrack) | [](https://jbrowse.org/jb2-staging/docs/config/lineararcdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearbasicdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearmultirowfeaturedisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearscoredisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/gccontenttrack) | [](https://jbrowse.org/jb2-staging/docs/config/lineargccontenttrackdisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/gwastrack) | [](https://jbrowse.org/jb2-staging/docs/config/linearmanhattandisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/hictrack) | [](https://jbrowse.org/jb2-staging/docs/config/linearhicdisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/ldtrack) | [](https://jbrowse.org/jb2-staging/docs/config/ldtrackdisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/maftrack) | [](https://jbrowse.org/jb2-staging/docs/config/linearmafdisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/multiquantitativetrack) | [](https://jbrowse.org/jb2-staging/docs/config/multilinearwiggledisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/quantitativetrack) | [](https://jbrowse.org/jb2-staging/docs/config/linearwiggledisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/referencesequencetrack) | [](https://jbrowse.org/jb2-staging/docs/config/lineargccontentdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearreferencesequencedisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/syntenytrack) | [](https://jbrowse.org/jb2-staging/docs/config/dotplotdisplay) | DotplotView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/lgvsyntenydisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearsyntenydisplay) | LinearSyntenyView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/multiwaysyntenydisplay) | LinearGenomeView |
| [](https://jbrowse.org/jb2-staging/docs/config/varianttrack) | [](https://jbrowse.org/jb2-staging/docs/config/chordvariantdisplay) | CircularView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/lddisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearmultisamplevariantdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearmultisamplevariantmatrixdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearpairedarcdisplay) | LinearGenomeView |
|  | [](https://jbrowse.org/jb2-staging/docs/config/linearvariantdisplay) | LinearGenomeView |

<!-- DISPLAY_VIEW_TYPES END -->

Add a track type only when you need a new conceptual track category, a custom
config schema for that category, or behavior shared across multiple displays.
Register it with `pluginManager.addTrackType(...)`, reusing the base track
config schema.

## When to add a custom display type

- Drawing chrome over the rendered content (e.g. the Y-scale axis in wiggle
  tracks, soft-clip indicators in alignments)
- Adding track-menu items that toggle display-only state (e.g. "Show soft
  clipping", "Modifications")
- Wiring a [custom widget](https://jbrowse.org/jb2-staging/docs/developer_guides/creating_widget) into feature
  clicks (e.g. `VariantFeatureWidget`)
- Bundling a specific adapter with drawing code tuned for it, so users get the
  right combination by default. The generic pairing a plugin skips here is
  `FeatureTrack` with `LinearBasicDisplay`.

The display owns view-specific state, menu items, overlays, and the drawing
itself. A rendering backend is built, never registered: the plugin ABI has no
rendering-backend element type, so the display calls `createRenderingBackend`,
which walks the WebGPU → WebGL2 → Canvas2D ladder, or `createCanvas2DBackend`
when it ships no shader path.

## Display foundations

LGV displays compose one **foundation mixin** on `BaseDisplay`, all sharing
`baseLinearDisplayConfigSchema`. The foundation answers how the display
_fetches_; how it _renders_ is a separate axis on top.

<!-- DISPLAY_FOUNDATIONS START -->

<!-- prettier-ignore -->
| Foundation | Brings | Used by |
| --- | --- | --- |
| `MultiRegionDisplayMixin()` | Per-region fetch + render: the fetch autoruns, `rpcProps()` refetch wiring, and byte gating. The common case. | `LinearAlignmentsDisplay`, `LinearCanvasBaseDisplay`, `LinearMafDisplay`, `LinearManhattanDisplay`, `LinearMultiRowFeatureDisplay`, `LinearReferenceSequenceDisplay`, `LinearScoreDisplay`, `LinearWiggleDisplay`, `MultiLinearWiggleDisplay`, `MultiSampleVariantBaseModel` |
| `GlobalFetchMixin()` | One non-regional dataset with no per-region partitioning, plus the render lifecycle. Installs no fetch autoruns; the display adds its own via `installGlobalFetchAutorun`. | `LinearArcDisplay`, `LinearHicDisplay`, `LinearPairedArcDisplay`, `MultiWaySyntenyDisplay`, `SharedLDModel` |

<!-- DISPLAY_FOUNDATIONS END -->

Both walkthroughs use `MultiRegionDisplayMixin`, the common case. The
[architecture spec](https://github.com/GMOD/jbrowse-components/blob/main/agent-docs/ARCHITECTURE.md#display-stacks)
goes further into why fetch and render are split.

If your display holds onto what the pointer is over, read
[the hover rules](https://github.com/GMOD/jbrowse-components/blob/main/agent-docs/reference/DISPLAY_HOVER.md)
first: a zoom, a side-scroll, an internal scroll and the region-too-large banner
all move content under a stationary cursor without firing a pointer event, so a
stored hit goes on naming what used to be there.

## Cross-cutting mixins

Orthogonal to the foundation — compose any of them on top of whichever one you
picked. Each is one mixin with one overridable hook, and composing it **is** the
opt-in: a display that never overrides the hook gets the default and pays
nothing. Reach for one before writing the behavior yourself; **Composed by** is
read off the `types.compose(...)` calls, so it also answers "does anything else
already do this?"

<!-- CROSS_CUTTING_MIXINS START -->

<!-- prettier-ignore -->
| Mixin | The display supplies | Composed by |
| --- | --- | --- |
| `TrackHeightMixin()` | Internal vertical scroll. `scrollableHeight` (default `Infinity` = doesn't scroll). Brings the clamped `setScrollTop` and the autorun that re-clamps when content shrinks | `LinearAlignmentsDisplay`, `LinearArcDisplay`, `LinearCanvasBaseDisplay`, `LinearHicDisplay`, `LinearMafDisplay`, `LinearManhattanDisplay`, `LinearMultiRowFeatureDisplay`, `LinearPairedArcDisplay`, `LinearReferenceSequenceDisplay`, `LinearScoreDisplay`, `LinearWiggleDisplay`, `MultiLinearWiggleDisplay`, `MultiSampleVariantBaseModel`, `MultiWaySyntenyDisplay`, `SharedLDModel` |
| `LegendMixin()` | A legend the user can turn off. A promotable `showLegend` config slot, whose `promotedBase` sets whether this display type's legend is on by default. Brings the resolved `showLegend` getter, the `showLegendDisplayTypeDefault` pin `showLegendCheckboxItem` takes, and `setShowLegend` | `LinearAlignmentsDisplay`, `LinearCanvasBaseDisplay`, `LinearHicDisplay`, `LinearMultiRowFeatureDisplay`, `MultiLinearWiggleDisplay`, `MultiSampleVariantBaseModel`, `SharedLDModel` |
| `TreeSidebarMixin()` | Row set with a dendrogram sidebar. `sources` (the display rows, named), the three `treeSidebarConfigSchemaFields` slots, plus the `run` callback naming its own clustering RPC and the `sortRows` callback naming what a row carries at a column. Brings `layout` / `clusterTree` / `clusterProvenance` / `treeAreaWidth` / `subtreeFilter`, the `showTree` / `showBranchLength` / `showRowLabels` getters and setters over those slots, the `runClustering` / `clusterRegion` and `sortRowsBy` declarative launch specs `setupTreeSidebarAutoruns` consumes, the `root`, `willClearTree` and `rowOrderIsCustom` getters, and the tree-hover and canvas-ref volatiles the shared sidebar draws through | `LinearMafDisplay`, `LinearMultiRowFeatureDisplay`, `MultiLinearWiggleDisplay`, `MultiSampleVariantBaseModel` |
| `ContextMenuMixin()` | The right-click state of a display whose menu acts on a | `LinearMafDisplay`, `LinearMultiRowFeatureDisplay`, `MultiLinearWiggleDisplay` |
| `RowHeightMixin()` | The two-valued row height every multi-row display has. A `rowHeightConfigSchemaFields` slot whose `0` means fit-to-display-height, and an `autoRowHeight` getter saying what that fit divides. Brings the raw `rowHeight` getter, `setRowHeight`, and the resolved `effectiveRowHeight` every consumer reads | `LinearMafDisplay`, `LinearMultiRowFeatureDisplay`, `MultiSampleVariantBaseModel` |
| `HeightModeMixin()` | Track-height strategy; the one row that must compose **after** `TrackHeightMixin()`, whose `height` and `resizeHeight` it overrides. `growTargetHeight` (default = the raw slot). Brings `heightMode`/`autoHeight`/`fitHeightToDisplay`, `grownHeight`, the reactive `height` override, `setHeightMode`, and the grow-aware `resizeHeight` | `LinearAlignmentsDisplay`, `LinearCanvasBaseDisplay` |
| `ScoreScaleMixin()` | Score axis. Nothing — the config slots. Brings `scaleType` / `autoscaleType` / `minScore` / `maxScore` / `*Bound` / `numStdDev` and their setters, i.e. the whole `ScoreScaleModel` interface the shared score menu and `SetMinMaxDialog` consume | `LinearAlignmentsDisplay`, `WiggleScoreConfigMixin` |

<!-- CROSS_CUTTING_MIXINS END -->

Order matters in one place: `types.compose` gives a collision to the later
argument, so composing `HeightModeMixin()` before `TrackHeightMixin()` silently
leaves grow mode inert. The mixin reports that at attach.

## Walkthroughs

Two end-to-end guides build the same display, differing only in the renderer.
Start with the first:

- [](https://jbrowse.org/jb2-staging/docs/developer_guides/plotting_features) - fetch in a worker, draw with
  Canvas2D. Right for gene-scale tracks.
- [](https://jbrowse.org/jb2-staging/docs/developer_guides/creating_gpu_display) - the same display with a
  `.slang` shader behind it, for roughly ≳100K features per frame.

Both are build-step plugins; [](https://jbrowse.org/jb2-staging/docs/developer_guides/simple_plugin) covers the
scaffold and build setup they assume.

For a worked in-tree display, read `plugins/wiggle/src/LinearWiggleDisplay` for
an overlay drawn over rendered content, or
`plugins/alignments/src/LinearAlignmentsDisplay` for many toggleable menu items
and a custom feature widget.

## See also

- [](https://jbrowse.org/jb2-staging/docs/developer_guides/data_fetching)
- [](https://jbrowse.org/jb2-staging/docs/developer_guides/svg_export)

