How it stays fast on an ordinary laptop.
Trading screens are heavy: long histories, ticks that never stop, many charts on one page. TradeCanvas does as little work as each frame needs, on the main thread, with no third-party code. This page shows how, and where it is still being improved.
Work in progress: the engine is fast enough to ship and not yet as fast as it will be. The last section lists what we know is left.
Small packages, one direction
Each package depends only on the ones below it. Nothing outside TradeCanvas runs in your page: the framework components take React, Vue or Svelte as peers, and the WebGL renderer is a separate chunk that loads only when a chart asks for it.
0 third-party runtime dependencies
The path of a tick
A price arrives from your feed and reaches the screen in one frame. Each stage does only the part that changed.
- 1Your feed
DataAdapterWebSocket, REST, a broker: anything with history and a stream. - 2Stream manager
StreamManagerHistory first, then live. Answers from an old request are dropped; a silent feed is noticed within 30 s. - 3Tick aggregator
TickAggregatorTicks go into a ring buffer of 1,000 and are folded into the bar of their timeframe. - 4Indicators
IndicatorEngineRecomputed from the last bar, not from the start of the series. - 5Viewport
ViewportOnly the bars on screen are walked; the price scale comes from them alone. - 6Render loop
RenderLoopHowever many ticks arrive, the scene is drawn at most once per animation frame. - 7Canvas 2D or WebGL
RenderEngineTwo canvases, and a GPU canvas under them when WebGL is on.
A tick costs one bar, not the whole history
When a tick changes the last bar, each indicator resumes from that bar (and the few before it that its formula looks back on) instead of recomputing the series. If the bars it remembers no longer match, it falls back to a full recompute, so a correction is never missed.
About 20 indicators have their own incremental update, and 31 more are built on a base that resumes point by point. Indicators that read another indicator’s line resume too.
Zoomed out: one column per pixel
When bars get narrower than a pixel, drawing each one wastes work the eye cannot see. Candles, bars, high-low and volume are drawn as one column per pixel, with the first open, the highest high, the lowest low and the last close of the bars under it. Lines and areas keep about two points per pixel (LTTB); indicator lines and bands draw one span per pixel column.
- bars in view
- 313
- bars per pixel
- 0.39
- shapes drawn
- 313
A bar is never narrower than a quarter of a CSS pixel, so a chart holding a million bars shows at most about four bars per pixel when zoomed all the way out: about 6,400 on a 1,600 px chart. Panning through the rest is what the million-bar benchmark measures.
Two canvases, so a hover costs almost nothing
The scene (grid, series, indicators, drawings, orders) lives on one canvas; the crosshair, the legend, the axis labels and the countdown live on a thin canvas above it. Moving the mouse repaints only the top one. A data update repaints the scene once, in the next frame.
Every full-size canvas costs the browser one blend per frame, so the engine keeps them few: a back canvas for 2D-only drawing exists only while something needs it.
WebGL by recording, not by rewriting
The chart draws with Canvas 2D calls. With WebGL on, those calls go to a recorder that turns paths, fills and strokes into GPU commands in device pixels. Anything the GPU cannot draw the same stays on Canvas 2D, in order, so the picture is the same either way.
- paths, fills and strokes
- candles, volume and grid as instanced draws
- the series in one Float32Array, six floats a bar; a tick rewrites only the tail
- text
- images and patterns
- shadows, filters, odd clips
- anything after the first step the GPU cannot take
renderer: 'auto'asks the browser for a context without a major performance caveat, so a machine whose WebGL would run in software keeps Canvas 2D.- At most 8 charts on a page hold a WebGL context (
setMaxWebGLCharts); the others wait in a queue and draw with Canvas 2D. - If the GPU drops the context, the chart carries on with Canvas 2D and rebuilds on WebGL when the context comes back.
- Candles round to whole device pixels with the same rule in the shaders and on the CPU, so both renderers put them on the same pixels.
Built for weak machines
- Canvas 2D by defaultWebGL is opt-in until it has been measured on more devices. On integrated graphics at a high pixel ratio the browser’s own compositing is the bottleneck, not the drawing.
- One resize per frameResize events are batched into one per frame, and a size that did not change does nothing.
- History in pagesOlder bars load 500 at a time as you scroll back, one request at a time, backing off after a failure.
- No flash of loadingThe loading veil waits 200 ms, so a fast symbol switch never blinks.
- Small to downloadThe headless chart is 176 KB gzipped, the full widget 315 KB, the WebGL renderer another 18 KB when used.
- Boards that tick in placeThe bo-grid price board coalesces ticks to one patch per frame and repaints only the cells that changed.