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

@stacklatte/chart-web

v1.2.0

Published

React trading chart for the web — plain HTML5 Canvas 2D, no WASM, no react-native-web. Candlesticks, RSI/MACD/Bollinger/Volume, trade markers, pinch-zoom, live streaming. Ships an LLM-safe declarative ChartConfig + JSON Schema so agents can generate chart

Readme

@stacklatte/chart-web

High-performance web trading charts — rendered with plain HTML5 Canvas 2D. No WebAssembly, no react-native-web shim: just React + a <canvas>.

npm License: MIT

This package renders the exact same chart engine as @stacklatte/chart-core (the React Native / Skia version) — same props, same indicators, same themes, same config pipeline. Every piece of pixel-geometry and indicator math is imported directly from @stacklatte/chart-core's platform-agnostic core (@stacklatte/chart-core/core); only the rendering layer (Canvas 2D draw calls instead of Skia JSX) and the gesture layer (Pointer Events + wheel instead of react-native-gesture-handler) are reimplemented for the browser.


Why chart-web

  • One component, not a kit. Candlesticks, line series, SMA/EMA/Bollinger Bands overlays, RSI + MACD + volume sub-panels, trade markers, wheel/touch pan-zoom, crosshair inspection, and live-streaming updates all ship built in.
  • Real indicator math. RSI uses Wilder smoothing, MACD is seeded and aligned correctly (12/26/9), Bollinger Bands use an O(n) sliding window — the same tested implementation chart-core uses, not a reimplementation that can drift.
  • No WASM, no shim. Plain <canvas> + ctx.fillText — no CanvasKit download, no react-native-web dependency graph. Only peers are react and react-dom.
  • LLM-safe declarative config. buildChartPipeline() takes a versioned ChartConfig document — every color and interval is a closed enum, never a raw string an LLM can typo — and returns validated, ready-to-render props, or throws a precise, itemized error. A generated JSON Schema ships alongside it for structured output / tool-calling. See llms.txt for the quick path.
  • Also available for React Native. @stacklatte/chart-core is the same engine, same props, GPU-rendered via Skia on iOS/Android.

| | chart-web / chart-core | General-purpose chart libs (Recharts, Victory) | Web-only trading chart libs | |---|---|---|---| | Candlesticks + OHLC | ✅ built in | 🛠 build it yourself | ✅ | | RSI / MACD / Bollinger / Volume | ✅ built in | ❌ | varies by plugin | | Trade markers (entry/exit/SL/TP) | ✅ built in, pan/zoom-synced | 🛠 build it yourself | varies | | Pan, wheel-zoom, touch-pinch, crosshair | ✅ built in | 🛠 manual | ✅ | | Native (iOS/Android) and web | ✅ two packages, one shared core | web only | web only | | Declarative config + JSON Schema | ✅ | ❌ | ❌ | | TypeScript, strict | ✅ | varies | varies |


Install

npm install @stacklatte/chart-web

That's it — react and react-dom are the only peer dependencies.


Peer dependencies

| Package | Version | |---|---| | react | >=19.0.0 | | react-dom | >=19.0.0 |


Candlestick chart

import { SLChart } from '@stacklatte/chart-web';
import type { Candle } from '@stacklatte/chart-web';

const candles: Candle[] = [
  { timestamp: 1700000000000, open: 42000, high: 43500, low: 41800, close: 43100 },
  { timestamp: 1700003600000, open: 43100, high: 44200, low: 42900, close: 44000 },
  // ...
];

export default function TradingScreen() {
  return <SLChart data={candles} width={800} height={400} theme="dark" />;
}

Line chart

<SLChart data={candles} width={800} height={400} chartType="line" theme="dark" />

Moving average overlays

import { SLChart, calcSMA, calcEMA } from '@stacklatte/chart-web';

const sma20 = calcSMA(candles, 20, 'sma-20', '#F5A623');
const ema9  = calcEMA(candles,  9, 'ema-9',  '#4a90e2');
const ema21 = calcEMA(candles, 21, 'ema-21', '#50E3C2');

<SLChart
  data={candles}
  indicators={[sma20, ema9, ema21].filter(Boolean)}
  width={800}
  height={400}
/>

Bollinger Bands

import { SLChart, calcBollingerBands } from '@stacklatte/chart-web';

const bb = calcBollingerBands(candles, 20, 2, {
  upper: '#9B59B6', basis: '#7F8C8D', lower: '#9B59B6',
});

<SLChart
  data={candles}
  indicators={bb ? [bb.upper, bb.basis, bb.lower] : []}
  shadedAreas={bb ? [{ fromId: 'bb-upper', toId: 'bb-lower', color: '#9B59B6', opacity: 0.1 }] : []}
  width={800}
  height={400}
/>

RSI / MACD / volume sub-panels

<SLChart data={candles} showRsiPanel showMacdPanel showVolumePanel width={800} height={720} />

showVolumePanel reads candle.volume — add a volume field to your Candle data to enable it (see Data format).


Trade markers

Annotate entries, exits, stop-losses, and take-profits directly on the price panel. Markers are positioned with the exact same pixel math as the candles, so they stay perfectly in sync through pan, zoom, and live updates — no external state to keep in sync yourself.

import type { ChartMarker } from '@stacklatte/chart-web';

const markers: ChartMarker[] = [
  { timestamp: 1700003600000, price: 43100, kind: 'entry-long', label: 'Entry' },
  { timestamp: 1700010800000, price: 44500, kind: 'take-profit', label: 'TP hit', changePct: 3.2 },
  { timestamp: 1700014400000, price: 42800, kind: 'stop-loss', label: 'Stopped out', changePct: -1.8 },
];

<SLChart data={candles} markers={markers} width={800} height={400} />

| kind | Shape | Default color | |---|---|---| | entry-long | ▲ | candleUp | | exit-long | ▼ | neutral (markerNeutral) | | entry-short | ▼ | candleDown | | exit-short | ▲ | neutral (markerNeutral) | | stop-loss | ✕ | candleDown | | take-profit | ● | candleUp |

Markers render at a fixed pixel size regardless of zoom level, and are clipped to the visible time window automatically.

Hover tooltip

Hovering a marker (mouse; no extra prop needed — this works independently of showOhlcHud) shows a small tooltip with its price, timestamp, and:

  • label, if set — otherwise a heading derived from kind (e.g. "Stop-loss", "Exit (long)").
  • changePct, if set — a signed, colored +/-X.XX% row, handy for a closed trade's P&L.

No wiring required beyond passing markers — the tooltip is built into <SLChart>.


OHLC HUD

<SLChart data={candles} showOhlcHud indicators={[ema9, ema21]} width={800} height={400} />

The HUD is a plain draggable <div> overlay (drag its grip strip). When no pointer is active it shows the latest candle; hovering or dragging the chart locks it to the pointed candle.


Zoom and pan

Pan, wheel-zoom, and touch-pinch-zoom are built in — no setup required.

<SLChart
  data={candles}
  visibleDataPoints={60}
  maxZoom={400}
  width={800}
  height={400}
/>

| Input | Action | |---|---| | Mouse drag / 1-finger touch drag | Pan the time axis | | Mouse wheel | Zoom in/out around the cursor | | 2-finger touch pinch | Zoom in/out around the pinch focal point | | Hover or drag (with showOhlcHud) | Lock the crosshair to a candle | | Hover a marker | Show its tooltip (price, time, label/changePct) — works regardless of showOhlcHud | | Pan to the right edge | Re-engage live-follow mode |


Live / streaming data

const [candles, setCandles] = useState<Candle[]>(initialCandles);
const [trigger, setTrigger] = useState(0);

function onNewCandle(updatedCandles: Candle[]) {
  setCandles(updatedCandles);
  setTrigger(t => t + 1); // snaps back to latest
}

<SLChart data={candles} scrollToLatestTrigger={trigger} width={800} height={400} />

Themes

<SLChart data={candles} theme="dark"  width={800} height={400} />
<SLChart data={candles} theme="light" width={800} height={400} />

Custom theme — pass any ChartThemeColors object:

import type { ChartThemeColors } from '@stacklatte/chart-web';

const myTheme: ChartThemeColors = {
  background:    '#0d0d0d',
  gridH:         '#1a1a1a',
  gridV:         '#141414',
  priceLabel:    '#999999',
  timeLabel:     '#555555',
  crosshair:     '#777777',
  candleUp:      '#26a69a',
  candleDown:    '#ef5350',
  lineChart:     '#00e5ff',
  rsiLine:       '#f1c40f',
  rsiThreshold:  '#2a2a2a',
  hudBackground: 'rgba(0,0,0,0.85)',
  hudText:       '#ffffff',
  markerNeutral: '#64b5f6',
};

<SLChart data={candles} theme={myTheme} width={800} height={400} />

ChartConfig pipeline (LLM-safe declarative config)

@stacklatte/chart-web re-exports the same ChartConfigChartProps compiler as chart-core, so a config document produced for one renders identically on the other — describe the chart as data, not JSX:

import { buildChartPipeline, SLChart } from '@stacklatte/chart-web';
import type { ChartConfig } from '@stacklatte/chart-web';

const config: ChartConfig = {
  version: '1',
  theme: 'dark',
  priceDisplay: 'candle',
  interval: '1h',
  overlays: [{ type: 'ema', id: 'ema-9', params: { period: 9 }, color: 'cyan' }],
  shadedAreas: [],
  subPanels: [{ type: 'rsi', params: { period: 14 } }, { type: 'volume' }],
  markers: [{ timestamp: 1700003600000, price: 43100, kind: 'entry-long' }],
  viewport: { visibleCandles: 100, followLive: true },
  hud: { showOhlcHud: true },
};

const compiled = buildChartPipeline(config, candles);
<SLChart {...compiled} width={800} height={480} />

This exists specifically so an LLM (or any code-generation step) can produce chart setup safely:

  • Every string is a closed enumtheme, interval, overlay type, and every color come from a fixed, named union. There's no raw hex, no arbitrary millisecond literal, nothing an LLM can plausibly hallucinate a wrong-but-valid-looking value for.
  • It fails loudly, not silently. buildChartPipeline validates first and throws one error listing every problem ([code] path: message) if the document doesn't match the schema, instead of rendering something subtly wrong.
  • A matching JSON Schema ships with @stacklatte/chart-core (a dependency of this package) at node_modules/@stacklatte/chart-core/dist/ChartConfig.schema.json — drop it straight into an LLM's structured-output or tool-call schema.

See llms.txt for a condensed reference aimed at coding agents.


Props

Same prop contract as @stacklatte/chart-core's SLChart — see chart-core's README for the full table. width/height are plain numbers (CSS pixels); there's no useWindowDimensions — measure your container however you like (e.g. ResizeObserver).


Data format

interface Candle {
  timestamp: number; // Unix milliseconds, sorted ascending
  open:      number;
  high:      number;
  low:       number;
  close:     number;
  volume?:   number; // optional — enables showVolumePanel and the HUD's V row
}

Architecture

| Module | Renders | |---|---| | draw/axis.ts | Time/price grid lines and labels (native ctx.fillText — no DOM overlay needed) | | draw/mainPanel.ts | Candles, line series, indicator overlays, shaded areas, crosshair dot | | draw/markers.ts | Trade markers (entry/exit/stop-loss/take-profit) on the main panel | | draw/rsiPanel.ts | RSI sub-panel with 30/70 threshold lines | | draw/macdPanel.ts | MACD sub-panel (line, signal, histogram) | | draw/volumePanel.ts | Volume sub-panel (bars colored by candle direction) | | draw/crosshair.ts | Dashed crosshair lines | | draw/path.ts | Catmull-Rom smooth-path stroking/filling (the Canvas 2D equivalent of chart-core's Skia pathUtils) | | components/OhlcHud.tsx | Draggable OHLC info overlay |

All chart math — layout, viewport clamping, indicator calculation, axis ticks, the ChartConfig compiler — comes from @stacklatte/chart-core/core and is re-exported from this package, so the two renderers never drift apart on behavior.


Contributing

Issues and PRs welcome. See the monorepo README for setup instructions.


License

MIT — see LICENSE.md.