Chart configuration. Full types: types.ts.
theme:'dark' | 'light'orThemeConfigDataPoint: tuple[x, y, size?]or object{ x, y, size? }autoScroll?: boolean: whentrue,appendData()keeps visible x-range anchored to newest data (only when data zoom enabled andxAxis.min/maxunset). Demo:live-streaming/
annotations?: ReadonlyArray<AnnotationConfig>: overlays (lineX, lineY, point, text).position.space: 'plot'uses fractions [0,1] for x/y. Full guide: annotations.md. Authoring:annotation-authoring/.
SeriesType:'line' | 'area' | 'bar' | 'scatter' | 'pie' | 'candlestick'. Seetypes.ts.- Sampling (cartesian):
sampling?: 'none' | 'lttb' | 'average' | 'max' | 'min',samplingThreshold?: number(default 5000). Zoom-aware resampling when data zoom enabled. visible?: boolean: hide series from rendering and interaction. Legend toggle updates both.
- Data:
OHLCDataPoint— tuple[timestamp, open, close, low, high]or object. style?: 'classic' | 'hollow'.sampling?: 'none' | 'ohlc'for bucket aggregation. Body-only hit-testing. Demos:candlestick/,candlestick-streaming/.- Acceptance test: for OHLC sampling validation (endpoint preservation, aggregation rules, edge cases), see
examples/acceptance/ohlc-sample.ts.
lineStyle?: { width?, opacity?, color? }(default width 2).areaStyle?for fill under line. Color precedence:lineStyle.color→series.color→ palette.
Line and area series support null entries in DataPoint[] arrays to represent gaps (disconnected segments):
series: [{
type: 'line',
data: [[0, 1], [1, 3], null, [3, 5], [4, 7]], // gap between x=1 and x=3
}]connectNulls?: boolean(default:false): whentrue, null entries are stripped and the line/area draws through the gap. Whenfalse, null entries produce visible gaps.- Multi-segment pattern: concatenate pre-split data with null separators:
[...segment1, null, ...segment2]. - Sampling: when data contains null gaps and sampling is enabled, ChartGPU bypasses sampling and uses raw data to preserve gap positions. Gap-aware sampling may be added in a future release.
- Supported formats:
DataPoint[]only.XYArraysDataandInterleavedXYDatado not support null gaps.
baseline?: number(default: y-axis min).areaStyle?: { opacity?, color? }.
Extends the shared series fields with type: 'bar' and bar-specific layout/styling. See types.ts.
barWidth?: number | string: bar width in CSS pixels (number) or as a percentage string.- When
barWidthis a percentage string (e.g.'80%'), it is interpreted as a percentage of the maximum per-bar width that still avoids overlap within a category, given the currentclusterCount(derived fromstack) andbarGap.'100%'equals the default “auto” width (the max non-overlap width).- Values are clamped to ([0, 100])%, and the resulting layout guarantees
clusterWidth <= categoryInnerWidth(no intra-category overlap).
- When
barWidthis a number (CSS px), it is clamped to the same maximum non-overlap per-bar width. - When omitted, ChartGPU uses the same “auto” width (max non-overlap) behavior.
- When
barGap?: number: gap between bars in the same category/group (ratio in ([0, 1])). Default:0.01(minimal gap). To make grouped bars flush (no gap between bars within a group), setbarGapto0or a value near0. SeecreateBarRenderer.ts.barCategoryGap?: number: gap between categories (ratio in ([0, 1])). Default:0.2. SeecreateBarRenderer.ts.stack?: string: stack group id (bars with the same id may be stacked).itemStyle?: BarItemStyleConfig: per-bar styling.- Rendering (current): bar series render as clustered bars per x-category via an instanced draw path. If multiple bar series share the same non-empty
stackid, they render as stacked segments within the same cluster slot (positive values stack upward from the baseline; negative values stack downward). Bars are clipped to the plot grid (scissor) so they do not render into the chart margins. SeecreateBarRenderer.ts, shader sourcebar.wgsl, and coordinator wiring increateRenderCoordinator.ts. For an example, seeexamples/grouped-bar/.- Note: y-axis auto bounds are currently derived from raw series y-values (not stacked totals). If stacked bars clip, set
yAxis.min/yAxis.max.
- Note: y-axis auto bounds are currently derived from raw series y-values (not stacked totals). If stacked bars clip, set
Extends the shared series fields with type: 'scatter', optional symbol?: ScatterSymbol, and optional symbolSize?: number | ((value: ScatterPointTuple) => number). See types.ts.
- Scatter point tuples may include an optional third
sizevalue (readonly [x, y, size?]). - Rendering (current): scatter series render as instanced circles (SDF + alpha blending). Size is treated as a radius in CSS pixels from either the per-point
size(when provided) orseries.symbolSizeas a fallback. See the internal renderercreateScatterRenderer.tsand shaderscatter.wgsl. mode?: 'points' | 'density': scatter rendering mode. Default:'points'.- When
mode === 'points', ChartGPU draws individual point markers (current behavior). - When
mode === 'density', ChartGPU renders a binned density heatmap in screen space (useful for very large point clouds where markers overplot).
- When
binSize?: number: density bin size in CSS pixels (used only whenmode === 'density'). Default:2.- DPR behavior:
binSizeis specified in CSS px, but bins are computed in device pixels usinground(binSize * devicePixelRatio)(minimum 1 device pixel). This keeps the visual bin size roughly consistent across displays. SeecreateScatterDensityRenderer.ts.
- DPR behavior:
densityColormap?: 'viridis' | 'plasma' | 'inferno' | readonly string[]: colormap used for density rendering (used only whenmode === 'density'). Default:'viridis'.- Named presets:
'viridis' | 'plasma' | 'inferno'use built-in “anchor” color stops that are interpolated into a 256-entry lookup table. SeecreateScatterDensityRenderer.ts. - Custom gradient: a
readonly string[]is interpreted as a low→high gradient of CSS color strings (interpolated into a 256-entry lookup table). Invalid color strings fall back to black. Note: the current density shader renders output as fully opaque (alpha = 1.0) regardless of any alpha in your color stops. SeecreateScatterDensityRenderer.tsandscatterDensityColormap.wgsl.
- Named presets:
densityNormalization?: 'linear' | 'sqrt' | 'log': normalization curve used to map per-bin counts to color intensity (used only whenmode === 'density'). Default:'log'.- Normalization is applied relative to the maximum bin count in the current view: linear uses
count / max, sqrt usessqrt(count / max), and log useslog1p(count) / log1p(max). This means color intensity can rescale as you zoom/pan (because the per-view max changes). SeescatterDensityColormap.wgsl.
- Normalization is applied relative to the maximum bin count in the current view: linear uses
Notes (density mode):
- Correctness (important): density mode uses raw (unsampled) points for binning, even when
samplingis configured, to avoid undercounting. - Zoom/pan behavior: density is recomputed as the view changes. When x-values are monotonic, ChartGPU limits compute to the current visible x-range; otherwise it may process the full series. See the coordinator wiring in
createRenderCoordinator.tsand compute shaderscatterDensityBinning.wgsl. - Performance vs resolution:
binSizetrades resolution for performance. Smaller bins increase detail but increase both bin count and compute cost per recompute. - Example: for a working 1M-point density scatter demo (including controls for colormap, normalization, and bin size), see
examples/scatter-density-1m/.
- Non-cartesian. No x/y bounds or cartesian hit-test. Slice hit-test via
findPieSlice.radius?,center?,startAngle?. Example:pie/.
AxisConfig: configuration forxAxis/yAxis. Seetypes.ts.- Explicit domains (override auto-bounds):
AxisConfig.min?: number/AxisConfig.max?: number: when set, ChartGPU uses these explicit axis bounds and does not auto-derive bounds from data for that axis.- Precedence: explicit
min/maxalways override any auto-bounds behavior.
- Y-axis auto-bounds during x-zoom (new default):
yAxis.autoBounds?: 'visible' | 'global'controls how ChartGPU derives the y-axis domain whenyAxis.min/yAxis.maxare not set.'visible'(default): when x-axis data zoom is active, ChartGPU derives y-bounds from the visible (zoomed) x-range.'global': derive y-bounds from the full dataset (pre-zoom behavior), even while x-zoomed.
- This option is intended for
yAxis(it has no effect onxAxis).
AxisConfig.tickFormatter?: (value: number) => string | null: custom formatter for axis tick labels. When provided, replaces the built-in tick label formatting for that axis.- For
type: 'value'axes,valueis the numeric tick value. - For
type: 'time'axes,valueis a timestamp in milliseconds (epoch-ms, same unit asnew Date(ms)). - Return a
stringto display as the label, ornullto suppress that specific tick label. - When omitted, ChartGPU uses its built-in formatting:
Intl.NumberFormatfor value axes, adaptive tier-based date formatting for time axes. - The formatter is also used for label width measurement in the adaptive time x-axis tick count algorithm, ensuring overlap avoidance uses the correct label widths.
- For
// Duration formatting (seconds → human-readable)
yAxis: {
tickFormatter: (seconds) => {
const d = Math.floor(seconds / 86400);
const h = Math.floor((seconds % 86400) / 3600);
return d > 0 ? `${d}d ${h}h` : `${h}h`;
}
}
// Percentage formatting (0–1 → 0%–100%)
yAxis: { tickFormatter: (v) => `${(v * 100).toFixed(0)}%` }
// Integer-only ticks (suppress fractional labels)
xAxis: { tickFormatter: (v) => Number.isInteger(v) ? v.toLocaleString() : null }
// Custom time axis formatting
xAxis: {
type: 'time',
tickFormatter: (ms) => new Date(ms).toLocaleDateString('de-DE')
}
// Append units
yAxis: { tickFormatter: (v) => `${v.toFixed(1)} ms` }-
xAxis.type: 'time'(timestamps): whenxAxis.type === 'time', x-values are interpreted as timestamps in milliseconds since Unix epoch (the same unit accepted bynew Date(ms)), including candlesticktimestampvalues. For GPU precision, ChartGPU may internally rebase large time x-values (e.g. epoch-ms domains) before uploading to Float32 vertex buffers; this is automatic and does not change your units. See the runtime axis label/tick logic increateRenderCoordinator.ts. -
Time x-axis tick labels (automatic tiers): when
xAxis.type === 'time', x-axis tick labels are formatted based on the current visible x-range (after data zoom):Visible x-range (approx.) Label format < 1 dayHH:mm1–7 daysMM/DD HH:mm~1–12 weeks(or< ~3 months)MM/DD~3–12 months(≤ ~1 year)MMM DD> ~1 yearYYYY/MMNotes: month/year thresholds are approximate (30d / 365d), and formatting uses the browser's
Datesemantics (local timezone). SeecreateRenderCoordinator.ts. -
Adaptive tick count (overlap avoidance, time x-axis only): when
xAxis.type === 'time', ChartGPU may vary the tick count per render to avoid DOM label overlap. It picks the largest tick count in [1, 9] whose measured labels do not overlap (minimum gap 6 CSS px); if measurement isn't available it falls back to the default tick count. GPU tick marks and DOM tick labels use the same computed tick count. SeecreateRenderCoordinator.tsandcreateAxisRenderer.ts. -
AxisConfig.name?: string: renders an axis title for cartesian charts when provided (and non-empty aftertrim()): x-axis titles are centered below x-axis tick labels, and y-axis titles are rotated (-90°) and placed left of y-axis tick labels; titles can be clipped ifgrid.bottom/grid.leftmargins are too small. WhendataZoomincludes a slider (see below), ChartGPU reserves extra bottom space so the x-axis title remains visible above the slider overlay and is centered within the remaining space above the slider track. SeecreateRenderCoordinator.tsand option resolution inOptionResolver.ts. -
Axis title styling: titles are rendered via the internal DOM text overlay and use the resolved theme's
textColorandfontFamilywith slightly larger, bold text (label elements also setdir='auto').
ChartGPUOptions.gridLines?: GridLinesConfig: optional configuration for the background grid lines drawn inside the plot area. SeeGridLinesConfiganddefaults.ts.- What grid lines are: grid lines are evenly-spaced lines drawn across the plot grid to aid visual reading of values. They are not aligned to axis ticks — they are distributed uniformly across the horizontal and vertical extent of the plot area. Lines are rendered via a WebGPU
line-listprimitive, so they are always 1 device pixel wide (this is a WebGPU limitation; line width is not configurable). - Default behavior: when
gridLinesis omitted, grid lines are shown using the theme'sgridLineColor(dark theme:rgba(255,255,255,0.1); light theme:rgba(0,0,0,0.1)) with 5 horizontal and 6 vertical lines. SeedefaultGridLinesandcreateGridRenderer.ts.
Top-level grid lines configuration. See types.ts.
show?: boolean: global toggle for all grid lines. Whenfalse, no grid lines are drawn. Default:true.color?: string: CSS color string for all grid lines. Can be overridden per-direction viahorizontal.colororvertical.color. Falls back totheme.gridLineColorwhen not specified. Expected formats:#rgb,#rrggbb,#rrggbbaa,rgb(r,g,b),rgba(r,g,b,a).opacity?: number: global opacity multiplier for grid lines (0–1). This multiplies the alpha channel of the resolved color (including per-direction overrides). Default:1.horizontal?: boolean | GridLinesDirectionConfig: horizontal grid lines (constant-Y, spanning left→right). Accepts a boolean shorthand (true= show with defaults,false= hide) or a detailedGridLinesDirectionConfig. Default:{ show: true, count: 5 }.vertical?: boolean | GridLinesDirectionConfig: vertical grid lines (constant-X, spanning top→bottom). Accepts a boolean shorthand (true= show with defaults,false= hide) or a detailedGridLinesDirectionConfig. Default:{ show: true, count: 6 }.
Per-direction (horizontal or vertical) grid line settings. See types.ts.
show?: boolean: whether to show grid lines in this direction. Whenfalse, no lines are drawn regardless ofcount. Default:true.count?: number: number of evenly-spaced grid lines. Default:5(horizontal),6(vertical).color?: string: CSS color string for lines in this direction. Overrides the top-levelgridLines.colorandtheme.gridLineColor.
gridLines: { show: false } // hide all
gridLines: { horizontal: true, vertical: false } // horizontal only
gridLines: { color: 'rgba(100,100,255,0.2)', horizontal: { count: 8 }, vertical: { count: 10 } }ChartGPUOptions.dataZoom?: ReadonlyArray<DataZoomConfig>: optional data-zoom configuration list. SeeChartGPUOptionsandDataZoomConfig.- Runtime behavior (current): data zoom controls a shared percent-space zoom window
{ start, end }in ([0, 100]) that is applied to the effective x-domain for both rendering and pointer interaction. See the x-domain application increateRenderCoordinator.tsand percent-space semantics increateZoomState.ts.- Span constraints (min/max): ChartGPU clamps the zoom window span using
DataZoomConfig.minSpan/DataZoomConfig.maxSpanat runtime (applies consistently to inside zoom, slider UI, and programmatic APIs), and re-applies constraints when options/data change (e.g. onsetOption(...)and streamingappendData(...)). SeecreateZoomState.tsand coordinator wiring increateRenderCoordinator.ts.- When
minSpan/maxSpanare omitted, ChartGPU uses a dataset-aware default minimum span forxAxis.type: 'value' | 'time', targeting roughly one data interval: approximately (100/(N-1))% where (N) is the largest raw point count across non-pie cartesian series (raw/unsampled data; updates as points are appended). When there is insufficient data to infer an interval ((N < 2)), ChartGPU falls back to 0.5% to keep the UI usable. SeecreateRenderCoordinator.ts. - For
xAxis.type: 'category', no dataset-aware default is currently applied; useminSpanexplicitly if you need deeper zoom for very large category counts.
- When
- Zoom-aware sampling (cartesian): when a cartesian series has sampling enabled, ChartGPU resamples from raw (unsampled) series data over the current visible x-range; visible-range slicing is applied immediately during zoom/pan for smooth visual feedback, while full resampling is debounced (~100ms) for performance; a ±10% buffer zone reduces resampling frequency during small pans. See
createRenderCoordinator.ts. - Inside zoom: when
ChartGPUOptions.dataZoomincludes{ type: 'inside' }, ChartGPU enables an internal wheel/drag interaction. SeecreateRenderCoordinator.tsandcreateInsideZoom.ts. - Zoom gesture: mouse wheel zoom, centered on the current cursor x-position (only when the pointer is inside the plot grid).
- Pan gesture: shift+left-drag or middle-mouse drag pans left/right (only when the pointer is inside the plot grid).
- Scope: the zoom window is applied to the x-domain; the y-domain is derived from data unless you set explicit
yAxis.min/yAxis.max.- Default behavior: during x-zoom,
yAxis.autoBounds: 'visible'derives y-bounds from the visible x-range. - Opt out: set
yAxis.autoBounds: 'global'to keep y-bounds derived from the full dataset, or set explicityAxis.min/yAxis.max.
- Default behavior: during x-zoom,
- Grid-only: input is ignored outside the plot grid (respects
gridmargins). - Slider UI: when
ChartGPUOptions.dataZoomincludes{ type: 'slider' }, ChartGPU mounts a slider-style UI that manipulates the same percent zoom window. ChartGPU also reserves 40 CSS px of additional bottom plot space so x-axis tick labels and the x-axis title remain visible above the slider overlay (you generally should not need to manually “make room” by increasinggrid.bottom). SeeChartGPU.ts, option resolution inOptionResolver.ts, and the internal UI helpercreateDataZoomSlider.ts. - Coexistence: multiple data-zoom configs can coexist (e.g. inside + slider) and drive the same x-zoom window.
- Config fields:
start/endare used as the initial percent window (defaulting to0/100when omitted).minSpan/maxSpanare applied to the runtime clamping behavior (see above).xAxisIndexis currently accepted by the type but onlyxAxisIndex: 0is supported by the runtime zoom path.
- Span constraints (min/max): ChartGPU clamps the zoom window span using
DataZoomConfig: data zoom configuration type. SeeDataZoomConfig.type: 'inside' | 'slider'xAxisIndex?: numberstart?: number: start percent in ([0, 100])end?: number: end percent in ([0, 100])minSpan?: numbermaxSpan?: number
tooltip?: TooltipConfig:show,trigger: 'item' | 'axis',formatter. Enabled by default.TooltipParams.value:[x, y](cartesian/pie),[timestamp, open, close, low, high](candlestick). Distinguish viavalue.length.- Safety: tooltip uses
innerHTML— return trusted/sanitized strings only. - Helpers:
formatTooltipItem,formatTooltipAxisinformatTooltip.ts. Examples:basic-line/,interactive/.
ChartGPUOptions.animation?: AnimationConfig | boolean: optional animation configuration.- Default: when omitted, animation is enabled with defaults (equivalent to
true). SeeOptionResolver.ts. - Disablement: set to
falseto disable all animation. - Defaults: when enabled,
AnimationConfig.durationdefaults to300ms when omitted.
- Default: when omitted, animation is enabled with defaults (equivalent to
AnimationConfig: supports optionalduration?: number(ms),easing?: 'linear' | 'cubicOut' | 'cubicInOut' | 'bounceOut', anddelay?: number(ms). Seetypes.ts.- Built-in easing implementations (internal): see
easing.tsand the name→function helpergetEasing(...).
- Built-in easing implementations (internal): see
- Initial-load intro animation: when animation is enabled, series marks animate on first render. Axes, grid lines, and labels render immediately (not animated). Per-series effects: line/area series reveal left-to-right via plot scissor; bar series grow upward from baseline; pie slices expand radius; scatter points fade in. The intro animation requests frames internally during the transition. See
createRenderCoordinator.ts. Streaming demos may prefer disabling animation (animation: false). - Data update transition animation: when animation is enabled, subsequent calls to
ChartGPUInstance.setOption(...)(and the internalRenderCoordinator.setOptions(...)) that change series data can animate transitions after the initial render has occurred. See the internal implementation increateRenderCoordinator.tsand the visual acceptance examples inexamples/data-update-animation/(bar + line + pie) andexamples/multi-series-animation/(area + bar + line + scatter on a single chart, with configurable line width).- When it triggers (high-level): a post-initial-render options update that changes
series[i].data(cartesian and pie), withChartGPUOptions.animationenabled. - What animates (high-level):
- Cartesian series: y-values interpolate by index while x-values come from the new series (index-aligned). Bars morph via the same y interpolation.
- Pie series: slice values interpolate by index, producing animated angle changes.
- Derived domains/scales: when auto-derived axis domains change (from updated data), the domain values animate to the new extents.
- Constraints / notes (high-level):
- Match-by-index: interpolation is index-based; length changes and type/shape mismatches may skip interpolation and apply the new series immediately.
- Large-series safeguard: very large series may skip per-point interpolation while still animating derived domains (internal safeguard).
- Mid-flight updates: a new
setOption(...)during an active transition rebases the transition from the current displayed state (avoids a visual jump).
- When it triggers (high-level): a post-initial-render options update that changes
OHLCDataPoint (public export): exported from the public entrypoint src/index.ts and defined in types.ts. Represents a candlestick data point as either a tuple (readonly [timestamp, open, close, low, high], ECharts order) or an object (Readonly<{ timestamp, open, close, low, high }>). Used by CandlestickSeriesConfig. Both candlestick rendering and OHLC sampling are fully functional (see CandlestickSeriesConfig above).
candlestickDefaults (public export): exported from the public entrypoint src/index.ts and defined in defaults.ts. Provides default configuration values for candlestick series. Both candlestick rendering and OHLC sampling are fully functional (see CandlestickSeriesConfig above).
Default chart options used as a baseline for resolution.
See defaults.ts for the defaults (including grid, grid lines, palette, and axis defaults).
Behavior notes (essential):
- Default grid:
left: 60,right: 20,top: 40,bottom: 40 - Palette / series colors:
ChartGPUOptions.paletteacts as an override for the resolved theme palette (resolvedOptions.theme.colorPalette). Whenseries[i].coloris missing, the default series color comes fromresolvedOptions.theme.colorPalette[i % ...]. For backward compatibility, the resolvedpaletteis the resolved theme palette. SeeresolveOptionsandThemeConfig. - Line series stroke color precedence: for
type: 'line', effective stroke color follows:lineStyle.color→series.color→ theme palette. SeeresolveOptions. - Line series fill color precedence: for
type: 'line'withareaStyle, effective fill color follows:areaStyle.color→ resolved stroke color (from above precedence). SeeresolveOptions. - Area series fill color precedence: for
type: 'area', effective fill color follows:areaStyle.color→series.color→ theme palette. SeeresolveOptions. - Axis ticks:
AxisConfig.tickLengthcontrols tick length in CSS pixels (default: 6)
PerformanceMetrics:fps,frameTimeStats(min/max/avg/p50/p95/p99),gpuTiming,memory,frameDrops,totalFrames,elapsedTime. Returnsnullbefore first frame.PerformanceCapabilities:gpuTimingSupported,highResTimerSupported,performanceMetricsSupported.- Branded types:
ExactFPS,Milliseconds,Bytes. Seetypes.ts.
resolveOptions(userOptions?: ChartGPUOptions) / OptionResolver.resolve(userOptions?: ChartGPUOptions)
Resolves user options against defaults by deep-merging user-provided values with defaults and returning a resolved options object.
See OptionResolver.ts for the resolver API and resolved option types.
Behavior notes (essential):
- Theme input:
ChartGPUOptions.themeaccepts'dark' | 'light'or aThemeConfig; the resolvedthemeis always a concreteThemeConfig. SeeChartGPUOptionsandresolveOptions. - Default theme: when
themeis omitted, the resolved theme defaults to'dark'viagetTheme(preset:darkTheme). - Theme name resolution:
resolveOptions({ theme: 'light' })resolvesthemeto the light preset config (seelightTheme). - Palette override: when
ChartGPUOptions.paletteis provided (non-empty), it overrides the resolved theme palette (resolvedOptions.theme.colorPalette). The resolvedpalettemirrors the resolved theme palette for backward compatibility. SeeresolveOptions.