Skip to main content
Version: 8.3.2

Offscreen canvas

LightningChart JS has a beta feature that allows isolating all chart operations to a separate web worker thread and displaying the output to the user interface via an OffscreenCanvas.

info

What does the feature being "beta" mean?

LCJS follows strict semantic versioning practices, meaning APIs and behaviors are not compromised outside major version releases in which case they are explicitly listed with respective migration guidance.

Features marked as beta are new, experimental and considered outside the semantic versioning practice. They can be changed if we find that the API/behavior needs reworking based on user feedback

When is this useful?

This is mainly a performance optimization approach. By moving all utilization of LCJS into a web worker thread, it effectively removes all charting related CPU utilization from the main thread, which reduces lag and frees up processing power for the rest of the application.

The approach is most conveniently usable if you don't need the charting data at all in the main thread, in which case you directly stream all necessary data to the web worker. That said, it is also possible to share charting data from the main-thread to a web worker chart using SharedArrayBuffers

Usage syntax

// Main thread
// let canvas: HTMLCanvasElement
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker(new URL("./worker.ts", import.meta.url), {
type: "module",
});
worker.postMessage(offscreen, [offscreen]);
// Web worker
import { lightningChart } from "@lightningchart/lcjs";

let chartState: undefined | { canvas: OffscreenCanvas, chart: ChartXY }
self.onmessage = ({ data }) => {
if (data.type === 'init') {
const canvas = data.canvas as OffscreenCanvas;

const lc = lightningChart({
offscreenCanvas: canvas,
});

// ... use LCJS API normally - charts will be rendered wherever the original Canvas element is
}
};

For dynamic sizing of the DOM element to work, you'll need to setup a ResizeObserver that triggers layout() method in the web worker side when the canvas size changes:

// Main thread
const observer = new ResizeObserver(() => {
worker.postMessage({ type: 'resize', width: canvas.offsetWidth, height: canvas.offsetHeight, dpr: devicePixelRatio })
})
observer.observe(canvas)
// Web worker
// ...
if (data.type === 'resize') {
if (!chartState) return
chartState.canvas.width = Math.round(data.width * data.dpr)
chartState.canvas.height = Math.round(data.height * data.dpr)
chartState.chart.engine.layout()
}

Limitations

  • There currently isn't any implementation of piping user interactions from UI thread to web workers (or LCJS library) so any charts created using this approach will not support any user interactivity.
  • LCJS inside web worker currently doesn't work with developer licenses. Using this approach currently requires either a deployment license or a special type of development license. Trial licenses also work.
  • WebGL context sharing is unimplemented with offscreen canvas mode. This means that when using this approach, you are limited to max 16 charts (or WebGL contexts) on the page.

If you are interested in Offscreen canvas mode but feel that these limitations hinder you, please contact us.