npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brewsite/charts

v0.8.3

Published

`@brewsite/charts` adds native 3D chart elements to BrewSite scenes. Declare bar, line, area, pie, scatter, and heatmap charts with a JSX DSL and the engine renders them as animated Three.js geometry inside the BrewSite scene graph.

Readme

@brewsite/charts

1. Overview

@brewsite/charts adds native 3D chart elements to BrewSite scenes. Declare bar, line, area, pie, scatter, and heatmap charts with a JSX DSL and the engine renders them as animated Three.js geometry inside the BrewSite scene graph.

Current version: 2.1.0 — V1 upgraders see MIGRATION.md.

2. Installation

pnpm add @brewsite/charts

Peer dependencies:

| Package | Version | |---|---| | @brewsite/core | workspace:* or >=0.1.0 | | react | >=18 | | three | >=0.160 |

3. Quick Start — Inline Data

No ChartProvider required when data is passed inline:

import { useMemo } from 'react';
import { SceneEngine, Scene, corePlugin } from '@brewsite/core';
import { chartPlugin, BarChart, ChartAxis, ChartSeries, ChartLegend } from '@brewsite/charts';

const salesRows = [
  { month: 'Jan', revenue: 120, costs: 85 },
  { month: 'Feb', revenue: 140, costs: 92 },
  { month: 'Mar', revenue: 110, costs: 78 },
];

function SalesPage() {
  const charts = useMemo(() => chartPlugin(), []);
  return (
    <SceneEngine
      manifestUrl="/assets/manifest.json"
      plugins={[corePlugin(), charts]}
    >
      <Scene id="chart-scene">
        <BarChart id="revenue" data={salesRows} theme="darkGlass">
          <ChartAxis axis="x" field="month" label="Month" />
          <ChartAxis axis="y" field="revenue" label="Revenue ($)" />
          <ChartSeries field="revenue" label="Revenue" />
          <ChartSeries field="costs"   label="Costs" />
          <ChartLegend visible position="right" />
        </BarChart>
      </Scene>
    </SceneEngine>
  );
}

4. Plugin Setup

chartPlugin() returns a WidgetPlugin that registers chart DSL handlers and provides a per-engine ChartDataStore. Create one instance per SceneEngine:

import { useMemo } from 'react';
import { SceneEngine, SceneCanvas, EngineOverlayHost, corePlugin } from '@brewsite/core';
import { chartPlugin } from '@brewsite/charts';

function App() {
  const charts = useMemo(() => chartPlugin(), []);
  return (
    <SceneEngine
      plugins={[corePlugin(), charts]}
      getFrame={() => <MyScene />}
    >
      <SceneCanvas />
      <EngineOverlayHost />
    </SceneEngine>
  );
}

5. Data Sources

V2 supports three data source paths. Mix them freely across charts in the same scene.

Inline data

Pass rows or columnar data directly via the data prop. ChartProvider not required.

const rows = [
  { month: 'Jan', revenue: 120 },
  { month: 'Feb', revenue: 140 },
];

<BarChart id="revenue" data={rows}>
  <ChartAxis axis="x" field="month" />
  <ChartAxis axis="y" field="revenue" />
</BarChart>

Async fetch

Pass a URL via dataUrl. The widget fetches the data at runtime (JSON default, or CSV). The chart renders empty until the fetch resolves. ChartProvider not required.

<LineChart id="remote-chart" dataUrl="/api/metrics.json">
  <ChartAxis axis="x" field="month" />
  <ChartAxis axis="y" field="arr" />
</LineChart>

// CSV source:
<BarChart id="csv-chart" dataUrl="/data/sales.csv">
  {/* ... */}
</BarChart>

Named source with ChartProvider

Register named datasets once and reference them from multiple charts. Supports linked-brush filtering via filterGroup.

import { ChartProvider, BarChart, LineChart, ChartData } from '@brewsite/charts';

<ChartProvider data={{ sales: salesRows, kpis: kpiRows }}>
  <BarChart id="bar1">
    <ChartData source="sales" filterGroup="dashboard" />
    {/* ... */}
  </BarChart>

  <LineChart id="line1">
    <ChartData source="kpis" filterGroup="dashboard" />
    {/* ... */}
  </LineChart>
</ChartProvider>

useChartData hook

Read resolved data reactively in overlay components:

import { useChartData } from '@brewsite/charts';

function Overlay() {
  const data = useChartData('sales');
  return <div>{data.rows.length} rows</div>;
}

Reactive inline data — useLiveChartData (V2.1)

Propagate React state changes into an inline chart without recompiling the scene. Only effective for charts whose dataSource.type === 'inline' (i.e., charts using the data={rows} prop directly).

import { useMemo, useState } from 'react';
import { chartPlugin, BarChart, ChartAxis, useLiveChartData } from '@brewsite/charts';

function LiveDashboard() {
  const charts = useMemo(() => chartPlugin(), []);
  const [rows, setRows] = useState(initialRows);

  // Propagates rows into the chart on every reference change.
  // The scene DSL still needs data={initialRows} to seed the SceneTrack.
  useLiveChartData(charts, 'live-chart', rows);

  // Update rows externally (e.g., on interval, WebSocket, etc.):
  useEffect(() => {
    const id = setInterval(() => setRows(fetchLatest()), 5000);
    return () => clearInterval(id);
  }, []);

  return (
    <SceneEngine plugins={[charts]}>
      <Scene id="s1">
        <BarChart id="live-chart" data={initialRows} theme="darkGlass">
          <ChartAxis axis="x" field="month" />
          <ChartAxis axis="y" field="revenue" />
        </BarChart>
      </Scene>
    </SceneEngine>
  );
}

Notes:

  • The hook fires after first paint (useEffect). The very first frame shows initialRows; subsequent frames show live data. For most use cases this single-frame delta is invisible.
  • Has no effect on named (<ChartData source="...">) or async (dataUrl="...") data sources. Use ChartProvider + ChartDataStore.register() for reactive named sources.

6. Per-Type Components

V2 provides one component per chart type with narrowed TypeScript props. The deprecated <Chart type="..."> from V1 continues to work.

<BarChart>

<BarChart
  id="revenue"
  data={rows}
  orientation="vertical"
  stackMode="stacked"
  barPadding={0.2}
  theme="darkGlass"
  animateEntry          // V2.1: bars grow upward on scene entry
  animationDuration={0.4}  // V2.1: completes at 40% of blockProgress
>
  <ChartAxis axis="x" field="month" />
  <ChartAxis axis="y" field="revenue" />
  <ChartSeries field="revenue" label="Revenue" />
  <ChartSeries field="costs"   label="Costs" />
  <ChartLegend visible />
  <ChartDataLabels position="top" format=".0f" />
  <ReferenceLine axis="y" value={500} label="Target" />
</BarChart>

<LineChart>

<LineChart
  id="metrics"
  dataUrl="/api/data.json"
  lineShape="circle"
  lineSmoothness={0.5}
  showPoints={true}
>
  <ChartAxis axis="x" field="month" />
  <ChartAxis axis="y" field="value" gridlines />
  <ChartSeries field="arr"     label="ARR" />
  <ChartSeries field="revenue" label="Revenue" />
</LineChart>

<ScatterPlotChart> (4D encoding)

<ScatterPlotChart
  id="scatter"
  data={rows}
  sizeField="revenue"
  colorField="region"
  pointShape="sphere"
  sizeScale={{ min: 0.05, max: 0.3 }}
  colorInterpolator="viridis"
>
  <ChartAxis axis="x" field="acquisitionCost" label="CAC ($)" />
  <ChartAxis axis="y" field="ltv"             label="LTV ($)" />
</ScatterPlotChart>

<PieChart> / donut

<PieChart
  id="market-share"
  data={rows}
  innerRadius={0.4}
  pieTilt={0.4}
  explodeSlice="North America"
>
  <ChartAxis axis="x" field="region" />
  <ChartAxis axis="y" field="share" />
  <ChartLegend visible position="bottom" />
  <ChartDataLabels position="outside" format=".1%" />
</PieChart>

<AreaChart> (with band variant)

<AreaChart id="range" data={rows} stackMode="stacked">
  <ChartAxis axis="x" field="month" />
  <ChartAxis axis="y" field="revenue" />
  {/* Band area: fills between revenue and revenueMin */}
  <ChartSeries field="revenue" bandField="revenueMin" label="Revenue Range" />
</AreaChart>

<HeatMapChart>

<HeatMapChart
  id="heatmap"
  data={rows}
  timeField="week"
  colorInterpolator="blues"
>
  <ChartAxis axis="x" field="day" />
  <ChartAxis axis="y" field="hour" />
</HeatMapChart>

7. Multi-Series Example

<BarChart id="comparison" data={rows} theme="enterprise">
  <ChartAxis axis="x" field="quarter" label="Quarter" />
  <ChartAxis axis="y" field="revenue" label="Revenue ($k)" format=",.0f" gridlines />
  <ChartSeries field="revenue" label="Revenue" />
  <ChartSeries field="costs"   label="Costs" />
  <ChartSeries field="profit"  label="Profit" />
  <ChartLegend visible position="right" title="Metrics" />
  <ChartDataLabels position="top" format=".0f" />
</BarChart>

8. Scene-to-Scene Datum Morphing

Charts with the same id across consecutive scenes animate data values between matched data points when keyField is set. In V2.1, morphing is supported in BarChart, ScatterPlotChart, LineChart, and AreaChart.

// Scene A
<BarChart id="revenue-comparison" data={yearARows}>
  <ChartData keyField="quarter" />
  <ChartAxis axis="x" field="quarter" />
  <ChartAxis axis="y" field="revenue" />
</BarChart>

// Scene B — same id + keyField triggers datum-level morphing
<BarChart id="revenue-comparison" data={yearBRows}>
  <ChartData keyField="quarter" />
  <ChartAxis axis="x" field="quarter" />
  <ChartAxis axis="y" field="revenue" />
</BarChart>

The same pattern works for <LineChart> (Y positions morph) and <AreaChart> (upper and lower boundary points morph).

9. Axis Mapping Functions (V2.1)

Tier 1 — Serializable compute transforms

Derive new columns from existing fields inside the DSL. All operations are serializable and stored in the SceneTrack.

<ScatterPlotChart id="team-perf" sizeField="sqrt_headcount">
  <ChartData
    source="teams"
    transforms={[
      { type: 'compute', outputField: 'sqrt_headcount', operation: { fn: 'sqrt', inputField: 'headcount' } },
      { type: 'compute', outputField: 'log_revenue', operation: { fn: 'log', inputField: 'revenue', base: 10 } },
    ]}
  />
  <ChartAxis axis="x" field="teamSize" />
  <ChartAxis axis="y" field="log_revenue" label="Revenue (log₁₀)" />
</ScatterPlotChart>

Supported operations: log (with optional base), sqrt, normalize (maps to [0, 1] over dataset range), scale (multiply by constant), add (add constant).

Tier 2 — Runtime accessor functions

Attach arbitrary JavaScript accessor functions to a chart by ID. Accessors are not serialized into the SceneTrack — they live in plugin memory and persist across all scenes using the same chart ID.

import { useChartAccessors } from '@brewsite/charts';
import type { ChartAccessorFunctions } from '@brewsite/charts';

function MyComponent() {
  const charts = useMemo(() => chartPlugin(), []);

  // Stabilize the object with useMemo to avoid re-registering on every render:
  const accessors = useMemo<ChartAccessorFunctions>(() => ({
    sizeAccessor: (row) => Math.sqrt(Number(row.headcount)),
    colorAccessor: (row) => String(row.region),
  }), []);

  useChartAccessors(charts, 'team-perf', accessors);

  // ...
}

ChartAccessorFunctions has four optional fields: xAccessor, yAccessor, sizeAccessor, colorAccessor. Supported on BarChart, LineChart, and ScatterPlotChart. On unmount, renderers fall back to Number(row[field]).

10. DSL Reference

Shared Base Props (all per-type components)

| Prop | Type | Description | |---|---|---| | id | string | Required. Unique chart element ID | | data | DataInput | Inline rows or columnar data. Mutually exclusive with dataUrl | | dataUrl | string | URL for async JSON/CSV fetch. Mutually exclusive with data | | theme | ChartThemeName \| ChartTheme | Theme preset name or custom theme object | | opacity | number | Overall opacity 0–1. Default 1 | | interactive | boolean | Enable hover/click events. Default false | | x | number | NVS left edge [0, 1]. Default 0 | | y | number | NVS top edge [0, 1]. Default 0 | | w | number | NVS width [0, 1]. Default 1 | | h | number | NVS height [0, 1]. Default 1 | | z | number | World-space z offset. Default 0 | | rotation | [x, y, z] | Euler rotation in radians | | bounds | { width?, height?, depth? } | Geometry fill ratio within NVS region. width/height are NVS fractions [0..1]; depth is world-space thickness | | gridlines | boolean | Per-chart gridlines shorthand | | sceneTheme | SceneTheme | Cross-package font/color-mode context | | animateEntry | boolean | V2.1 — Enable bar-grow entry animation. Scoped to BarChart. Default false | | animationDuration | number | V2.1 — Entry animation duration as fraction of blockProgress [0..1]. Default 0.4 |

<ChartData>

| Prop | Type | Description | |---|---|---| | source | string | Named source registered via ChartProvider. Required for named path. | | keyField | string | Key field for datum-level morphing | | transforms | DataTransform[] | Transforms applied at resolve time. Includes 'compute' in V2.1 | | filterGroup | string | Linked-brush filter group ID |

<ChartAxis>

| Prop | Type | Description | |---|---|---| | axis | 'x' \| 'y' | Which axis to configure | | field | string | Data field name | | label | string | Axis label text | | format | string | d3-format string for tick labels | | scaleType | 'linear' \| 'log' \| 'time' \| 'band' \| 'sqrt' | Scale type | | domain | [min, max] | Fixed domain override | | tickCount | number | Approximate tick count | | nice | boolean | Round domain to nice values | | clamp | boolean | Clamp out-of-domain values | | reverse | boolean | Reverse axis direction | | gridlines | boolean | Show gridlines for this axis | | gridlineOpacity | number | Gridline opacity 0–1 |

<ChartSeries>

| Prop | Type | Description | |---|---|---| | field | string | Data field for this series | | label | string | Legend label text | | color | string | Override series color (hex) | | bandField | string | Lower-bound field for area band variant |

<ChartLegend>

| Prop | Type | Description | |---|---|---| | visible | boolean | Show/hide legend. Default true when present | | position | 'right' \| 'bottom' \| 'top' \| 'left' | Legend placement. Default 'right' | | title | string | Legend title text | | columns | number | Number of legend columns | | maxItems | number | Maximum legend entries to show |

<ChartDataLabels> (new in V2)

| Prop | Type | Description | |---|---|---| | position | 'top' \| 'center' \| 'outside' | Label placement. Default 'top' | | format | string | d3-format string. Default '.0f' |

<ReferenceLine> (new in V2)

| Prop | Type | Description | |---|---|---| | axis | 'x' \| 'y' | Which axis the value is on | | value | number | Axis value where the line appears | | label | string | Label text | | color | string | Line color (hex). Falls back to theme.referenceLines.defaultColor |

11. Chart Types

| Component | Type | Description | |---|---|---| | <BarChart> | bar | Vertical/horizontal bars, grouped or stacked multi-series | | <LineChart> | line | 3D tube lines with configurable cross-section shape and point markers | | <AreaChart> | area | Filled extruded area with stacked and band variants | | <PieChart> | pie | Pie/donut with configurable innerRadius, tilt, and slice explode | | <ScatterPlotChart> | scatter | 3D scatter plot with optional size and color encoding (4D) | | <HeatMapChart> | heatmap | 2D color-mapped grid with optional time animation |

12. Themes

Preset themes

| Name | Description | |---|---| | darkGlass | Dark background with glass-like transmission materials | | neonCyber | Vibrant neon palette with high emissive intensity | | enterprise | Muted professional palette on a light background | | lightMinimal | Clean light theme with flat opaque materials |

Pass a preset name string or import the theme constant directly:

import { darkGlassChartTheme } from '@brewsite/charts';
<BarChart id="rev" theme={darkGlassChartTheme}>...</BarChart>

Custom themes with createChartTheme

V2.1 adds five new optional token groups to ChartTheme. Existing theme objects work unchanged — new groups are optional with renderer fallback defaults.

import { createChartTheme } from '@brewsite/charts';

const brandTheme = createChartTheme('darkGlass', {
  name: 'brand',
  axis: {
    lineColor: '#ff4400',
    labelColor: '#ffffff',
    titleFontSize: 0.07,  // V2.1: independent axis title font size
  },
  legend: {
    textOpacity: 0.85,    // V2.1: legend label opacity
  },
  bar: { padding: 0.25 },    // V2.1: default barPadding when DSL prop absent
  area: { fillOpacity: 0.6 }, // V2.1: default fillOpacity
  gridlines: {               // V2.1: replaces deprecated background.gridColor
    color: '#4a8080',
    opacity: 0.2,
    visible: true,           // on by default for this theme
    dashSize: 0.03,          // dashed gridlines (requires LineDashedMaterial)
    gapSize: 0.02,
  },
  dataLabels: { fontSize: 0.05, color: '#e0e8ff' },
  referenceLines: { defaultColor: '#ff8844', lineWidth: 0.005, lineOpacity: 0.85 },
});

Cross-package font via sceneTheme

import { darkSceneTheme } from '@brewsite/core';

<BarChart
  id="rev"
  theme="darkGlass"
  sceneTheme={{
    ...darkSceneTheme,
    font: { ...darkSceneTheme.font, webglFontUrl: '/fonts/inter-msdf.ttf' },
  }}
>
  {/* ... */}
</BarChart>

13. Linked-Brush Filtering

Charts that share a filterGroup are automatically linked. Selecting data in one chart filters all others in the same group.

import { useChartFilter } from '@brewsite/charts';

function FilterControls() {
  const { applyFilter, clearFilters } = useChartFilter('dashboard');
  return (
    <div>
      <button onClick={() => applyFilter('month', ['Jan', 'Feb'])}>Jan+Feb</button>
      <button onClick={() => clearFilters()}>Clear</button>
    </div>
  );
}
<ChartProvider data={{ sales: rows }}>
  <BarChart id="chart-a">
    <ChartData source="sales" filterGroup="dashboard" />
  </BarChart>

  <LineChart id="chart-b">
    <ChartData source="sales" filterGroup="dashboard" />
  </LineChart>
</ChartProvider>

14. Interactivity

<BarChart id="revenue" interactive>
  {/* ... */}
</BarChart>
import { useMemo, useEffect } from 'react';
import { chartPlugin } from '@brewsite/charts';

const plugin = useMemo(() => chartPlugin(), []);

useEffect(() => {
  const chart = plugin.getWidget('revenue');
  if (chart) {
    chart.onHover  = (info) => setTooltipInfo(info);
    chart.onSelect = (info) => console.log('selected', info?.row);
  }
}, [plugin]);

Use ChartTooltipOverlay for built-in tooltip projection:

import { ChartTooltipOverlay } from '@brewsite/charts';

// nvsBounds required in V2; pass widget.nvsBounds or a fullscreen rect
<ChartTooltipOverlay nvsBounds={chartWidget.nvsBounds} />

15. TypeScript

Key exported types:

| Type | Description | |---|---| | ChartType | 'bar' \| 'line' \| 'area' \| 'pie' \| 'scatter' \| 'heatmap' | | ChartState | Compiled runtime state for one chart element | | ChartStateDataSource | Discriminated union: InlineDataSource \| NamedDataSource \| AsyncDataSource | | ChartTypeOptions | Discriminated union of per-type option bags | | BarChartOptions | Bar-specific options | | LineChartOptions | Line-specific options | | ScatterChartOptions | Scatter-specific options | | PieChartOptions | Pie-specific options | | AreaChartOptions | Area-specific options | | HeatMapChartOptions | Heatmap-specific options | | ChartTheme | Complete theme token set | | ChartThemeName | Preset theme name union | | ChartPluginInstance | Return type of chartPlugin() | | ChartAccessorFunctions | V2.1 — Function accessor object for useChartAccessors | | ChartHoverInfo | Hover/select event payload | | ChartProviderProps | Props for <ChartProvider> | | DataTransform | Union of transform descriptors (filter, sort, groupBy, bin, compute) | | ComputeTransform | V2.1 — Serializable column-derive transform | | ResolvedDataFrame | { rows, fields } resolved from the data store | | DataInput | DataRow[] \| ColumnarData — accepted by the data prop | | ChartBarTokens | V2.1 — Bar chart theme defaults token group | | ChartAreaTokens | V2.1 — Area chart theme defaults token group | | ChartGridlinesTokens | V2.1 — Gridline visual token group | | ChartDataLabelsTokens | V2.1 — Data label theme token group | | ChartReferenceLineTokens | V2.1 — Reference line theme token group |

16. V1 Migration

See MIGRATION.md for the complete V1 → V2 migration guide, including:

  • Replacing <Chart type="..."> with per-type components
  • Migrating state.dataSource (string → discriminated union)
  • Migrating state.lineShape, state.innerRadius, etc. (flat → typeConfig.options)
  • Updating ChartTooltipOverlay props (camera/domElementnvsBounds)
  • Converting bounds.width/height from world-units to NVS fractions
  • V2.1 notes for custom renderer authors

17. License

MIT