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.