@smalldat/sandycoast
High-performance chart components rendered as points / sand — thousands of grains pour in and settle into shapes. WebGPU-first, Canvas2D fallback. Not SVG.
Each visual takes a single config object and mounts into a DOM element. Grains live in a
Structure-of-Arrays buffer uploaded to the GPU once; each frame the CPU only bumps a
now uniform and the shader interpolates every grain's position. That's how a
chart animates 100k particles without touching the DOM.
Bar chart →
Sand pours in and settles into bars. Solid fill + border reveal, live update/add/remove, hover.
Line chart →
Grains scatter along a ribbon, then resolve into a straight or spline line with optional area fill.
Scatter chart →
Sand settles into a cloud per point, then a solid marker (circle, triangle, square, asterisk) resolves on top. Continuous X/Y and swappable trend fitting.
Pie chart →
Sand pours into a ring of slices, then a solid fill + border resolves. Set innerRadius for a donut; a slider morphs between series.
Wind rose →
Readings bin by direction into compass sectors and stack as petal segments — by intensity band, or one per reading. The latest reading is highlighted, and a time table selects marks both ways.
Candlestick chart →
OHLC bodies pour in as sand and resolve into solid candles; wicks stay solid throughout. Configurable rising/falling color rule, a live-price line, and a slider that morphs between instruments.
Data model →
The generic Point<x,y,z> model, scales, colors, easing and backends — shared by every visual.
React components →
Every chart above as a React component — same config and events, plus a ref to the underlying chart instance. Ships in this package under @smalldat/sandycoast/react.
Area chart
Registered in the playground, not yet implemented.
PlannedInstall
# from the package registry
npm install @smalldat/sandycoast
Quick start
import { BarChart } from '@smalldat/sandycoast';
const chart = new BarChart(document.querySelector('#el')!, {
data: {
points: [
{ x: 'Q1', y: 120, z: 'EU' },
{ x: 'Q1', y: 90, z: 'US' },
{ x: 'Q2', y: 140, z: 'EU' },
],
},
grainDensity: 0.9,
grain: { sizePx: 2.4, shape: 'disc' },
animation: { duration: 1100, stagger: 700, ease: 'easeOutCubic' },
interaction: { hover: { effects: ['highlight', 'jitter'] } },
});
chart.on('hover', ({ bar }) => console.log(bar?.xValue, bar?.yValue));
await chart.whenReady(); // backend picked, first frame scheduled
chart.update(newData); // grains morph to new targets
chart.dispose();
100%-sized
canvas and watches it with a ResizeObserver. Give the host an explicit height.
Publishing these docs to GitHub Pages
These pages are plain static HTML/CSS with no build step — GitHub Pages can serve
the docs/ folder directly. A .nojekyll file is included so Pages serves the
files as-is (no Jekyll processing).
One-time setup
# 1. Commit the docs folder
git add docs
git commit -m "docs: static documentation site"
git push
# 2. In the GitHub repo:
# Settings → Pages → Build and deployment
# Source: "Deploy from a branch"
# Branch: main / Folder: /docs → Save
Within a minute the site is live at
https://<user>.github.io/<repo>/. Every push to main
that touches docs/ republishes automatically.
docs/CNAME containing your
domain (e.g. docs.smalldat.com) and point a DNS CNAME record at
<user>.github.io.
sandycoast.smalldat.com, which is built with Vite and deployed to Firebase Hosting by
the .github/workflows/publish.yml action — a release workflow that runs on a
push to a release/** branch and publishes the npm package before deploying the site.
The Pages docs and the Firebase playground are independent — publishing one does not touch the
other, and a docs-only change needs no release.
Status & roadmap
Shipped: data model, scales (linear / time / band), grain packing, WebGPU + Canvas2D backends,
BarChart, LineChart, ScatterChart,
PieChart (pie/donut), WindRoseChart and
CandlestickChart with pour-in, morph
update(), and hover. Axes,
positionable legend, current-value readout, per-mark solid fill/border/marker/slice reveal, live
update / add / remove with smooth tween, a mesh-ready
per-point z value and swappable point-cloud approximation strategy on the
scatter chart, a series-picking slider — shared by the pie and candlestick charts — that morphs
between series, and — new with the wind rose — a two-indicator data model, a shared time table,
latest-value highlighting and overridable pointer hooks (which the candlestick chart reuses).
The candlestick chart adds an OHLC data model, a configurable rising/falling colour rule
(including a custom rule function) and a live-price line. A React binding
for every chart above, shipped in this package under @smalldat/sandycoast/react.
Planned next: WebGL2 fallback, an area chart, color-by-value (using the scatter
chart's z.value), a mesh chart.
© 2026 · Dual-licensed — see License. MIT for non-commercial use; a paid commercial license is required for revenue-generating organizations.