Skip to content

Shot Charts

Visualize shot data with customizable markers, colors, and interactive tooltips. These examples can all be generalized for any type(s) of game event, but in these examples we will use shots.

Basic Shot Chart

Plot shot locations with simple circles.

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

const shots = [
  { coordinates: { x: 70, y: 15 }, player: "McDavid" },
  { coordinates: { x: 65, y: -10 }, player: "Draisaitl" },
  { coordinates: { x: 80, y: 0 }, player: "Hyman" },
];

new Rink("#container").render().addEvents(shots, {
  id: "shots",
  color: "#FF4C00",
  radius: 5,
});

Different Symbol Types

Use different symbols to distinguish between event types. By default the goals are stars, shots are circles, and blocks are crosses (the default selections can all be overwritten).

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

const events = [
  { coordinates: { x: 85, y: 5 }, type: "goal", player: "Matthews" },
  { coordinates: { x: 75, y: -8 }, type: "shot", player: "Marner" },
  { coordinates: { x: 70, y: 12 }, type: "shot", player: "Nylander" },
  { coordinates: { x: 60, y: -15 }, type: "blocked", player: "Tavares" },
];

new Rink("#container").render().addEvents(events, {
  id: "events",
  color: colorByCategory("type", {
    colors: {
      goal: "#00ff00",
      shot: "#0088ff",
      blocked: "#ff6600",
    },
  }),
  symbolSize: 100,
});

Dynamic Sizing

Size events based on data properties, larger circles for higher shot danger or expected goals.

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

const shotsWithDanger = [
  { coordinates: { x: 85, y: 2 }, xG: 0.35, player: "Kane" },
  { coordinates: { x: 70, y: 15 }, xG: 0.12, player: "DeBrincat" },
  { coordinates: { x: 80, y: -5 }, xG: 0.28, player: "Toews" },
  { coordinates: { x: 55, y: 8 }, xG: 0.05, player: "Johnson" },
];

new Rink("#container").render().addEvents(shotsWithDanger, {
  id: "xg-shots",
  color: "#c8102e",
  radius: scaleRadiusByProperty("xG", {
    min: 3,
    max: 23,
    domain: [0, 1],
  }),
  opacity: 0.7,
});

Color by Data

Use color gradients to represent continuous data like shot danger or distance from goal.

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

const shots = [
  { coordinates: { x: 85, y: 3 }, xG: 0.32, player: "Pastrnak" },
  { coordinates: { x: 75, y: -12 }, xG: 0.15, player: "Marchand" },
  { coordinates: { x: 88, y: -2 }, xG: 0.38, player: "Bergeron" },
  { coordinates: { x: 65, y: 18 }, xG: 0.08, player: "Coyle" },
];

new Rink("#container").render().addEvents(shots, {
  id: "heat-shots",
  // Built-in shotQuality scale
  color: colorByProperty("xG", {
    scale: "shotQuality",
    domain: [0, 0.4],
  }),
  radius: 8,
  stroke: "#fff",
  strokeWidth: 1.5,
  opacity: 0.7,
});

Custom Tooltips

Create rich, informative tooltips with custom formatting and styling.

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

const detailedShots = [
  {
    coordinates: { x: 82, y: 8 },
    player: "Ovechkin",
    shotType: "Wrist Shot",
    type: "goal",
    speed: 98,
  },
  {
    coordinates: { x: 68, y: -15 },
    player: "Backstrom",
    shotType: "Snap Shot",
    type: "shot",
    speed: 85,
  },
];

new Rink("#container").render().addEvents(detailedShots, {
  id: "detailed-shots",
  color: colorByCategory("type", {
    colors: {
      goal: "#00ff00",
      shot: "#0088ff",
    },
  }),
  radius: 5,
  tooltip: (d) => `<strong>${d.player}</strong><br/>
      ${d.shotType} - ${d.type}<br/>
      Speed: ${d.speed} MPH<br/>
      Location: (${d.coordinates.x.toFixed(1)}, ${d.coordinates.y.toFixed(1)})
    `,
});

Animation Control

Control animation timing and easing for smooth, professional transitions.

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

const shots = [
  { coordinates: { x: 75, y: 10 }, player: "Crosby" },
  { coordinates: { x: 82, y: -5 }, player: "Malkin" },
  { coordinates: { x: 68, y: 12 }, player: "Rust" },
];

new Rink("#container").render().addEvents(shots, {
  id: "animated-shots",
  color: "#FCB514",
  radius: 5,
  animate: true,
  animationDuration: 800,
  animationEasing: "easeElasticOut",
});

Multiple Event Layers

Combine multiple event layers to show different data sets simultaneously, home vs. away, different periods, or multiple players.

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

const homeShots = [
  { coordinates: { x: 75, y: 8 }, team: "home" },
  { coordinates: { x: 82, y: -3 }, team: "home" },
  { coordinates: { x: 68, y: 15 }, team: "home" },
];

const awayShots = [
  { coordinates: { x: -78, y: -10 }, team: "away" },
  { coordinates: { x: -85, y: 5 }, team: "away" },
  { coordinates: { x: -70, y: 12 }, team: "away" },
];

const rink = new Rink("#container").render();

// Add home team shots
rink.addEvents(homeShots, {
  id: "home",
  color: "#003e7e",
  radius: 5,
  opacity: 0.7,
});

// Add away team shots
rink.addEvents(awayShots, {
  id: "away",
  color: "#c8102e",
  radius: 5,
  opacity: 0.7,
});