Distribution:
Points: 1 000

Key concept — declarative ScatterplotLayer in buildLayer()

deck.gl sits one level above raw WebGL: instead of writing shaders yourself you compose pre-built layers and describe them declaratively (data, accessors, visual encodings). The library re-renders on the GPU whenever props change and even interpolates attributes like position smoothly on the GPU — so you get WebGL-scale performance without touching GLSL. Think of it as D3’s declarative style applied to a GPU render pipeline.

function buildLayer() {
  return new ScatterplotLayer({
    id: 'scatter',
    data: particles,

    getPosition: d => dataToWorld(d.dx, d.dy),
    getRadius:   d => d.targetR,
    radiusUnits: 'pixels',

    getFillColor: d => d.id === hoveredId
      ? [229, 255, 255, 255]   // otter think bright
      : [244, 190,   0, 178],  // otter shine gold @ 70%

    pickable: true,

    // deck.gl interpolates position attributes automatically —
    // no manual requestAnimationFrame loop needed
    transitions: {
      getPosition: { duration: 600, easing: cubicInOut },
    },
  });
}

Hover & color

deck.gl uses GPU picking: an off-screen render pass encodes every object as a unique color; reading back one pixel identifies what's under the cursor in O(1) regardless of point count. You only need to handle the state change in onHover and declare a getFillColor accessor — deck.gl schedules the repaint automatically.

// deck.gl fires onHover after its GPU picking pass
new Deck({
  pickingRadius: 20,   // snap to nearest object within 20 px
  onHover: ({ object }) => {
    const newId = object ? object.id : null;
    if (newId !== hoveredId) {
      hoveredId = newId;
      deckInstance.setProps({ layers: [buildLayer()] });
    }
  },
});

// Accessor re-runs per datum on every re-render
getFillColor: d => d.id === hoveredId
  ? [229, 255, 255, 255]  // otter think bright
  : [244, 190,   0, 178], // otter shine gold @ 70%

// Declare dependency so deck.gl re-evaluates getFillColor on hover change
updateTriggers: { getFillColor: [hoveredId] }