The Linear Gauge Chart Type
Linear Gauge Charts visualize a single value against a scale ā ideal for dashboards, KPI panels, and instrument displays. SciChart.js does not have a dedicated gauge series type; instead linear gauges are composed from FastRectangleRenderableSeriesš combined with annotations for value indicators and labels.
The JavaScript Linear Gauges example can be found in the SciChart.JS Examples Suite on GitHub, or in the live demo at scichart.com/demo.
Core Building Blocksā
A linear gauge in SciChart.js is assembled from standard chart primitives:
- FastRectangleRenderableSeriesš ā renders the gauge track as one or more colored rectangles
- XyxyDataSeriesš ā positions each rectangle via explicit
[x, y, x1, y1]coordinates (use EColumnMode.StartEndš + EColumnYMode.TopBottomš) - XyyDataSeriesš ā alternative data series for dynamic/animated gauges where each segment is driven by a center X +
[y, y1]range - IFillPaletteProviderš ā assigns a color to each rectangle by index, enabling multi-colored segment bands
- GradientParamsš ā fills a single rectangle with a smooth color gradient via
fillLinearGradient - LineArrowAnnotationš ā draws the value indicator pointer
- TextAnnotationš ā displays the current value label and scale markings
- DataLabelProviderš ā renders text inside each rectangle segment (e.g. "Low / Moderate / High")
Examplesā
Vertical Segmented Gaugeā
A vertical gauge with discrete color bands per segment and a LineArrowAnnotationš value indicator. Each rectangle in XyxyDataSeriesš represents one band; an IFillPaletteProviderš maps a color to each by index:
// Each segment top value and matching color
const SEGMENT_TOPS = [20, 40, 60, 80, 100];
const SEGMENT_COLORS = ["#FF4F81", "#FF9A3C", "#50C7E0", "#264B93", "#1B2A4A"];
const GAUGE_WIDTH = 10;
// Build XyxyDataSeries: each rectangle spans x=[0, GAUGE_WIDTH], y=[segmentBottom, segmentTop]
const xValues: number[] = [];
const yValues: number[] = [];
const x1Values: number[] = [];
const y1Values: number[] = [];
SEGMENT_TOPS.forEach((top, i) => {
xValues.push(0);
yValues.push(i === 0 ? 0 : SEGMENT_TOPS[i - 1]);
x1Values.push(GAUGE_WIDTH);
y1Values.push(top);
});
// Implement IFillPaletteProvider to color each segment individually
class SegmentPaletteProvider implements IFillPaletteProvider {
readonly fillPaletteMode = EFillPaletteMode.SOLID;
private colors = SEGMENT_COLORS.map(c => parseColorToUIntArgb(c));
onAttached(parentSeries: IRenderableSeries) {}
onDetached() {}
overrideFillArgb(xValue: number, yValue: number, index: number): number {
return this.colors[index % this.colors.length];
}
}
const gaugeSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values }),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
strokeThickness: 0.5,
stroke: "#FFFFFF22",
paletteProvider: new SegmentPaletteProvider()
});
sciChartSurface.renderableSeries.add(gaugeSeries);
// Arrow annotation pointing at the current value
const value = 65;
sciChartSurface.annotations.add(
new LineArrowAnnotation({
x1: GAUGE_WIDTH, y1: value,
x2: GAUGE_WIDTH + 1, y2: value,
stroke: "#FFFFFF",
strokeThickness: 2,
arrowHeadPosition: EArrowHeadPosition.Start,
arrowStyle: { headLength: 10, headWidth: 7, headDepth: 1, fill: "#FFFFFF", strokeThickness: 1 }
}),
new TextAnnotation({
x1: GAUGE_WIDTH + 1.3, y1: value,
text: `${value}`,
fontSize: 12,
textColor: "#FFFFFF",
horizontalAnchorPoint: EHorizontalAnchorPoint.Left,
verticalAnchorPoint: EVerticalAnchorPoint.Center
})
);
Key points:
- Each segment is a rectangle with
x=[0, GAUGE_WIDTH]andy=[segmentBottom, segmentTop]ā built using EColumnMode.StartEndš and EColumnYMode.TopBottomš SegmentPaletteProviderimplements IFillPaletteProviderš and returns a color keyed byindex- LineArrowAnnotationš with EArrowHeadPosition.Startš places the arrowhead at the gauge edge pointing at the value
Vertical Gradient Gaugeā
A single rectangle filled with a smooth GradientParamsš color ramp, combined with a visible NumericAxisš for automatic scale markings:
// Show a visible left axis for tick marks instead of manual TextAnnotations
sciChartSurface.yAxes.add(new NumericAxis(wasmContext, {
axisAlignment: EAxisAlignment.Left,
growBy: new NumberRange(0.05, 0.05),
drawMajorBands: false,
drawMajorGridLines: false,
drawMinorGridLines: false,
drawMinorTickLines: false,
overrideOffset: 0
}));
// Single rectangle filled with a vertical linear gradient
const gaugeSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, {
xValues: [0],
yValues: [0],
x1Values: [10],
y1Values: [100]
}),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
strokeThickness: 1,
stroke: "#FFFFFF44",
// GradientParams: Point(0,0) is top of the rectangle, Point(0,1) is bottom
fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
{ offset: 0, color: "#FF4F81" },
{ offset: 0.3, color: "#FF9A3C" },
{ offset: 0.6, color: "#50C7E0" },
{ offset: 0.8, color: "#264B93" },
{ offset: 1, color: "#1B2A4A" }
])
});
sciChartSurface.renderableSeries.add(gaugeSeries);
// Value indicator arrow
const value = 60;
sciChartSurface.annotations.add(
new LineArrowAnnotation({
x1: 10, y1: value,
x2: 11, y2: value,
stroke: "#FFFFFF",
strokeThickness: 2,
arrowHeadPosition: EArrowHeadPosition.Start,
arrowStyle: { headLength: 10, headWidth: 7, headDepth: 1, fill: "#FFFFFF", strokeThickness: 1 }
}),
new TextAnnotation({
x1: 11.2, y1: value,
text: `${value}`,
fontSize: 12,
textColor: "#FFFFFF",
horizontalAnchorPoint: EHorizontalAnchorPoint.Left,
verticalAnchorPoint: EVerticalAnchorPoint.Center
})
);
Key points:
fillLinearGradientaccepts a GradientParamsš with a startPoint(0,0)(top) and endPoint(0,1)(bottom), with color stops at fractional offsets- A visible
yAxiswithdrawMajorGridLines: falseanddrawMajorBands: falseprovides a clean tick-mark scale alongside the gauge, removing the need for manual TextAnnotationš labels overrideOffset: 0on the axis pins it flush to the left edge of the chart padding
Horizontal Segmented Gauge with Data Labelsā
A horizontal gauge using x as the measure axis, with category labels rendered inside each segment via DataLabelProviderš:
// Segment boundaries along the x-axis
const SEGMENT_ENDS = [33, 66, 100];
const SEGMENT_COLORS = ["#50FA7B", "#FF9A3C", "#FF4747"];
const SEGMENT_LABELS = ["Low", "Moderate", "High"];
const GAUGE_HEIGHT = 10;
const xValues: number[] = [];
const yValues: number[] = [];
const x1Values: number[] = [];
const y1Values: number[] = [];
SEGMENT_ENDS.forEach((end, i) => {
xValues.push(i === 0 ? 0 : SEGMENT_ENDS[i - 1]);
yValues.push(0);
x1Values.push(end);
y1Values.push(GAUGE_HEIGHT);
});
class SegmentPaletteProvider implements IFillPaletteProvider {
readonly fillPaletteMode = EFillPaletteMode.SOLID;
private colors = SEGMENT_COLORS.map(c => parseColorToUIntArgb(c));
onAttached(parentSeries: IRenderableSeries) {}
onDetached() {}
overrideFillArgb(xValue: number, yValue: number, index: number): number {
return this.colors[index % this.colors.length];
}
}
const gaugeSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values }),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
strokeThickness: 0.1,
stroke: "#FFFFFF22",
paletteProvider: new SegmentPaletteProvider(),
// Enable data labels to display text inside each segment
dataLabels: {
style: { fontFamily: "Arial", fontSize: 14 },
color: "#FFFFFF"
}
});
// Override the label text using a custom getText function
(gaugeSeries.dataLabelProvider as DataLabelProvider).getText = (state) => {
return SEGMENT_LABELS[state.index];
};
sciChartSurface.renderableSeries.add(gaugeSeries);
// Arrow pointing up at the current value from below
const value = 75;
sciChartSurface.annotations.add(
new LineArrowAnnotation({
x1: value, y1: GAUGE_HEIGHT,
x2: value, y2: GAUGE_HEIGHT + 1,
stroke: "#FFFFFF",
strokeThickness: 2,
arrowHeadPosition: EArrowHeadPosition.Start,
arrowStyle: { headLength: 10, headWidth: 7, headDepth: 1, fill: "#FFFFFF", strokeThickness: 1 }
}),
new TextAnnotation({
x1: value, y1: GAUGE_HEIGHT + 1.8,
text: `${value}`,
fontSize: 12,
textColor: "#FFFFFF",
horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
verticalAnchorPoint: EVerticalAnchorPoint.Bottom
})
);
Key points:
- To create a horizontal gauge, swap axes:
xholds the measure range (SEGMENT_ENDS) andyholds the bar height (GAUGE_HEIGHT). The segment rectangles are[xStart, 0, xEnd, GAUGE_HEIGHT] dataLabelsoptions on the series enable per-rectangle text; overridingdataLabelProvider.getTextreturns a custom string per index instead of the default Y value- The LineArrowAnnotationš is rotated 90° by swapping
x1/y1andx2/y2so the arrow points upward at the value along the x-axis
Animated Real-Time Gaugeā
A segmented gauge that fills from a baseline, using XyyDataSeriesš for dynamic data and a heat-map color palette that changes as the value moves:
// A 20-step color ramp from green (cold) to red (hot)
const GRADIENT_COLORS = [
"#1C5727", "#277B09", "#2C8A26", "#3CAC45", "#58FF80",
"#59FD03", "#7FFC09", "#98FA96", "#AEFE2E", "#FEFCD2",
"#FBFF09", "#FBD802", "#F9A700", "#F88B01", "#F54602",
"#F54702", "#F50E02", "#DA153D", "#B22122", "#B22122"
];
class HeatPaletteProvider extends DefaultPaletteProvider implements IFillPaletteProvider {
readonly fillPaletteMode = EFillPaletteMode.SOLID;
private colors = GRADIENT_COLORS.map(c => parseColorToUIntArgb(c));
overrideFillArgb(xValue: number, yValue: number, index: number): number {
return this.colors[index - 1] ?? this.colors[0];
}
}
// Build data for a gauge that fills from -10 to `value`
const buildGaugeData = (value: number, position: number) => {
const steps = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.filter(y => y <= value);
const xValues = steps.map(() => position);
const yValues = steps.map((_, i) => (i === 0 ? steps[0] : steps[i - 1]));
return { xValues, yValues, y1Values: steps };
};
// Background rectangle (the empty gauge track) ā offset by -2 so foreground bars
// at x=0 have equal 2-unit borders on both sides
sciChartSurface.renderableSeries.add(new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyyDataSeries(wasmContext, {
xValues: [-2],
yValues: [-10.5],
y1Values: [10.5]
}),
columnXMode: EColumnMode.Start,
columnYMode: EColumnYMode.TopBottom,
dataPointWidth: 14,
dataPointWidthMode: EDataPointWidthMode.Range,
fill: "#1B2A4A",
stroke: "gray",
strokeThickness: 2
}));
// Foreground data series - updated on each interval tick
const dataSeries = new XyyDataSeries(wasmContext);
const { xValues, yValues, y1Values } = buildGaugeData(0, 0);
dataSeries.appendRange(xValues, yValues, y1Values);
sciChartSurface.renderableSeries.add(new FastRectangleRenderableSeries(wasmContext, {
dataSeries,
columnXMode: EColumnMode.Start,
columnYMode: EColumnYMode.TopBottom,
dataPointWidth: 10,
dataPointWidthMode: EDataPointWidthMode.Range,
stroke: "#1B2A4A",
strokeThickness: 4, // thick stroke creates gap between segments
paletteProvider: new HeatPaletteProvider()
}));
// Cycle through sample values to animate
const VALUES = [0, 3, 4, 7, -2, -8, 4];
let i = 0;
setInterval(() => {
i = (i + 1) % VALUES.length;
const { xValues, yValues, y1Values } = buildGaugeData(VALUES[i], 0);
dataSeries.clear();
dataSeries.appendRange(xValues, yValues, y1Values);
}, 1000);
Key points:
- XyyDataSeriesš stores
(x, y, y1)triplets ā here each triplet is one small rectangle block along the gauge - EDataPointWidthMode.Rangeš combined with
dataPointWidthcontrols the pixel width of each block in data-coordinate units - Calling
dataSeries.clear()followed bydataSeries.appendRange()on asetIntervaltick replaces only the filled portion, animating the gauge in real time - A background FastRectangleRenderableSeriesš with a dark fill renders the empty track behind the active segments
strokeThickness: 4on the foreground series with a matching dark stroke color creates visible gaps between individual segments
Orientationā
Orientation is controlled entirely by which axis carries the gauge scale:
| Orientation | Measure axis | Bar thickness axis | Data coordinates |
|---|---|---|---|
| Vertical | Y | X | x=[left, right], y=[bottom, top] |
| Horizontal | X | Y | x=[left, right], y=[bottom, top] |
Both orientations use the same EColumnMode.StartEnd + EColumnYMode.TopBottom approach ā only which dimension holds the gauge range changes.
Choosing the Right Data Seriesā
| Use case | Data series | Why |
|---|---|---|
| Static segment bands | XyxyDataSeriesš | Full 2D bounding box control per rectangle |
| Dynamic / animated fill | XyyDataSeriesš | Efficiently update only the Y range; X (position) stays constant |
| Single gradient fill | XyxyDataSeriesš with fillLinearGradient | One rectangle covers the whole gauge; gradient handles color |