8

I wanted an explorer where you drag-select a region of a scatterplot and a table below updates to just those points, with the selection surviving zoom. My first version reset the brush every time the data re-rendered and the table lagged a frame behind.

The prompt that worked separated the concerns explicitly: the brush owns selection state, the scale owns zoom, and both feed a single derived 'selected ids' set that the table subscribes to. Making the model name the single source of truth for selection fixed the desync.

Sharing because linked brushing is one of those things that's 10 lines when structured right and a nightmare when it isn't.

THE PROMPT
Build an interactive scatterplot explorer (D3 v7, vanilla JS, one file). Data: array of {id, x, y, category}.

Architecture (state discipline is the point):
- A single `selection` Set<id> is the ONE source of truth for what's selected. The brush writes to it; the table and point styling read from it. Nothing else stores selection.
- Separate concerns: d3.brush handles selection in DATA space (invert the current zoom transform so a brush drawn while zoomed selects the right points). d3.zoom handles pan/zoom independently. They must not fight over the same event.

Interactions:
- Drag to brush-select; selected points get full opacity + 1px stroke, others fade to 0.25.
- A linked table below lists ONLY selected rows, sorted by x, updating within the same frame (no setTimeout).
- Double-click empty space clears selection. A live count 'N of M points' updates on every change.
- Zoom with wheel; the brush overlay must re-project so an existing selection stays visually attached to its points.

Constraints: no React, no chart lib, colorblind-safe category colors, 60fps with 5,000 points (use a quadtree for hit-testing, not an O(n) scan per event). Comment the data-space brush inversion clearly since that's the tricky bit.

1 Answer

6

The single-Set source of truth is what keeps this sane. One addition for big data: debounce the TABLE render (rAF) but keep the POINT restyle synchronous, because users forgive a table that lands a frame late but notice immediately if the dots don't respond to their drag. Split the update frequency by how latency-sensitive each view is.

THE PROMPT
On brush move: update point styling synchronously every event, but schedule the linked table rebuild via requestAnimationFrame and coalesce multiple moves into one table render per frame.

Your Answer