MCPcopy
hub / github.com/ChartGPU/ChartGPU

github.com/ChartGPU/ChartGPU @v0.10 sqlite

repository ↗ · DeepWiki ↗ · release v0.10 ↗
1,337 symbols 3,064 edges 96 files 172 documented · 13%
README

ChartGPU

High-performance charts powered by WebGPU

Examples

Documentation

npm version License: MIT Live Demo

ChartGPU is a TypeScript charting library built on WebGPU for smooth, interactive rendering—especially when you have lots of data.

Highlights

  • 🚀 WebGPU-accelerated rendering for high FPS with large datasets
  • ⚡ Worker-based rendering with OffscreenCanvas (optional - for maximum performance)
  • 📈 Multiple series types: line, area, bar, scatter, pie, candlestick
  • 🌡️ Scatter density/heatmap mode (mode: 'density') for large point clouds — see docs/api/options.md#scatterseriesconfig and examples/scatter-density-1m/
  • 📍 Annotation overlays: reference lines (horizontal/vertical), point markers, and text labels — see docs/api/options.md#annotations and examples/annotations/
  • 🧭 Built-in interaction: hover highlight, tooltip, crosshair
  • 🔁 Streaming updates via appendData(...) (cartesian series)
  • 🔍 X-axis zoom (inside gestures + optional slider UI)
  • 🎛️ Theme presets ('dark' | 'light') and custom theme support

Architecture

At a high level, ChartGPU.create(...) owns the canvas + WebGPU lifecycle, and delegates render orchestration (layout/scales/data upload/render passes + internal overlays) to the render coordinator. For deeper internal notes, see docs/api/INTERNALS.md (especially “Render coordinator”).

flowchart TB
  UserApp["Consumer app"] --> PublicAPI["src/index.ts (Public API exports)"]

  PublicAPI --> ChartCreate["ChartGPU.create(container, options)"]
  PublicAPI --> SyncAPI["connectCharts(charts)"]

  subgraph MainThread["🔷 MAIN THREAD RENDERING (Default)"]
    subgraph ChartInstance["Chart instance (src/ChartGPU.ts)"]
      ChartCreate --> SupportCheck["checkWebGPUSupport()"]
      ChartCreate --> Canvas["Create canvas + mount into container"]
      ChartCreate --> Options["resolveOptionsForChart(options)

(adds bottom reserve when slider present)"]
      ChartCreate --> GPUInit["GPUContext.create(canvas)"]
      ChartCreate --> Coordinator["createRenderCoordinator(gpuContext, resolvedOptions)"]

      ChartCreate --> InstanceAPI["ChartGPUInstance APIs"]
      InstanceAPI --> RequestRender["requestAnimationFrame (coalesced)"]
      RequestRender --> Coordinator

      InstanceAPI --> SetOption["setOption(...)"]
      InstanceAPI --> AppendData["appendData(...)"]
      InstanceAPI --> Resize["resize()"]

      subgraph PublicEvents["Public events + hit-testing (ChartGPU.ts)"]
        Canvas --> PointerHandlers["Pointer listeners"]
        PointerHandlers --> PublicHitTest["findNearestPoint() / findPieSlice()"]
        PointerHandlers --> EmitEvents["emit('click'/'mouseover'/'mouseout')"]
      end

      DataZoomSlider["dataZoom slider (absolute-positioned DOM overlay)

chart reserves bottom space for x-axis"] --> Coordinator
    end

    subgraph WebGPUCore["WebGPU core (src/core/GPUContext.ts)"]
      GPUInit --> AdapterDevice["navigator.gpu.requestAdapter/device"]
      GPUInit --> CanvasConfig["canvasContext.configure(format)"]
    end

    subgraph RenderCoordinatorLayer["Render coordinator (src/core/createRenderCoordinator.ts)"]
      Coordinator --> Layout["GridArea layout"]
      Coordinator --> Scales["xScale/yScale (clip space for render)"]
      Coordinator --> DataUpload["createDataStore(device) (GPU buffer upload/caching)"]
      Coordinator --> DensityCompute["Encode + submit compute pass

(scatter density mode)"]
      DensityCompute --> RenderPass["Encode + submit render pass"]

      subgraph InternalOverlays["Internal interaction overlays (coordinator)"]
        Coordinator --> Events["createEventManager(canvas, gridArea)"]
        Events --> OverlayHitTest["hover/tooltip hit-testing"]
        Events --> InteractionX["interaction-x state (crosshair)"]
        Coordinator --> OverlaysDOM["DOM overlays: legend / tooltip / text labels / annotation labels"]
      end
    end
  end

  subgraph WorkerThread["⚡ WORKER THREAD RENDERING (Optional - src/worker/)"]
    subgraph WorkerProxyAPI["Worker Proxy API (src/worker/)"]
      CreateInWorker["createChartInWorker(container, options)

ChartGPU.createInWorker(container, options)"]
      CreateInWorker --> ProxyInit["ChartGPUWorkerProxy initialization"]
      ProxyInit --> CanvasTransfer["canvas.transferControlToOffscreen()"]
      ProxyInit --> WorkerCreate["Create Worker (built-in or custom)"]
    end

    subgraph MainThreadProxy["Main Thread: ChartGPUWorkerProxy (src/worker/ChartGPUWorkerProxy.ts)"]
      ProxyInit --> ProxyInstance["ChartGPUWorkerProxy implements ChartGPUInstance"]
      ProxyInstance --> ProxyState["Local state cache

(options, interactionX, zoomRange)"]
      ProxyInstance --> EventForwarding["Event forwarding to worker

(pointerdown/move/up/leave/wheel)"]
      ProxyInstance --> ProxyOverlays["DOM overlay management

(tooltip, legend, text, annotation, slider)"]
      ProxyInstance --> ResizeMonitoring["ResizeObserver + DPR monitoring

(RAF batched)"]

      EventForwarding --> ForwardPointer["computePointerEventData()

(calculates grid coords on main thread)"]
      ResizeMonitoring --> ResizeRAF["RAF-batched resize messages"]
    end

    subgraph WorkerInbound["Main → Worker (src/worker/protocol.ts)"]
      CanvasTransfer -->|"postMessage: init"| WorkerInit["InitMessage + OffscreenCanvas transfer

(includes devicePixelRatio from main thread)"]
      ProxyInstance -->|"postMessage: setOption"| WorkerSetOpt["SetOptionMessage"]
      ProxyInstance -->|"postMessage: appendData"| WorkerAppend["AppendDataMessage + ArrayBuffer transfer"]
      ResizeRAF -->|"postMessage: resize"| WorkerResize["ResizeMessage

(includes devicePixelRatio)"]
      ForwardPointer -->|"postMessage: forwardPointerEvent"| WorkerPointer["ForwardPointerEventMessage

(includes pre-computed grid coordinates)"]
      ProxyInstance -->|"postMessage: setZoomRange"| WorkerZoom["SetZoomRangeMessage"]
      ProxyInstance -->|"postMessage: setInteractionX"| WorkerInteractionX["SetInteractionXMessage"]
      ProxyInstance -->|"postMessage: dispose"| WorkerDispose["DisposeMessage"]
    end

    subgraph WorkerCore["Worker Thread: ChartGPUWorkerController (src/worker/ChartGPUWorkerController.ts)"]
      WorkerInit --> WGPUInit["GPUContext.create(offscreenCanvas)"]
      WGPUInit --> WOptions["resolveOptionsForChart(msg.options)

(adds bottom reserve when slider present)"]
      WOptions --> WCoordinator["createRenderCoordinator(gpuContext, resolvedOptions)

computeInteractionScalesGridCssPx

(supports OffscreenCanvas)"]
      WCoordinator --> WRenderLoop["MessageChannel render loop"]
      WorkerSetOpt --> WOptions
      WorkerAppend --> WDataStore["Worker DataStore (GPU buffer upload)"]
      WorkerResize --> WCoordinator
      WorkerPointer --> WHitTest["Worker hit-testing

(uses interactionScales with grid coords)

findNearestPoint/findPointsAtX"]
      WorkerZoom --> WCoordinator
      WorkerInteractionX --> WCoordinator
      WorkerDispose --> WCleanup["Resource cleanup"]
    end

    subgraph WorkerOutbound["Worker → Main (postMessage)"]
      WGPUInit -->|"ready"| ReadyMsg["ReadyMessage + GPU capabilities + PerformanceCapabilities"]
      WRenderLoop -->|"rendered"| RenderedMsg["RenderedMessage (frame stats)"]
      WRenderLoop -->|"performanceUpdate"| PerfMsg["PerformanceUpdateMessage (FPS, frame time, memory)"]
      WHitTest -->|"tooltipUpdate"| TooltipMsg["TooltipUpdateMessage

(complete tooltip content + position)"]
      WCoordinator -->|"legendUpdate"| LegendMsg["LegendUpdateMessage"]
      WCoordinator -->|"axisLabelsUpdate"| AxisMsg["AxisLabelsUpdateMessage"]
      WCoordinator -->|"annotationsUpdate"| AnnotationsMsg["AnnotationsUpdateMessage

(annotation label positions + styles)"]
      WHitTest -->|"hoverChange"| HoverMsg["HoverChangeMessage"]
      WHitTest -->|"click"| ClickMsg["ClickMessage"]
      WHitTest -->|"crosshairMove"| CrosshairMsg["CrosshairMoveMessage"]
      WCoordinator -->|"zoomChange"| ZoomMsg["ZoomChangeMessage"]
      WGPUInit -->|"deviceLost"| DeviceLostMsg["DeviceLostMessage"]
      WCleanup -->|"disposed"| DisposedMsg["DisposedMessage"]
      WCoordinator -->|"error"| ErrorMsg["ErrorMessage"]
    end

    subgraph MainThreadDOM["Main Thread: DOM Overlay Rendering (ChartGPUWorkerProxy)"]
      ReadyMsg --> ProxyOverlays
      ReadyMsg --> PerfCache["Cache PerformanceCapabilities + set isInitialized"]
      PerfMsg --> PerfUpdate["Cache PerformanceMetrics + notify callbacks"]
      TooltipMsg --> DOMTooltip["RAF-batched tooltip.show(x, y, content)

(receives complete tooltip data from worker)"]
      LegendMsg --> DOMLegend["RAF-batched legend.update(items, theme)"]
      AxisMsg --> DOMAxis["RAF-batched textOverlay.addLabel(...)

(auto-handles container overflow)"]
      AnnotationsMsg --> DOMAnnotations["RAF-batched annotationTextOverlay.addLabel(...)

(dedicated annotation overlay)"]
      HoverMsg --> DOMHover["Re-emit 'mouseover'/'mouseout' events"]
      ClickMsg --> DOMClick["Re-emit 'click' event"]
      CrosshairMsg --> DOMCrosshair["Update cached interactionX + emit"]
      ZoomMsg --> DOMZoom["Update cached zoomRange + zoomState"]

      ProxyOverlays --> DOMTooltip
      ProxyOverlays --> DOMLegend
      ProxyOverlays --> DOMAxis
      ProxyOverlays --> DOMAnnotations
    end
  end

  subgraph Renderers["GPU renderers (src/renderers/*)"]
    RenderPass --> GridR["Grid"]
    RenderPass --> AreaR["Area"]
    RenderPass --> BarR["Bar"]
    RenderPass --> ScatterR["Scatter"]
    RenderPass --> ScatterDensityR["Scatter density/heatmap"]
    RenderPass --> LineR["Line"]
    RenderPass --> PieR["Pie"]
    RenderPass --> CandlestickR["Candlestick"]
    RenderPass --> ReferenceLineR["Reference lines"]
    RenderPass --> AnnotationMarkerR["Annotation markers"]
    RenderPass --> CrosshairR["Crosshair overlay"]
    RenderPass --> HighlightR["Hover highlight overlay"]
    RenderPass --> AxisR["Axes/ticks"]

    WRenderLoop --> GridR
  end

  subgraph Shaders["WGSL shaders (src/shaders/*)"]
    GridR --> gridWGSL["grid.wgsl"]
    AreaR --> areaWGSL["area.wgsl"]
    BarR --> barWGSL["bar.wgsl"]
    ScatterR --> scatterWGSL["scatter.wgsl"]
    ScatterDensityR --> scatterDensityBinningWGSL["scatterDensityBinning.wgsl"]
    ScatterDensityR --> scatterDensityColormapWGSL["scatterDensityColormap.wgsl"]
    LineR --> lineWGSL["line.wgsl"]
    PieR --> pieWGSL["pie.wgsl"]
    CandlestickR --> candlestickWGSL["candlestick.wgsl"]
    ReferenceLineR --> referenceLineWGSL["referenceLine.wgsl"]
    AnnotationMarkerR --> annotationMarkerWGSL["annotationMarker.wgsl"]
    CrosshairR --> crosshairWGSL["crosshair.wgsl"]
    HighlightR --> highlightWGSL["highlight.wgsl"]
  end

  subgraph ChartSync["Chart sync (src/interaction/createChartSync.ts)"]
    SyncAPI --> ListenX["listen: 'crosshairMove'"]
    SyncAPI --> DriveX["setCrosshairX(...) on peers"]
  end

  InteractionX --> ListenX
  DriveX --> InstanceAPI
  CrosshairMsg --> ListenX

Demo

ChartGPU demo

Candlestick Charts

Financial OHLC (open-high-low-close) candlestick rendering with classic/hollow style toggle and color customization. The live streaming demo renders 5 million candlesticks at over 100 FPS with real-time updates.

Candlestick chart example

Scatter Density (1M points)

GPU-binned density/heatmap mode for scatter plots (mode: 'density') to reveal structure in overplotted point clouds. See docs/api/options.md#scatterseriesconfig and the demo in examples/scatter-density-1m/.

Scatter density chart example (1M points)

10M points (benchmark)

10,000,000 points rendered at ~120 FPS (benchmark mode).

10 million point benchmark at 120 FPS

Quick start

import { ChartGPU } from 'chartgpu';
const container = document.getElementById('chart')!;
await ChartGPU.create(container, {
  series: [{ type: 'line', data: [[0, 1], [1, 3], [2, 2]] }],
});

Worker-based rendering (optional)

For maximum performance with large datasets, use worker-based rendering to keep the main thread responsive:

import { ChartGPU } from 'chartgpu';
const container = document.getElementById('chart')!;
// Identical API, but rendering happens in a Web Worker
await ChartGPU.createInWorker(container, {
  series: [{ type: 'line', data: [[0, 1], [1, 3], [2, 2]] }],
});

When to use workers: - Large datasets (>10K points) with frequent updates - Real-time streaming data - Complex multi-series charts - Mobile/low-power devices

See Worker API Documentation

Extension points exported contracts — how you extend this code

ChartGPUInstance (Interface)
(no doc) [2 implementers]
src/ChartGPU.ts
RenderSchedulerInternalState (Interface)
* Internal mutable state for the render scheduler. * Stored separately from the public state interface.
src/core/RenderScheduler.ts
PerformanceTrackingState (Interface)
* Performance tracking state for a chart instance. * Circular buffer pattern for exact FPS measurement.
src/worker/ChartGPUWorkerController.ts
DataZoomSlider (Interface)
(no doc)
src/components/createDataZoomSlider.ts
AxisLabelThemeConfig (Interface)
(no doc)
src/utils/axisLabelStyling.ts
StreamBuffer (Interface)
(no doc)
src/data/createStreamBuffer.ts
ThemeConfig (Interface)
(no doc)
src/themes/types.ts
AnnotationMarkerRenderer (Interface)
(no doc)
src/renderers/createAnnotationMarkerRenderer.ts

Core symbols most depended-on inside this repo

scale
called by 78
src/utils/scales.ts
destroy
called by 61
src/core/GPUContext.ts
assertEquals
called by 48
examples/acceptance/area-style-color.ts
dispose
called by 44
src/ChartGPU.ts
resize
called by 35
src/ChartGPU.ts
getRange
called by 34
src/interaction/createZoomState.ts
dispose
called by 30
src/core/createRenderCoordinator.ts
prepare
called by 27
src/renderers/createPieRenderer.ts

Shape

Function 958
Method 226
Interface 137
Class 16

Languages

TypeScript100%

Modules by API surface

src/core/createRenderCoordinator.ts155 symbols
src/ChartGPU.ts87 symbols
src/worker/ChartGPUWorkerProxy.ts59 symbols
src/config/types.ts40 symbols
examples/worker-rendering/main.ts33 symbols
examples/million-points/main.ts33 symbols
src/config/OptionResolver.ts31 symbols
src/worker/ChartGPUWorkerController.ts29 symbols
src/renderers/createScatterDensityRenderer.ts28 symbols
src/worker/protocol.ts27 symbols
src/core/GPUContext.ts27 symbols
src/renderers/createBarRenderer.ts26 symbols

Dependencies from manifests, versioned

@types/node25.0.9 · 1×
@webgpu/types0.1.69 · 1×
tsx4.21.0 · 1×
typescript5.0.0 · 1×
vite5.0.0 · 1×

For agents

$ claude mcp add ChartGPU \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact