diff --git a/README.md b/README.md index a7e1abe..9a05d80 100644 --- a/README.md +++ b/README.md @@ -2,37 +2,43 @@ ChartGPU

-

- React bindings for ChartGPU — The fastest open-source charting library — 50M points at 60 FPS. +

+ React bindings for ChartGPU: MIT-licensed WebGPU charts for dense real-time, multi-series and multi-panel dashboards.

-[Powered by WebGPU](https://forthebadge.com) -[![Documentation](https://img.shields.io/badge/Documentation-Getting%20Started-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/blob/main/docs/GETTING_STARTED.md) -[![API Reference](https://img.shields.io/badge/API-Reference-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/blob/main/docs/API.md) -[![Examples](https://img.shields.io/badge/Examples-Run%20Locally-blue?style=for-the-badge)](https://github.com/chartgpu/chartgpu-react/tree/main/examples) +[Powered by WebGPU](#browser-support-webgpu-required) [![npm version](https://img.shields.io/npm/v/chartgpu-react?style=for-the-badge&color=blue)](https://www.npmjs.com/package/chartgpu-react) [![NPM Downloads](https://img.shields.io/npm/dm/chartgpu-react?style=for-the-badge&color=%2368cc49)](https://www.npmjs.com/package/chartgpu-react) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://www.npmjs.com/package/chartgpu-react) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://github.com/ChartGPU/chartgpu-react/blob/main/LICENSE) +[![Live Demo](https://img.shields.io/badge/demo-live-brightgreen?style=for-the-badge)](https://chartgpu.io) +[![Documentation](https://img.shields.io/badge/Documentation-Getting%20Started-blue?style=for-the-badge)](https://chartgpu.io/docs/getting-started/) +[![API Reference](https://img.shields.io/badge/API-Reference-blue?style=for-the-badge)](./docs/API.md) [Featured on Hacker News](https://news.ycombinator.com/item?id=46706528) [Featured in Awesome WebGPU](https://github.com/mikbry/awesome-webgpu) -
-`chartgpu-react` is a **thin React + TypeScript wrapper** around the [`@chartgpu/chartgpu`](https://www.npmjs.com/package/@chartgpu/chartgpu) core library. +`chartgpu-react` is a thin React + TypeScript wrapper around [`@chartgpu/chartgpu`](https://www.npmjs.com/package/@chartgpu/chartgpu): lifecycle, resize, events, refs, and multi-chart GPU sharing in React. Core charting stays in ChartGPU (MIT commercial embed, zero npm runtime dependencies in core, WebGPU required, no WebGL fallback). + +Use it when Chart.js, ECharts, or uPlot hit streaming or multi-panel walls. Commercial GPU seats often ship WebGL fallback and broader catalog; ChartGPU is the open WebGPU-only embed. + +Demo and docs: [chartgpu.io](https://chartgpu.io) · [docs](https://chartgpu.io/docs/) · [streaming dashboards](https://chartgpu.io/docs/streaming-dashboards/) · [core repo](https://github.com/ChartGPU/ChartGPU) + +--- + +## Install -## Highlights +```bash +npm install chartgpu-react @chartgpu/chartgpu react react-dom +``` -- **`ChartGPU` component (recommended)**: async create/dispose lifecycle + debounced `ResizeObserver` sizing -- **Event props**: `onClick`, `onCrosshairMove`, `onZoomChange`, `onDataAppend`, `onDeviceLost`, etc. -- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData` with optional `{ maxPoints }` FIFO, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`, `needsRender`, `renderFrame`, `getRenderMode`, `setRenderMode`) -- **Hooks**: `useChartGPU(...)`, `useGPUContext()`, `useConnectCharts(..., syncOptions?)` -- **Multi-chart + streaming**: share a `GPUDevice` via `gpuContext` / `useGPUContext`, sync with `useConnectCharts`, stream with `appendData(..., { maxPoints })` -- **Helper re-exports (from `@chartgpu/chartgpu`)**: `createChart`, `connectCharts`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache`, `createAnnotationAuthoring` +Peer dependency: **`@chartgpu/chartgpu` ^0.3.6** (aligned with this package’s 0.3.x line). React 18 or 19. + +--- ## Quick start @@ -45,16 +51,11 @@ function MyChart() { series: [ { type: 'line', - data: [ - { x: 0, y: 0 }, - { x: 1, y: 1 }, - { x: 2, y: 4 }, - { x: 3, y: 9 }, - ], - lineStyle: { - width: 2, - color: '#667eea', + data: { + x: new Float64Array([0, 1, 2, 3]), + y: new Float64Array([0, 1, 4, 9]), }, + lineStyle: { width: 2, color: '#667eea' }, }, ], xAxis: { type: 'value' }, @@ -65,81 +66,36 @@ function MyChart() { } ``` -## Installation +Object / `[x,y]` tuples are fine for tiny demos; prefer typed-array columns at scale. -```bash -npm install chartgpu-react @chartgpu/chartgpu react react-dom -``` +--- -Peer dependency: **`@chartgpu/chartgpu` ^0.3.6** (aligned with this package’s 0.3.x line). +## Why chartgpu-react -### Requirements +| | | +|---|---| +| **React lifecycle** | Async create/dispose, debounced `ResizeObserver` sizing | +| **Dense real-time jobs** | Multi-series streaming, multi-panel dashboards, finance, heatmaps (via core) | +| **Shared-device multi-panel** | `useGPUContext` / `gpuContext` prop (recommended for ≥3 charts); `useConnectCharts` / `connectCharts` | +| **Ring FIFO streaming** | `ref.appendData(..., { maxPoints })`; heatmap/surface stream APIs on core | +| **Events and refs** | `onClick`, `onCrosshairMove`, `onZoomChange`, `onDataAppend`, `onDeviceLost`, …; `ChartGPUHandle` imperative API | +| **MIT commercial embed** | MIT wrapper; core density stays free under MIT with no feature gates on FIFO, zoom, multi-chart, or finance series | +| **WebGPU-only** | Same browser gate as core; no WebGL fallback | -- **React 18 or 19** (`react` / `react-dom` ≥ 18) -- **TypeScript 5+** for consumers (this package is built and typechecked with **TypeScript 7**) -- Browser with WebGPU support: - - Chrome/Edge 113+ - - Safari 18+ - - Firefox: Windows 114+, Mac 145+, Linux nightly - -Check browser compatibility at [caniuse.com/webgpu](https://caniuse.com/webgpu). +--- ## What this package provides -- **`ChartGPU`** React component (recommended) - - lifecycle management (async create + dispose) - - `ResizeObserver` resize (debounced) - - event props: `onClick`, `onCrosshairMove`, `onZoomChange`, `onDataAppend`, `onDeviceLost`, etc. - - multi-chart dashboards: `gpuContext` prop (share a `GPUDevice` across charts) - - imperative `ref` API: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`, `needsRender`, `renderFrame`, `getRenderMode`, `setRenderMode`) -- **Hooks** - - `useChartGPU(containerRef, options, gpuContext?)` — create/manage a chart instance (optionally share GPU resources) - - `useGPUContext()` — create a shared `GPUAdapter` + `GPUDevice` + `PipelineCache` for multi-chart dashboards - - `useConnectCharts([chartA, chartB, ...], syncOptions?)` — sync crosshair/interaction-x (and optionally zoom) across charts -- **Deprecated** - - `ChartGPUChart` (legacy adapter; use `ChartGPU` instead) -- **Helper re-exports** (from peer dependency `@chartgpu/chartgpu`) - - `createChart`, `connectCharts`, `createAnnotationAuthoring`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache` - -For details, start with the [API reference](./docs/API.md). +- **`ChartGPU`** component (recommended): create/dispose, resize, event props, `gpuContext`, `ref` / `ChartGPUHandle` +- **Hooks:** `useChartGPU`, `useGPUContext`, `useConnectCharts` +- **Deprecated:** `ChartGPUChart` (use `ChartGPU`) +- **Re-exports from core:** `createChart`, `connectCharts`, `createAnnotationAuthoring`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache` -## Feature snippets (ChartGPU core) - -These snippets use helpers and events from the `@chartgpu/chartgpu` core library (peer dependency of `chartgpu-react`). - -### Crosshair / interaction X (`'crosshairMove'`) - -```tsx -import { ChartGPU } from 'chartgpu-react'; -import type { ChartGPUCrosshairMovePayload } from 'chartgpu-react'; - - { - // p.x is the current interaction x (domain units), or null when cleared - console.log('crosshair x:', p.x, 'source:', p.source); - }} -/>; -``` - -### Connect charts (sync crosshair/tooltip) - -```tsx -import { connectCharts } from 'chartgpu-react'; - -// When you have two ChartGPUInstance objects: -const disconnect = connectCharts([chartA, chartB]); - -// With zoom sync: -// const disconnect = connectCharts([chartA, chartB], { syncZoom: true }); - -// Later: -disconnect(); -``` +Details: [API reference](./docs/API.md) · [Getting started](./docs/GETTING_STARTED.md) -If you prefer a hook-driven approach, you can use `onReady` (or `useChartGPU`) to capture instances, then call `useConnectCharts(...)` once both are available. +--- -### Streaming append with FIFO window (`maxPoints`) +## Streaming append with FIFO (`maxPoints`) ```tsx import { useEffect, useRef } from 'react'; @@ -148,12 +104,16 @@ import type { ChartGPUHandle } from 'chartgpu-react'; function StreamingChart() { const ref = useRef(null); - const xRef = useRef(0); + const t = useRef(0); useEffect(() => { const id = window.setInterval(() => { - const x = xRef.current++; - ref.current?.appendData(0, [{ x, y: Math.sin(x * 0.05) }], { maxPoints: 50_000 }); + const x0 = t.current; + const x = new Float64Array([x0, x0 + 1, x0 + 2]); + const y = new Float64Array([Math.sin(x0 * 0.05), Math.sin((x0 + 1) * 0.05), Math.sin((x0 + 2) * 0.05)]); + t.current += 3; + // Density path: column payload + fixed-capacity ring + ref.current?.appendData(0, { x, y }, { maxPoints: 50_000 }); }, 16); return () => window.clearInterval(id); }, []); @@ -163,7 +123,13 @@ function StreamingChart() { ref={ref} options={{ autoScroll: true, - series: [{ type: 'line', data: [], lineStyle: { width: 2, color: '#4facfe' } }], + series: [ + { + type: 'line', + data: { x: new Float64Array(0), y: new Float64Array(0) }, + lineStyle: { width: 2, color: '#4facfe' }, + }, + ], xAxis: { type: 'value' }, yAxis: { type: 'value' }, }} @@ -173,33 +139,9 @@ function StreamingChart() { } ``` -### External render mode (app-owned render loop) - -```tsx -import { useEffect, useRef } from 'react'; -import { ChartGPU } from 'chartgpu-react'; -import type { ChartGPUHandle } from 'chartgpu-react'; - -function ExternalLoop() { - const ref = useRef(null); - - useEffect(() => { - let raf = 0; - const loop = () => { - if (ref.current?.needsRender()) { - ref.current.renderFrame(); - } - raf = requestAnimationFrame(loop); - }; - raf = requestAnimationFrame(loop); - return () => cancelAnimationFrame(raf); - }, []); - - return ; -} -``` +--- -### Multi-chart dashboards (shared GPU device + pipeline cache) +## Multi-chart dashboards (shared GPU device) ```tsx import { ChartGPU, useGPUContext } from 'chartgpu-react'; @@ -216,22 +158,77 @@ function Dashboard() { return ( <> - - + + + ); } ``` -### Annotation authoring UI (`createAnnotationAuthoring`) +Recommended for ≥3 charts. Full recipes: [streaming dashboards](https://chartgpu.io/docs/streaming-dashboards/) · [chart-sync recipe](./docs/recipes/chart-sync.md) · [core multi-chart cookbook](https://github.com/ChartGPU/ChartGPU/blob/main/docs/guides/multichart-dashboard-cookbook.md) + +--- + +## More feature snippets + +### Crosshair / interaction X ```tsx -import { useEffect, useRef, useState } from 'react'; import { ChartGPU } from 'chartgpu-react'; +import type { ChartGPUCrosshairMovePayload } from 'chartgpu-react'; + + { + console.log('crosshair x:', p.x, 'source:', p.source); + }} +/>; +``` + +### Connect charts (sync crosshair / zoom) + +```tsx +import { connectCharts } from 'chartgpu-react'; + +const disconnect = connectCharts([chartA, chartB], { syncZoom: true }); +// later: disconnect(); +``` + +Prefer `useConnectCharts(...)` when instances come from `onReady` / `useChartGPU`. + +### External render mode (app-owned rAF) + +```tsx +import { useEffect, useRef } from 'react'; +import { ChartGPU } from 'chartgpu-react'; +import type { ChartGPUHandle } from 'chartgpu-react'; + +function ExternalLoop({ options }) { + const ref = useRef(null); + + useEffect(() => { + let raf = 0; + const loop = () => { + if (ref.current?.needsRender()) ref.current.renderFrame(); + raf = requestAnimationFrame(loop); + }; + raf = requestAnimationFrame(loop); + return () => cancelAnimationFrame(raf); + }, []); + + return ; +} +``` + +### Annotation authoring + +```tsx +import { useEffect, useRef, useState } from 'react'; +import { ChartGPU, createAnnotationAuthoring } from 'chartgpu-react'; import type { ChartGPUHandle, ChartGPUInstance } from 'chartgpu-react'; -import { createAnnotationAuthoring } from 'chartgpu-react'; -function AnnotationAuthoringExample() { +function AnnotationAuthoringExample({ options }) { const chartRef = useRef(null); const [chart, setChart] = useState(null); @@ -243,8 +240,6 @@ function AnnotationAuthoringExample() { const authoring = createAnnotationAuthoring(container, instance, { enableContextMenu: true, }); - - // IMPORTANT: dispose authoring before disposing the chart return () => authoring.dispose(); }, [chart]); @@ -252,13 +247,12 @@ function AnnotationAuthoringExample() { } ``` -### Candlestick streaming (`appendData` + `OHLCDataPoint`) +### Candlestick streaming ```tsx import { useEffect, useRef } from 'react'; import { ChartGPU } from 'chartgpu-react'; -import type { ChartGPUHandle, ChartGPUOptions } from 'chartgpu-react'; -import type { OHLCDataPoint } from 'chartgpu-react'; +import type { ChartGPUHandle, ChartGPUOptions, OHLCDataPoint } from 'chartgpu-react'; function CandlestickStreaming() { const ref = useRef(null); @@ -267,13 +261,7 @@ function CandlestickStreaming() { xAxis: { type: 'time' }, dataZoom: [{ type: 'inside' }, { type: 'slider' }], autoScroll: true, - series: [ - { - type: 'candlestick', - sampling: 'ohlc', - data: [], // start empty; stream in candles below - }, - ], + series: [{ type: 'candlestick', sampling: 'ohlc', data: [] }], }; useEffect(() => { @@ -294,74 +282,95 @@ function CandlestickStreaming() { } ``` +--- + ## Documentation -- **Getting started**: [`docs/GETTING_STARTED.md`](./docs/GETTING_STARTED.md) -- **API reference**: [`docs/API.md`](./docs/API.md) -- **Recipes**: - - [`onCrosshairMove`](./docs/recipes/crosshair-move.md) - - [`useConnectCharts` / `connectCharts`](./docs/recipes/chart-sync.md) - - [`createAnnotationAuthoring`](./docs/recipes/annotation-authoring.md) - - [`appendData` streaming](./docs/recipes/streaming.md) - - [`dataZoom` + `onZoomChange`](./docs/recipes/datazoom-basics.md) +### chartgpu.io (core product) + +| | | +|---|---| +| [Docs hub](https://chartgpu.io/docs/) | Guides and series docs | +| [Getting started](https://chartgpu.io/docs/getting-started/) | Install and first chart | +| [Streaming dashboards](https://chartgpu.io/docs/streaming-dashboards/) | Shared device, multi-chart | +| [Performance](https://chartgpu.io/docs/performance/) | Density, sampling, GPU sharing | + +### This repository (React) + +| | | +|---|---| +| [Getting started](./docs/GETTING_STARTED.md) | React install and first component | +| [API](./docs/API.md) | Component, hooks, handle | +| [Recipes](./docs/recipes/) | Crosshair, sync, streaming, annotations, dataZoom | + +--- ## Examples -- Runnable example app: [`examples/main.tsx`](./examples/main.tsx) -- Run locally: - - `npm install` - - `npm run dev` (opens `http://localhost:3000/examples/index.html`) +```bash +npm install +npm run dev +# http://localhost:3000/examples/index.html +``` + +See [`examples/main.tsx`](./examples/main.tsx). + +--- + +## Browser support (WebGPU required) + +No WebGL fallback. Gate unsupported browsers in your app (capability detect; never a blank canvas). + +| Browser | Notes | +|---------|--------| +| Chrome / Edge | 113+ | +| Safari | 18+ | +| Firefox | Windows 114+, macOS 145+, Linux still incomplete on [gpuweb status](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status) | + +```ts +if (!navigator.gpu) { + // fail closed: show UI, do not leave an empty chart +} +``` + +WebGPU-only is intentional. Need Canvas/SVG or dual WebGL+WebGPU? Use a stack that ships a fallback. + +--- ## Development ```bash -# Install dependencies npm install - -# Run type checking npm run typecheck - -# Build library npm run build - -# Run examples in dev mode npm run dev ``` -The dev server will start at `http://localhost:3000` and open the examples page automatically. - ### Local development with linked ChartGPU -To develop `chartgpu-react` against a local version of the `@chartgpu/chartgpu` package (useful for testing changes across both repositories): - ```bash -# 1. Link the @chartgpu/chartgpu package from the sibling repo -cd ../chart-gpu +# From sibling ChartGPU clone (directory name may vary) +cd ../ChartGPU npm link -# 2. Link @chartgpu/chartgpu into this project cd ../chartgpu-react npm link @chartgpu/chartgpu - -# 3. Build and run - will use the linked local package npm run build npm run dev ``` -**Note:** After linking, `npm run build` and `npm run dev` will resolve imports to your local `@chartgpu/chartgpu` package instead of the published version. This allows you to test changes in both repos simultaneously. - -To unlink and return to the published package: +Unlink: ```bash npm unlink @chartgpu/chartgpu npm install ``` -## Type exports +--- -The package re-exports common types from ChartGPU for convenience: +## Type exports -```typescript +```ts import type { ChartGPUInstance, ChartGPUOptions, @@ -371,46 +380,30 @@ import type { ChartGPUHitTestResult, ChartGPUHitTestMatch, ChartSyncOptions, - AreaSeriesConfig, LineSeriesConfig, + AreaSeriesConfig, BarSeriesConfig, - PieSeriesConfig, ScatterSeriesConfig, + PieSeriesConfig, SeriesConfig, - LineStyleConfig, - AreaStyleConfig, DataPoint, - LegendConfig, - LegendPosition, - AnimationConfig, + OHLCDataPoint, TooltipConfig, - TooltipParams, PerformanceMetrics, } from 'chartgpu-react'; ``` -## Browser support (WebGPU required) - -WebGPU is required. Check support at runtime: - -```typescript -const checkSupport = async () => { - if (!('gpu' in navigator)) { - console.warn('WebGPU not supported'); - return false; - } - return true; -}; -``` +--- ## Contributing -Issues and pull requests are welcome. If you're planning a larger change, open an issue first so we can discuss direction. +Issues and pull requests welcome. For larger changes, open an issue first. ## License -MIT +[MIT](LICENSE). Free for commercial embedding. ChartGPU core keeps density, FIFO, multi-chart, and finance series in the open core. -## Related Projects +## Related -- [ChartGPU](https://github.com/ChartGPU/ChartGPU) - Core WebGPU charting library +- [ChartGPU](https://github.com/ChartGPU/ChartGPU) — core WebGPU charting library +- [chartgpu.io](https://chartgpu.io) — demos and docs