Interactive JavaScript Canvas charts
FastPlotJs is a JavaScript charting library for interactive Canvas charts in the browser and renderer-neutral scenes in Node. Build line, bar, heatmap, polar, pie, and wonderplot charts. Try the live example, install from npm, or start with your first chart. Five 3D chart families are experimental.
From points to perspective.
Two series, one shared view. Drag to zoom into the data, or reset to see the complete chart.
Installation
Install the published package in a project with a JavaScript bundler. The package exports ESM and TypeScript declarations.
npm install fastplotjs
Import from the package root. Browser chart examples use a canvas element; Node scene generation does not need a browser.
import { createChart, plot } from "fastplotjs";
Your first chart
Put a canvas in your page, then call createChart after the element exists. This uses the public line chart API.
<canvas id="chart" width="800" height="400"></canvas>
import { createChart } from "fastplotjs";
const canvas = document.getElementById("chart");
const chart = createChart(canvas, {
type: "line",
engine: "auto",
data: [
{ x: 0, y: 12 },
{ x: 1, y: 18 },
{ x: 2, y: 15 },
{ x: 3, y: 24 },
],
axes: { show: true },
}, { color: "#1768eb", lineWidth: 2 });
Keep the returned chart instance if you need to update data, inspect interaction state, or destroy it later.
Data and traces
data supplies one series; traces supplies multiple named series. Each trace owns its points and can override family styling or bind to a subplot. Choose one data approach per chart.
typeThe chart family, such as line or bar.data / tracesUse data for one series, or traces for multiple named series.engineauto, ts, or strict rust.axesCartesian scale, tick, grid, and label options.Family data shapes: line, bar, and polar use { x, y }; heatmap uses { x, y, value }; wonderplot accepts optional value, size, and opacity channels; 3D uses finite numeric { x, y, z }.
For multi-trace input, provide a trace array. Each trace owns its data and can carry a name and color.
createChart(canvas, {
type: "line",
traces: [
{ name: "North", color: "#1768eb", data: [{ x: 0, y: 10 }, { x: 1, y: 15 }] },
{ name: "South", color: "#00a7a1", data: [{ x: 0, y: 8 }, { x: 1, y: 12 }] },
],
legend: true,
});
Choose a chart
The six current 2D families share createChart(canvas, options). Set type and put family-specific options under the matching key. The five 3D families below are experimental.
Line and area
Use ordered { x, y } points. x can be numeric or datetime input; null y values form gaps. Multiple traces give each series a name and color. Interpolation supports linear, smooth, and step-before/after/center; patterns include solid, dashed, dotted and dash-dot.
createChart(canvas, {
type: "line",
traces: [
{ name: "Actual", color: "#1768eb",
data: [{ x: 0, y: 10 }, { x: 1, y: 17 }, { x: 2, y: 14 }] },
{ name: "Target", color: "#00a7a1",
data: [{ x: 0, y: 12 }, { x: 1, y: 15 }, { x: 2, y: 18 }] },
],
lineStyle: { interpolation: "smooth", lineWidth: 2 },
markers: { show: true, shape: "circle", size: 5 },
areaFill: { show: true, opacity: 0.12, baseline: "zero" },
axes: { show: true }, legend: true,
});
Line options also include pointLabels, errorBars, trendline, and connectGaps. Use references.lines or references.bands for Cartesian thresholds.
Open the line chart guide for a focused, copyable example.
Bar charts
The vertical bars shown here pair a category in x with a numeric value in y. Horizontal bars use a numeric value in x and a category in y. Declare category order on the category axis. The bar mode is grouped, stacked, or normalized (100% stacking).
createChart(canvas, {
type: "bar",
traces: [
{ name: "2025", color: "#1768eb",
data: [{ x: "Jan", y: 12 }, { x: "Feb", y: 18 }] },
{ name: "2026", color: "#00a7a1",
data: [{ x: "Jan", y: 15 }, { x: "Feb", y: 20 }] },
],
axes: { show: true, xAxes: { x: { categories: ["Jan", "Feb"] } } },
bar: { mode: "grouped", orientation: "vertical",
barWidthRatio: 0.7, cornerRadius: 4,
valueLabels: { show: true, position: "outside-end" } },
legend: true,
});
Change mode to stacked for totals or normalized for shares of each category. Bar borders, corners and labels can be overridden per trace. Floating ranges and horizontal layouts are also supported.
Polar windrose
Polar data uses x for a direction and y for radial magnitude. Give categorical directions an explicit angleCategories order. Sectors can be grouped or stacked; the radial scale can be linear or logarithmic.
const directions = ["N", "E", "S", "W"];
createChart(canvas, {
type: "polar",
traces: [
{ name: "Morning", color: "#1768eb",
data: [{ x: "N", y: 5 }, { x: "E", y: 12 },
{ x: "S", y: 8 }, { x: "W", y: 4 }] },
{ name: "Evening", color: "#00a7a1",
data: [{ x: "N", y: 3 }, { x: "E", y: 7 },
{ x: "S", y: 11 }, { x: "W", y: 6 }] },
],
polar: { mode: "windrose", sectorMode: "stacked",
angleCategories: directions, startAngleDegrees: -90,
grid: { rings: 4, angleLabels: true }, sectorLabels: true },
interactions: { polarRotate: true }, legend: true,
});
Use sectorWidthDegrees, gaps, inner/outer radius ratios, grid rings/spokes, labels and vectors for richer windroses. getPolarRotation() and setPolarRotation() expose rotation state.
Heatmap
Each cell is { x, y, value }. x and y can be ordered string categories or numeric positions; value drives color. Explicit category arrays keep sparse grids in the intended order.
createChart(canvas, {
type: "heatmap",
data: [
{ x: "Mon", y: "AM", value: 12 },
{ x: "Tue", y: "AM", value: 34 },
{ x: "Mon", y: "PM", value: 27 },
{ x: "Tue", y: "PM", value: 48 },
],
heatmap: {
xCategories: ["Mon", "Tue"], yCategories: ["AM", "PM"],
minValue: 0, maxValue: 50,
colorStops: [
{ offset: 0, color: "#172554" },
{ offset: 0.5, color: "#2563eb" },
{ offset: 1, color: "#fef08a" },
],
cellGap: 2, cornerRadius: 3,
cellLabels: true, colorScaleLegend: true,
},
});
Options include linear or logarithmic color scale, missing-cell treatment, labels, guides and borders. A logarithmic color scale requires positive values.
Open the heatmap guide for a focused, copyable example.
Wonderplot
A wonderplot places markers at x/y while separate value, size, and opacity fields drive color, radius and alpha. These channels can use independent scales.
createChart(canvas, {
type: "wonderplot",
data: [
{ x: 1, y: 3, value: 10, size: 30, opacity: 80 },
{ x: 2, y: 5, value: 70, size: 90, opacity: 45 },
{ x: 3, y: 4, value: 40, size: 55, opacity: 65 },
],
axes: { show: true },
wonderplot: {
markerShape: "hexagon", markerSize: 6,
colorScale: { minValue: 0, maxValue: 100,
lowColor: "#e0f2fe", highColor: "#7c3aed" },
sizeScale: { minValue: 0, maxValue: 100, minSize: 4, maxSize: 14 },
opacityScale: { minValue: 0, maxValue: 100,
minOpacity: 0.25, maxOpacity: 1 },
colorScaleLegend: true,
},
});
Marker shapes include circle, square, diamond, triangle, pentagon and hexagon. Labels and borders can be set globally or per trace. Positive color values can use a logarithmic color scale.
Pie and donut
Use positive { label, value } data for part-to-whole sectors. Set pie.innerRadiusRatio above zero for a donut. Zero and null values are omitted; negative or non-finite values fail validation. Sector gaps, colors, labels, tooltips, and datum-aware legends use original source indexes.
createChart(canvas, {
type: "pie",
pie: {
data: [{ label: "Solar", value: 38 }, { label: "Wind", value: 27 },
{ label: "Hydro", value: 35 }],
innerRadiusRatio: 0.28,
labels: { content: "label-fraction" },
},
interactions: { tooltip: true, pointClick: true },
});
For projected depth, see the experimental Pie3D example. The ordinary pie chart remains two-dimensional.
Axes and scales
Cartesian axes support linear, logarithmic and datetime scales. x input for datetime accepts ISO strings, Date objects or Unix milliseconds. Logarithmic data must be positive. Named x and y axes control ticks, grids, labels, ranges, titles and top/bottom or left/right sides.
createChart(canvas, {
type: "line",
data: [{ x: 1, y: 10 }, { x: 2, y: 100 }],
axes: {
show: true,
xAxes: { x: { grid: true, ticks: 4 } },
yAxes: { y: { scale: "logarithmic", grid: true, title: "Value" } },
},
});
Use categories for ordered category axes. Further axis options include explicit tickValues, minor ticks, label formatters, label rotation/collision, padding and date/time formatting. viewport sets an initial visible range; references.lines and references.bands add threshold guides.
Subplots and shared axes
Arrange charts in rows and columns. A trace identifies its subplot; each subplot selects named axes. Reuse an axis ID for a shared scale, or give cells independent axes. Rows and columns may be numbers or weighted tracks.
createChart(canvas, {
type: "line",
traces: [
{ subplot: "pressure", color: "#1768eb",
data: [{ x: 0, y: 80 }, { x: 1, y: 85 }] },
{ subplot: "temperature", color: "#ef4444",
data: [{ x: 0, y: 21 }, { x: 1, y: 24 }] },
],
subplots: {
layout: { rows: [{ weight: 2 }, { weight: 1 }], columns: 1 },
items: {
pressure: { row: 0, column: 0, xAxis: "time", yAxis: "pressure" },
temperature: { row: 1, column: 0, xAxis: "time", yAxis: "temperature" },
},
},
axes: {
show: true,
xAxes: { time: { title: "Time", grid: true } },
yAxes: { pressure: { title: "Pressure" },
temperature: { title: "Temperature" } },
},
});
Viewport controls can target a subplotId. viewportGroup links otherwise separate axes for navigation.
Styling, labels, and guides
Chart-level title, subtitle, backgrounds, margin, legend, and style control presentation. Trace color and family options override shared defaults. Text, axes, plot area, legend and tooltip have their own style branches.
createChart(canvas, {
type: "line",
title: "Monthly output", subtitle: "Units produced",
backgroundColor: "#f8fafc",
plotAreaBackgroundColor: "#ffffff",
traces: [{ name: "Output", color: "#1768eb",
data: [{ x: 1, y: 12 }, { x: 2, y: 19 }] }],
markers: { show: true, shape: "diamond", size: 6 },
pointLabels: true,
legend: { show: true, position: "top-right" },
references: { lines: [{ axis: "y", value: 15,
label: "Target", pattern: "dashed" }] },
style: { text: { color: "#1f2937" } },
});
Labels may be suppressed when space is tight; give essential chart data an accessible text summary. responsive, explicit width/height and instance resize() support different layout strategies.
Interactions
Interaction options are opt-in. Enable Cartesian viewport zoom on a line, heatmap, or wonderplot chart, then use the chart instance to read or change viewport state. Bar and polar do not use Cartesian viewport zoom.
const chart = createChart(canvas, {
type: "line",
data: [{ x: 0, y: 2 }, { x: 1, y: 5 }],
interactions: { zoom: true },
});
const viewport = chart.getViewport();
chart.resetZoom();
Use interactions.zoom for wheel/drag/pinch, pan for movement, and tooltip, crosshair, pointClick or pointSelection for inspection. The instance offers zoomIn(), zoomOut(), pan(), autoscale(), getViewportControlState(), and on("viewportChange", handler). Polar rotation and experimental 3D camera control have family-specific methods. Area selection returning every original source point inside a drawn region is a future requirement, not a current API.
Data updates and synchronization
Update a chart instance without recreating its canvas. Use setData for one series, setTraces or setTraceData for multiple series, and appendData for incremental points. updateOptions changes presentation or configuration.
chart.setData([{ x: 0, y: 12 }, { x: 1, y: 20 }]);
chart.appendData([{ x: 2, y: 24 }]);
chart.updateOptions({ lineStyle: { pattern: "dashed" } });
const state = chart.getChartState();
if (state) otherChart.setChartState(state, { emit: false });
getDataState()/setDataState() handle data alone; chart state includes navigation, visibility and selection for synchronization. Set emit: false on a receiving chart to avoid two-way event loops.
Canvas display quality
Control pixel density independently of layout for every 2D chart and Canvas-rendered 3D chart. Automatic DPR and supersampling sharpen text and edges without changing logical sizes or pointer coordinates.
createChart(canvas, {
type: "pie3d",
pie: { data: [{ label: "A", value: 5 }, { label: "B", value: 3 }] },
canvas: { pixelRatio: "auto", resolutionScale: 2 },
pie3d: { curveTolerance: 0.1 },
});
| Option | Default | Effect |
|---|---|---|
canvas.pixelRatio | 1 | A positive finite number, or "auto" for the current devicePixelRatio. |
canvas.resolutionScale | 1 | A positive supersampling multiplier applied to pixelRatio. |
pie3d.curveTolerance | 1 logical pixel | Smaller values make experimental Pie3D curves more detailed, within the existing scene3d.maxPoints budget. |
Backing dimensions are rounded from logical dimensions multiplied by both ratios. More pixels use more memory and rendering work; explicit quality settings reject backing buffers over 16,384 pixels per edge or 67,108,864 pixels total. Native 2D arcs need no polygon-detail option. WebGL2 offscreen resolution and PNG export resolution are unchanged.
createChart handles resize and automatic DPR changes. renderToCanvas re-reads DPR on every call and accepts optional logical width and height for direct resizing. Canvas attributes expose backing pixels; layout and event coordinates remain logical pixels. Existing omitted options keep the previous 1:1 resolution.
Compare 2D and Pie3D before and after, including direct Canvas rendering.
Engines
ts selects the TypeScript implementation. auto uses an available native Node or initialized browser WASM runtime where a family builder supports it, with TypeScript fallback. rust is strict: missing runtime or unsupported family is an error. Select a mode explicitly when fallback behavior matters.
import { initWasmPlotEngine, getWasmPlotEngineStatus } from "fastplotjs";
await initWasmPlotEngine();
console.log(getWasmPlotEngineStatus());
WASM initialization is asynchronous; inspect its status before relying on acceleration. The package also exports Node native initialization/status helpers and a last-WASM-init-error helper. The experimental 3D families currently use the TypeScript scene path.
Node scenes and export
Use plot to produce a renderer-neutral scene without a browser. Unlike createChart, it needs explicit width and height. renderToCanvas draws into a supplied canvas.
import { plot } from "fastplotjs";
const scene = plot({
type: "line",
width: 800,
height: 400,
engine: "ts",
data: [{ x: 0, y: 12 }, { x: 1, y: 18 }],
});
console.log(scene.primitives.length);
The existing renderToPNG() returns UTF-8 JSON scene bytes in a Uint8Array, not a PNG image. For the five experimental 3D families, renderScene3dToSVG(options) returns a static SVG without a DOM. Asynchronous renderScene3dToPNG(options) requires the optional native Node addon; real PNG output has been validated on Windows, not asserted for other platforms. Static exports do not include camera interaction or picking.
API and options reference
Import from the package root. Browse the complete chart-options reference and methods, events, and runtime reference for the published 0.3.0 API.
createChart(canvas, options, style?)Browser chart instance with rendering, resizing, updates, interactions, events and cleanup.plot(options)Renderer-neutral scene; requires type, width and height.renderToCanvas(canvas, options, style?)Draw into a supplied canvas.renderToPNG()UTF-8 JSON scene bytes, not a PNG image.Top-level options include type, engine, data/traces, width/height, responsive, title/subtitle, axes, subplots, viewport, legend, interactions, style and references. Add the matching family branch: bar, polar, pie, heatmap, wonderplot, or an experimental 3D branch such as pie3d.
Core instance methods: render, resize, updateOptions, setData, setTraces, setTraceData, appendData, getViewport, setViewport, zoomIn, zoomOut, pan, resetZoom, autoscale, getChartState, setChartState, on, off, and destroy. Family-specific rotation/camera and tooltip/crosshair/selection methods are also exposed.
Troubleshooting
engine: "ts" to isolate optional runtime loading.auto for supported fallback or ts for a known TypeScript path.x/y; heatmaps need x/y/value.destroy() on unmount. Avoid bidirectional state event loops.Experimental 3D scenes
scatter3d and line3d accept finite numeric { x, y, z } points. Scatter points may also carry value, size and opacity. surface uses a regular point grid; mesh3d joins vertices with explicit triangular face indices. Camera and scene options may change in a future minor release.
createChart(canvas, {
type: "scatter3d", engine: "ts",
data: [
{ x: -1, y: 0, z: 0, value: 10 },
{ x: -0.5, y: 0.4, z: 0.7, value: 25 },
{ x: 0, y: 1, z: 1, value: 40 },
{ x: 0.5, y: 0.6, z: -0.4, value: 60 },
{ x: 1, y: 0, z: -1, value: 80 },
],
scene3d: {
renderer: "canvas",
camera: { projection: "perspective", eye: [1.9, 1.45, 1.9], fieldOfViewDegrees: 30 },
xAxis: { title: "X" }, yAxis: { title: "Y" }, zAxis: { title: "Z" },
},
scatter3d: {
colorScale: { lowColor: "#2563eb", highColor: "#ef4444" },
},
interactions: { camera3d: true, tooltip: true },
});
Line3D
Line3D connects ordered 3D points. Its family options include line width, opacity and linear or smooth interpolation.
createChart(canvas, {
type: "line3d", engine: "ts",
data: [
{ x: -1, y: 0, z: 0 },
{ x: -0.5, y: 0.6, z: 0.5 },
{ x: 0, y: 1, z: 1 },
{ x: 0.5, y: 0.7, z: 0.5 },
{ x: 1, y: 0, z: 0 },
],
scene3d: { renderer: "canvas",
camera: { projection: "perspective", eye: [1.9, 1.45, 1.9], fieldOfViewDegrees: 30 } },
line3d: { lineWidth: 3, lineOpacity: 0.85,
interpolation: "smooth" },
});
Surface
Surface builds triangles from a regular x/y grid of height samples. Keep the grid complete and ordered; wireframe and contour options make shape changes easier to inspect.
createChart(canvas, {
type: "surface", engine: "ts",
data: [
{ x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0.3 }, { x: 2, y: 0, z: 0 },
{ x: 0, y: 1, z: 0.3 }, { x: 1, y: 1, z: 1 }, { x: 2, y: 1, z: 0.3 },
{ x: 0, y: 2, z: 0 }, { x: 1, y: 2, z: 0.3 }, { x: 2, y: 2, z: 0 },
],
scene3d: { renderer: "canvas",
camera: { projection: "perspective", eye: [1.9, 1.45, 1.9], fieldOfViewDegrees: 30 } },
surface: {
lowColor: "#0ea5e9", highColor: "#f97316",
wireframe: { color: "#0f172a", width: 1, opacity: 0.4 },
},
});
Mesh3D
A mesh indexes explicit triangular faces into its vertex array. Face colors, opacity, wireframe, lighting and optional culling control its appearance.
createChart(canvas, {
type: "mesh3d", engine: "ts",
data: [
{ x: 0, y: 0, z: 0 }, { x: 1, y: 0, z: 0 },
{ x: 1, y: 1, z: 0 }, { x: 0, y: 1, z: 0 },
],
scene3d: { renderer: "canvas",
camera: { projection: "perspective", eye: [1.9, 1.45, 1.9], fieldOfViewDegrees: 30 } },
mesh3d: {
faces: [[0, 1, 2], [0, 2, 3]],
faceColors: ["#2563eb", "#14b8a6"],
wireframe: { color: "#0f172a", width: 1, opacity: 0.8 },
},
});
Pie3D
Pie3D projects the same Pie data and labels onto extruded sectors. It reuses the shared 3D camera and datum identity. pie3d.extrusionDepth defaults to 0.15 in normalized scene units; it is not a pixel height. Camera tilt changes the apparent height.
createChart(canvas, {
type: "pie3d",
pie: {
data: [{ label: "Solar", value: 38 }, { label: "Wind", value: 27 },
{ label: "Hydro", value: 35 }],
innerRadiusRatio: 0.2,
labels: { content: "label-fraction" },
},
scene3d: { camera: { projection: "orthographic", orthographicScale: 1.4, eye: [1.4, 1.8, 2.2] } },
interactions: { camera3d: true, tooltip: true, pointClick: true },
});
Pie3D supports one full-chart 3D scene. Named or mixed subplots, Cartesian 3D axes, and 2D Pie border controls are unsupported. Canvas is the default renderer; opt-in WebGL2, static SVG, and optional native Node PNG share projected geometry.
Scatter and line offer bounded level-of-detail options. Canvas and WebGL2 renderer selection is capability-dependent. These families use the TypeScript scene path for auto or ts; strict rust fails explicitly.
Release status
fastplotjs@0.3.0 is the current pre-1.0 npm release. The package ships compiled distribution files and declarations; the project repository remains private. Experimental 3D is identified separately, and not every chart family has a Rust builder.
Install from the public npm package. FastPlotJs is available under the MIT License, copyright © 2026 Intellisys Software. The package includes its license text and separate notices for bundled third-party components. Documentation snippets show public configuration only.
Website analytics privacy
This website is operated by INTELLISYS SOFTWARE, LLC, Armenia. Contact info@intellisys.software for privacy questions.
Optional analytics, when enabled and allowed by you, uses Umami Cloud to measure page views, documentation and example views, npm and license-link clicks, and successful code copies. No clipboard contents, account identity, advertising pixels or session recordings are collected by this site.
Umami receives request IP and browser information and derives temporary website-scoped visitor/session statistics, approximate geography, browser, operating system and device category. These estimates do not identify an exact number of people. Approved campaign values and limited referrer origins are included; arbitrary URL queries and fragments are discarded.
When optional analytics is offered, your choice is required before loading Umami. You can continue without analytics or stop it using the page controls. Choices last only for this page; no consent or visitor cookie is created by our integration. Do Not Track and Global Privacy Control disable it.
Umami Cloud Hobby analytics retention is six months. Provider infrastructure and operational logs may process request metadata outside Armenia, including in the US and EU; operational-log retention is governed by the provider. See Umami privacy information. Contact us for questions about access or deletion; we do not create a persistent identity to connect your activity across visits.
Feedback & support
Questions, bug reports, or feature ideas? Email info@intellisys.software.
For a bug report, include the FastPlotJs version, browser or Node version, and steps to reproduce.
