Skip to content

HeatmapChart

Defined in: packages/pixi-charts/src/charts/HeatmapChart.ts:163

Heatmap chart — categorical × categorical grid, coloured by a quantitative value field.

Rendering: texture-from-buffer (one draw call)

Section titled “Rendering: texture-from-buffer (one draw call)”

For an MxN grid of coloured cells, three options were considered:

  • Graphics rectangles, one per cell — collapses past ~10⁴ cells; not viable as a default.
  • Custom WebGL shader — fastest possible, but adds a shader-compilation/debugging surface not justified for v1. Same posture ScatterChart took toward shaders.
  • Texture-from-buffer — allocate a Uint8ClampedArray of (width * height * 4) RGBA bytes where width/height are the grid dimensions (not pixel dimensions), wrap it in a Texture via a BufferImageSource, render as a single Sprite stretched across the plot area. One draw call regardless of grid size, GPU- native scaling, and resize is free — only the sprite’s pixel width/ height change, the texture itself is reused.

The choice is texture-from-buffer. Custom shaders remain a documented future optimisation.

Crisp cells via scaleMode: 'nearest'. When the (small) texture is stretched across the (much larger) plot area, the default linear interpolation produces fuzzy gradients across cell borders — wrong for discrete heatmap cells. Nearest-neighbour sampling (the PIXI v8 string literal 'nearest', set on the BufferImageSource’s scaleMode) gives crisp cell edges.

Both axes are band scales. This is the first chart in the library with bandAdapter on x AND y simultaneously. Heatmaps in v1 do not auto-bin continuous data; consumers pre-bin into discrete categories and pass encoding.x.type / encoding.y.type as 'categorical' (enforced by the validator — see spec/validate.ts:requireHeatmapEncoding). A quantitative-pre-binned axis is treated as ordered categorical by simply stringifying the values.

Y convention: index 0 at top of plot. The first y category in insertion order renders at the top edge of the plot (screen-top). The y-axis labels run top-to-bottom in the same order. This matches the common heatmap convention (calendars, confusion matrices). The texture buffer is laid out row-major with (yIdx * width + xIdx) * 4 — combined with a band y-scale ranging [0, plotHeight], yIdx 0 ends up at the top.

Always quantitative (validator-enforced); default scheme viridis (perceptually uniform, colourblind-safe). Each cell’s normalised value t = (value - min) / (max - min) is passed to getSequentialColor to yield a PIXI 0xRRGGBB. A span of zero (every value identical) maps everything to t = 0.5 so the heatmap still renders something meaningful rather than NaN-coloured cells.

If (x, y) pairs are missing from data, those buffer positions stay (0, 0, 0, 0) — fully transparent. The container’s background shows through. The hit-tester returns null for sparse positions so tooltips don’t show stale data.

Always a continuous gradient (Legend type: 'continuous') sized to the value-field’s [min, max]. Positioned top-right of the plot, same placement as ScatterChart’s continuous legend.

Heatmaps don’t animate well in v1: a left-to-right reveal (Line/Area style) doesn’t fit a grid, a per-cell cascade looks gimmicky at any realistic size, and a whole-grid alpha fade adds little value over a straight static render. Deliberately omitted. update({ animate: true }) is accepted but ignored — heatmap updates always snap.

const chart = new HeatmapChart({ container, spec });
await chart.init(); // creates the PIXI Application AND does the first render
chart.update(newRows); // reuses the sprite/texture when grid dims unchanged
chart.destroy(); // idempotent; frees the GPU texture + primitives

Resize keeps the sprite/texture and just changes the sprite’s pixel dimensions. Chart.update reuses the same sprite. When the grid dimensions match (same category sets), the existing buffer is rewritten in place and uploaded via source.update() — no GPU reallocation. When grid dimensions change, the old Texture + BufferImageSource are destroyed before the new ones are allocated, preventing GPU memory growth across many updates.

For most use cases, prefer the declarative render entry point — use this class directly only when you need fine-grained lifecycle control.

new HeatmapChart(opts): HeatmapChart

Defined in: packages/pixi-charts/src/charts/HeatmapChart.ts:218

HeatmapChartOptions

HeatmapChart

Chart.constructor

get destroyed(): boolean

Defined in: packages/pixi-charts/src/core/Chart.ts:155

true once destroy has run.

boolean

Chart.destroyed


get initialized(): boolean

Defined in: packages/pixi-charts/src/core/Chart.ts:160

true once init has completed.

boolean

Chart.initialized

destroy(): void

Defined in: packages/pixi-charts/src/charts/HeatmapChart.ts:243

Destroy every owned primitive plus the GPU-backed texture, in addition to the base-class teardown. Idempotent.

void

Chart.destroy


init(): Promise<void>

Defined in: packages/pixi-charts/src/charts/HeatmapChart.ts:232

Override of Chart.init: after the PIXI Application is ready, runs the first render so the spec dispatcher hands back a fully- rendered chart.

Promise<void>

Chart.init


off(_event, handler): void

Defined in: packages/pixi-charts/src/core/Chart.ts:259

Remove a previously registered click handler. No-op if the handler wasn’t registered, or after destroy.

"click"

ChartEventHandler

void

Chart.off


on(_event, handler): () => void

Defined in: packages/pixi-charts/src/core/Chart.ts:248

Register a handler for a chart event. Returns an unsubscribe function; calling it (or off) removes the handler.

Currently only 'click' is supported. Handlers receive a ChartClickEvent describing the clicked datum, its index, the plot-area-local pixel position, and (for multi-series charts) the series name. The library reports the click; what it means is up to the consumer — pair with update to build drilldown.

Handlers are cleared automatically on destroy.

"click"

ChartEventHandler

() => void

const off = chart.on('click', (event) => {
console.log('clicked', event.datum, 'at index', event.index);
});
// later: off(); // or: chart.off('click', handler);

Chart.on


update(newData, options?): void

Defined in: packages/pixi-charts/src/core/Chart.ts:218

Swap the chart’s data without recreating the WebGL context. Reuses the existing PixiJS application, scales infrastructure, axes, legend, and interaction layer; recomputes scales, geometry, and hit-testing from the new data.

update() cannot change the chart type, encoding, orientation, donut inner radius, or color scheme — only the rows in data. To change any of those, destroy() this chart and construct a fresh one.

Synchronous. The PixiJS application is already initialised; updating is just recompute + redraw.

readonly Record<string, unknown>[]

UpdateOptions

void

If called before init has resolved.

If called after destroy, this is a silent no-op.

Chart.update