TradingView's Lightweight Charts is excellent at one thing: rendering financial data fast on a canvas. But the moment you try to build an actual trading interface, you hit the wall, it ships no drawing tools, no indicators, no replay, no multi-pane sync. Those are left to you.
So every project, I found myself doing the same archaeology: drawing tools in one community repo, an indicator implementation in another, a replay system I'd write from scratch, and three different API styles to glue together. I got tired of it and packaged the whole layer into one library: CandleKit (MIT).
The idea
One tree-shakeable API over Lightweight Charts that includes the pieces you keep rebuilding:
- Drawing tools — trendline, ray, Fibonacci, rectangle, circle, horizontal/vertical lines, with select / drag / reshape / lock / persist. Original engine on LWC canvas primitives, no third-party drawing runtime.
- Indicators — SMA, EMA, WMA, VWAP, Bollinger, RSI, MACD, ATR, Stochastic built in, plus an extensible registry so you can register your own.
- Deterministic replay — play/pause, step ±1, speed control, seek/jump, per-bar hooks.
-
Measurement ruler, multi-pane sync, and a plugin system where drawing/indicators/measurement are all swappable
ChartPlugins.
The core is framework-agnostic — no React, no DOM framework. There's an optional /react entry with components and hooks.
A chart in React in ~10 lines
import { ChartView } from "@getcandlekit/charts/react";
import "@getcandlekit/charts/styles.css";
export function App({ bars }) {
return (
<div style={{ height: 480 }}>
<ChartView data={bars} seriesType="candlestick" theme="dark" />
</div>
);
}
Install is just the package plus Lightweight Charts as a peer dependency:
npm install @getcandlekit/charts lightweight-charts
Adding drawing tools
Drop a toolbar in and you get the full drawing engine with persistence:
import { ChartView, DrawingToolbar } from "@getcandlekit/charts/react";
<ChartView data={bars} drawing={{ storageKey: "drawings:AAPL" }}>
<DrawingToolbar />
</ChartView>;
Adding indicators
import { ChartView, IndicatorPicker, IndicatorController, createBuiltinRegistry } from "@getcandlekit/charts/react";
const indicators = new IndicatorController(createBuiltinRegistry());
indicators.add("RSI", { length: 14 });
indicators.add("EMA", { length: 21 });
<ChartView data={bars} indicators={indicators}>
<IndicatorPicker />
</ChartView>;
And registering a custom indicator is one call — this is the real extension point:
import { IndicatorRegistry } from "@getcandlekit/charts";
const registry = new IndicatorRegistry().register({
name: "PriceMid",
title: "HL/2",
shortTitle: "MID",
category: "overlay",
defaultInputs: {},
inputConfig: [],
plotConfig: [{ id: "mid", color: "#f59e0b" }],
hlineConfig: [],
calculate: (bars) => ({
plots: { mid: bars.map((b) => ({ time: b.time, value: (b.high + b.low) / 2 })) },
}),
});
Deterministic replay
import { createReplayController } from "@getcandlekit/charts";
const replay = createReplayController();
replay.onBar((e) => chart.updateBar(e.bar));
await replay.load({
id: "demo",
series: [{ symbol: "AAPL", interval: "1m" }],
start: Date.parse("2024-01-02T14:30:00Z"),
end: Date.parse("2024-01-03T21:00:00Z"),
source: myReplayDataSource,
});
replay.setSpeed(8);
replay.play();
Same bars, same result every run, which is what you want for backtesting UIs or teaching a setup step by step.
Try it
There's a live workspace demo with drawing, indicators, measurement and replay in one chart: https://rohanbeingsocial.github.io/candlekit-charts/workspace/
Repo (MIT): https://github.com/rohanbeingsocial/candlekit-charts
A note on honesty: CandleKit is AI-assisted ("vibe-coded"), tested and documented, but early-stage, pin a version and read the changelog before upgrading. If you build trading UIs on Lightweight Charts, I'd genuinely love feedback on the indicator and plugin APIs. Stars help others find it, but issues and PRs help more.

Top comments (1)
This is a fantastic initiative. The "archaeology" you mentioned is painfully real. Stitching together 3rd party drawing primitives over Lightweight Charts usually ends up in a messy, lagging DOM. Wrapping it all into a single, cohesive API like CandleKit is a huge quality-of-life upgrade for devs building trading UIs.
Out of curiosity, how does the drawing engine perform when the user has dozens of complex fibs/channels drawn and the chart is receiving high-frequency tick updates? Does it force a full canvas redraw?
I actually went down a very similar rabbit hole recently. I got so frustrated with the limitations of extending third-party charting libraries that we ended up building and open-sourcing our own native Canvas engine from scratch (Exeria Charts), just so we could tightly couple the drawing tools, indicators, and real-time data stream in a single render loop.
It's awesome to see different approaches to solving this problem in the open-source space. Great work getting this out the door, the API looks incredibly clean!