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.
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.