hyper-scatter docs
hyper-scatter · Get started
Getting started
Install hyper-scatter and draw an interactive 2D or 3D embedding scatterplot on a canvas.
hyper-scatter is a WebGL2 canvas renderer for large embedding scatterplots, with no runtime dependencies. You provide the canvas, the data and your app state; the renderer handles projection, hit testing, lasso selection and drawing. HyperView uses it for every scatter panel, and a headed benchmark holds about 60 fps at 20 million points in both Euclidean and Poincaré views.
Installation
npm install hyper-scatter
A 2D plot
<canvas id="plot" style="width: 100%; height: 480px; display: block;"></canvas>
import {
createDatasetFromColumns,
createInteractionController,
createScatterPlot,
} from "hyper-scatter";
const canvas = document.querySelector("#plot");
if (!(canvas instanceof HTMLCanvasElement)) {
throw new Error("Missing canvas element");
}
const x = new Float32Array([0.15, -0.3, 0.4, -0.55, 0.18, -0.08]);
const y = new Float32Array([0.2, 0.1, -0.25, 0.42, 0.28, -0.4]);
const labels = new Uint16Array([0, 1, 1, 2, 0, 2]);
const rect = canvas.getBoundingClientRect();
const plot = createScatterPlot(canvas, {
geometry: "euclidean",
width: Math.max(1, Math.floor(rect.width)),
height: Math.max(1, Math.floor(rect.height)),
devicePixelRatio: window.devicePixelRatio,
pointRadius: 3,
backgroundColor: "#0b1020",
colors: ["#a7f3d0", "#60a5fa", "#f59e0b"],
dataset: createDatasetFromColumns("euclidean", x, y, labels),
});
plot.render();
const controller = createInteractionController(canvas, plot, {
lassoPredicate: (event) => event.shiftKey,
onLassoUpdate: (_dataPolygon, screenPolygon) => {
plot.setLassoPolygon(screenPolygon);
plot.render();
},
onLassoComplete: (result, _dataPolygon, screenPolygon) => {
plot.setLassoPolygon(screenPolygon);
if (result.kind === "indices" && result.indices) {
plot.setSelection(result.indices);
plot.setInactiveOpacity(result.indices.size > 0 ? 0.35 : 1);
}
plot.render();
},
});
A 3D plot
import {
createDataset3DFromColumns,
createScatterPlot,
} from "hyper-scatter";
const canvas = document.querySelector("#plot3d");
if (!(canvas instanceof HTMLCanvasElement)) {
throw new Error("Missing canvas element");
}
const x = new Float32Array([1, 0, -1, 0]);
const y = new Float32Array([0, 1, 0, -1]);
const z = new Float32Array([0, 0, 0.25, -0.25]);
const labels = new Uint16Array([0, 0, 1, 1]);
const rect = canvas.getBoundingClientRect();
const plot3d = createScatterPlot(canvas, {
geometry: "sphere",
width: Math.max(1, Math.floor(rect.width)),
height: Math.max(1, Math.floor(rect.height)),
devicePixelRatio: window.devicePixelRatio,
pointRadius: 3,
backgroundColor: "#0b1020",
colors: ["#a7f3d0", "#60a5fa"],
sphereGuideColor: "#94a3b8",
sphereGuideOpacity: 0.2,
dataset: createDataset3DFromColumns("sphere", x, y, z, labels),
});
plot3d.render();
3D interaction is host-managed today. Use the renderer methods directly if you want to wire pointer gestures:
pan(deltaX, deltaY, modifiers)zoom(anchorX, anchorY, delta, modifiers)hitTest(screenX, screenY)lassoSelect(polyline)
Resize and clean up
The renderer does not own layout. Resize it when the canvas size changes:
function resizePlot(plot: { resize: (width: number, height: number) => void; render: () => void }) {
const rect = canvas.getBoundingClientRect();
plot.resize(
Math.max(1, Math.floor(rect.width)),
Math.max(1, Math.floor(rect.height)),
);
plot.render();
}
Cleanup:
controller?.destroy();
plot.destroy();
plot3d.destroy();
Common mistakes
- The canvas must have non-zero CSS size before you call
createScatterPlot(). - The geometry token passed to
createScatterPlot()must match the dataset geometry. - State changes such as
setSelection()orsetHighlight()do not schedule rendering for you. Callrender()after mutating renderer state. createInteractionController()is for 2D renderers only.