Skip to main content

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.

tip

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.

Above: The JavaScript Linear Gauges example from the SciChart.js Demo

Core Building Blocks​

A linear gauge in SciChart.js is assembled from standard chart primitives:

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:

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:

  • fillLinearGradient accepts a GradientParamsšŸ“˜ with a start Point(0,0) (top) and end Point(0,1) (bottom), with color stops at fractional offsets
  • A visible yAxis with drawMajorGridLines: false and drawMajorBands: false provides a clean tick-mark scale alongside the gauge, removing the need for manual TextAnnotationšŸ“˜ labels
  • overrideOffset: 0 on 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: x holds the measure range (SEGMENT_ENDS) and y holds the bar height (GAUGE_HEIGHT). The segment rectangles are [xStart, 0, xEnd, GAUGE_HEIGHT]
  • dataLabels options on the series enable per-rectangle text; overriding dataLabelProvider.getText returns a custom string per index instead of the default Y value
  • The LineArrowAnnotationšŸ“˜ is rotated 90° by swapping x1/y1 and x2/y2 so 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 dataPointWidth controls the pixel width of each block in data-coordinate units
  • Calling dataSeries.clear() followed by dataSeries.appendRange() on a setInterval tick 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: 4 on 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:

OrientationMeasure axisBar thickness axisData coordinates
VerticalYXx=[left, right], y=[bottom, top]
HorizontalXYx=[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 caseData seriesWhy
Static segment bandsXyxyDataSeriesšŸ“˜Full 2D bounding box control per rectangle
Dynamic / animated fillXyyDataSeriesšŸ“˜Efficiently update only the Y range; X (position) stays constant
Single gradient fillXyxyDataSeriesšŸ“˜ with fillLinearGradientOne rectangle covers the whole gauge; gradient handles color

See Also​