Skip to content

Heatmap Layer ​

Continuous density visualization using Kernel Density Estimation (KDE). Creates smooth color gradients representing data concentration, ideal for identifying "hot zones" on the ice.

Basic Heatmap ​

The simplest usage creates a smooth density visualization of shot locations.

typescript
import { Rink, NHLDataManager } from "d3-hockey";

// 1. Load Data
const manager = await NHLDataManager.fromGameId("2022020195");
const shots = manager.getAllEvents({ shotsOnly: true });

// 2. Render Rink with Heatmap Layer
new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 5, // Smoothing radius in feet
  maxOpacity: 0.8, // Peak density opacity
});

Bandwidth Comparison ​

The bandwidth parameter controls how "spread out" each point's influence is. Lower values create tighter, more detailed heatmaps, while higher values create smoother, more generalized views.

typescript
new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 3, // Tight, detailed view
  maxOpacity: 0.85,
  threshold: 0.08,
});
typescript
new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 10, // Smooth, generalized view
  maxOpacity: 0.75,
});

Custom Color Scales ​

Use D3 color scales to customize the heatmap appearance.

typescript
import * as d3 from "d3";

new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 5,
  colorScale: d3.scaleSequential(d3.interpolateViridis),
  maxOpacity: 0.9,
});
typescript
new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 5,
  colorScale: d3.scaleSequential(d3.interpolateInferno),
  maxOpacity: 0.85,
});

Threshold Filtering ​

Use threshold to filter out low-density areas and focus on hot zones.

typescript
new Rink("#container").render().addHeatmap(shots, {
  bandwidth: 4,
  threshold: 0.2, // Only show top 80% density
  maxOpacity: 0.9,
});

Heatmap vs Hexbin Comparison ​

Compare the continuous heatmap with discrete hexagonal binning.

typescript
// Heatmap - smooth, continuous density
new Rink("#heatmap").render().addHeatmap(shots, {
  bandwidth: 5,
  maxOpacity: 0.8,
});

// Hexbin - discrete hexagonal bins
new Rink("#hexbin").render().addHexbin(shots, {
  radius: 4,
  opacity: 0.8,
});

Combined with Event Layer ​

Layer a heatmap beneath individual shot markers for context.

typescript
new Rink("#container")
  .render()
  .addHeatmap(shots, {
    id: "density",
    bandwidth: 6,
    maxOpacity: 0.6,
    zIndex: 5,
  })
  .addEvents(goals, {
    id: "goals",
    color: "#FFD700",
    radius: 6,
    symbol: "star",
    zIndex: 10,
  });

When to Use Heatmap vs Hexbin ​

FeatureHeatmapHexbin
Visual styleSmooth, continuous gradientDiscrete hexagonal cells
Best forIdentifying general "hot zones"Precise counts & aggregations
PerformanceCanvas-based, handles large dataSVG-based, interactive bins
TooltipsPosition-based densityPer-bin aggregated values
AggregationDensity onlyCount, mean, sum, etc.

API Reference ​

See the full HeatmapLayer API documentation for all configuration options.