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,
});