treemap-demo

Browser-based WebGL Treemap Visualizer using deck.gl

Maintained Updated 10mo ago

A browser treemap that renders thousands of nested rectangles on the GPU by handing them to deck.gl, the WebGL framework built for interactive maps. The trees are synthetic; the rendering path is the interesting part. Built with TypeScript, React, and Vite, and shipped as a static site.

Repurposing a map engine

deck.gl exists to draw geographic data — millions of points and polygons placed by longitude and latitude. A treemap has no geography. The demo bridges that gap by treating the screen as the entire world: a screenToNormalized helper maps pixel coordinates onto the Web Mercator range, x across 180 degrees of longitude and y across the 85 degree latitude clamp.

ts
function screenToNormalized(x, y) {
  const cx = window.innerWidth / 2;
  const cy = window.innerHeight / 2;
  const longitude = ((x - cx) / cx) * 180;
  const latitude = -((y - cy) / cy) * 85;
  return [longitude, latitude];
}

Each treemap cell becomes a four-vertex polygon in that fabricated coordinate space, and a single PolygonLayer draws every rectangle in one GPU pass. The payoff is leverage: picking, hover highlighting, and depth-ordered fills all come from deck.gl, so none of that machinery had to be written.

Slice-and-dice layout

The layout is one recursive pass. Each node sorts its children by value, then packs them proportionally along its longer axis — a horizontal split when the rectangle is wider than tall, vertical otherwise. Squarified treemaps work to keep every cell near a square; this one does not, so deep narrow branches collapse into thin slivers. The tradeoff buys speed. Data generation and layout are timed separately with performance.now() and surfaced live in the metrics panel, fast enough that dragging a slider rebuilds the whole tree with no visible stall.

Deterministic generation

There is no real dataset. Trees are grown on demand from a seeded PRNG, so a given seed always yields the same structure. The sliders drive target node count, depth, branching probability, and children per node — and the seed is itself a slider, making the entire space reproducible. Cells are tinted by depth and then jittered per path, so siblings at the same level stay visually distinct rather than flat-shaded into one block.

Tradeoffs

  • No pan or zoom. The deck.gl controller is disabled and the camera is fixed; interaction is limited to hovering a cell to read its full path.

  • Every slider change regenerates and re-lays-out the entire tree, debounced at 50ms, rather than mutating in place.

  • deck.gl is a heavy dependency. The bundle is split into vendor chunks but stays large.

The child_process shim

A transitive dependency reaches for Node child_process, a module the browser does not provide. Rather than patch the dependency, vite.config.ts aliases child_process to a local stub whose spawn throws if it is ever called. The bundler resolves the import, the throwing path never runs, and the static build stays clean.

§05

Gallery

Built with
Demo Experiment Shader TypeScript Visualization Web App
On this page