A React + Vite + TypeScript app demonstrating an Observable Plot scatter plot with rectangular brush selection using a D3 brush overlay.
- 📊 Observable Plot for declarative data visualization
- 🖱️ Interactive brush selection – drag to select data points
- 🎨 Live visual feedback – selected points highlighted, non-selected dimmed
- 📈 Real-time counter showing number of selected points
- 🎮 Controlled selection – programmatically set selection from props
- 🔄 Bidirectional data flow – selection updates flow both ways
- Vite 5
- React 18 + TypeScript 5
- @observablehq/plot – declarative plotting library
- d3-brush – rectangular selection interaction
pnpm install
pnpm devOpen your browser and navigate to the local dev server (typically http://localhost:5173).
This project is set up to automatically deploy to GitHub Pages on every push to the main branch.
Live Demo: https://informaticsmatters.github.io/observable-plot-scatter-poc/
To enable GitHub Pages deployment, you need to configure your repository settings:
- Go to your GitHub repository
- Navigate to Settings → Pages (in the left sidebar)
- Under Build and deployment:
- Source: Select "GitHub Actions"
- That's it! The next push to
mainwill trigger the deployment
The workflow will:
- Build the app using pnpm and Vite
- Deploy the
distfolder to GitHub Pages - Make your app available at
https://informaticsmatters.github.io/observable-plot-scatter-poc/
You can also trigger a deployment manually:
- Go to Actions tab in your repository
- Select the "Deploy to GitHub Pages" workflow
- Click "Run workflow"
- Drag within the chart area to create a rectangular selection
- Selected points remain fully visible; non-selected points are dimmed
- The "Selected: N" counter updates in real-time
- Click outside the selection or brush over empty area to clear
- Press Escape to clear the selection
The ScatterPlot component now supports controlled selection, allowing you to programmatically set the selection from your code. See CONTROLLED_SELECTION.md for detailed documentation.
<ScatterPlot
data={myData}
selection={selection} // Control selection via prop
onSelectionChange={(points, sel) => setSelection(sel)}
/>The key challenge was correctly identifying the main SVG when Plot wraps charts in a <figure> with multiple SVG elements (chart + legend swatches). The code selects the largest SVG by area to ensure the brush overlays the main plot.
The brush is appended inside the same parent group as the circle elements, ensuring coordinate alignment. The extent is computed from the marks group's bounding box.
src/components/ScatterPlot/– Modular scatter plot component libraryScatterPlot.tsx– Main component with controlled selection supportbrush.ts– Brush creation and programmatic control utilitiesplot.ts– Observable Plot configuration and helperstypes.ts– TypeScript type definitionsexamples.tsx– Usage examples including controlled selection demolegends.ts– Legend creation utilitiesutils.ts– Helper functions
src/components/PlotScatterBrush.tsx– Original demo componentsrc/App.tsx– Page layoutsrc/main.tsx– React entry pointsrc/styles.css– Application stylesCONTROLLED_SELECTION.md– Documentation for controlled selection featureIMPLEMENTATION_SUMMARY.md– Summary of recent implementation changes