The Waterfall Chart Type
A Waterfall Chart visualizes how individual positive and negative values contribute to a running total. Each bar starts where the previous one ended, making it easy to see which categories drive gains or losses. Commonly used in financial analysis, budget reporting, and business performance dashboards.
In SciChart.js, Waterfall Charts are assembled from FastRectangleRenderableSeriesš with XyxyDataSeriesš, using EColumnYMode.TopBottom to position each bar between its accumulated start and end values.
The JavaScript Waterfall Chart Example can be found in the SciChart.JS Examples Suite on GitHub, or in the live demo at scichart.com/demo.
Create a Waterfall Chartā
The key idea is to pre-process your data into a cumulative (waterfall) format ā each data point carries both the accumulated value before and after adding the period's change. These become the yValues (bar bottom) and y1Values (bar top) in the XyxyDataSeries.
// Demonstrates how to create a Waterfall Chart using SciChart.js
const {
SciChartSurface,
NumericAxis,
FastRectangleRenderableSeries,
XyxyDataSeries,
EColumnMode,
EColumnYMode,
EDataPointWidthMode,
ELabelAlignment,
TextLabelProvider,
NumberRange,
Thickness,
SciChartJsNavyTheme,
} = SciChart;
// or, for npm: import { SciChartSurface, ... } from "scichart"
const waterfallData = toWaterfallData(monthlyData);
const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, {
theme: new SciChartJsNavyTheme(),
});
// X axis uses a TextLabelProvider so month names appear on the x axis
sciChartSurface.xAxes.add(
new NumericAxis(wasmContext, {
labelProvider: new TextLabelProvider({
labels: waterfallData.map((d) => d.month),
}),
labelStyle: {
alignment: ELabelAlignment.Center,
padding: new Thickness(2, 1, 2, 1),
fontSize: 11,
},
maxAutoTicks: waterfallData.length,
growBy: new NumberRange(0.05, 0.05),
})
);
sciChartSurface.yAxes.add(
new NumericAxis(wasmContext, {
growBy: new NumberRange(0.1, 0.1),
labelFormat: ENumericFormat.Engineering,
})
);
// XyxyDataSeries stores x, y (bar bottom/from), x1 (same as x), y1 (bar top/to)
const dataSeries = new XyxyDataSeries(wasmContext, {
xValues: waterfallData.map((_, i) => i),
yValues: waterfallData.map((d) => d.from),
x1Values: waterfallData.map((_, i) => i),
y1Values: waterfallData.map((d) => d.to),
metadata: waterfallData as any,
});
const waterfallSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries,
columnXMode: EColumnMode.Mid,
columnYMode: EColumnYMode.TopBottom,
dataPointWidthMode: EDataPointWidthMode.Range,
strokeThickness: 0,
paletteProvider: new WaterfallPaletteProvider(),
dataLabelProvider: new WaterfallDataLabelProvider({
skipMode: EDataLabelSkipMode.ShowAll,
color: "white",
style: {
fontSize: 10,
multiLineAlignment: EMultiLineAlignment.Center,
lineSpacing: 4,
},
horizontalTextPosition: EHorizontalTextPosition.Center,
verticalTextPosition: EVerticalTextPosition.Center,
}),
});
sciChartSurface.renderableSeries.add(waterfallSeries);
In the code above:
- Data transformation ā
toWaterfallData()converts raw profit/loss entries into{ from, to }pairs.fromis the accumulated total before the entry andtois the total after. A final "Total" bar (from 0 to the overall total) is appended. XyxyDataSeriesā storesxValues(sequential index),yValues(fromā bar bottom),x1Values(same asxValuesforEColumnMode.Mid), andy1Values(toā bar top).EColumnMode.Midā centers each bar on its X index.EColumnYMode.TopBottomā interpretsyValues/y1Valuesas the explicit bottom and top of each bar.EDataPointWidthMode.Rangeā sets bar width relative to the X data range so bars are evenly spaced.- PaletteProvider ā colors each bar individually: green for positive, red for negative, blue for the Total bar.
RectangleSeriesDataLabelProviderā renders the cumulative total and the period change inside each bar.TextLabelProvideron the X axis ā maps numeric indices back to month names.
Data Transformationā
The waterfall layout requires converting your source data into bar segments. Each bar spans from an accumulated from value to an accumulated to value:
function toWaterfallData(data: { month: string; profit: number }[]) {
let accumulated = 0;
const result = data.map((d) => {
const from = accumulated;
accumulated += d.profit;
return { month: d.month, profit: d.profit, from, to: accumulated };
});
// Add a Total bar running from 0 to the final accumulated value
result.push({ month: "Total", profit: 0, from: 0, to: accumulated });
return result;
}
The from/to values are then fed directly as yValues/y1Values of XyxyDataSeries.
Coloring Bars with PaletteProviderā
Each bar is colored based on whether the period value is positive, negative, or the summary Total. Attach a custom paletteProvider to the series:
paletteProvider: {
fillPaletteMode: EFillPaletteMode.SOLID,
onAttached() {},
onDetached() {},
overrideFillArgb(_x, _y, _index, _opacity, metadata) {
const profit = metadata?.profit;
if (profit === 0) return parseColorToUIntArgb("#2196F3", 200); // Total
return profit > 0
? parseColorToUIntArgb("#4CAF50", 220) // Positive
: parseColorToUIntArgb("#F44336", 220); // Negative
},
}
For more details on per-point coloring, see the PaletteProvider API.
Adding Data Labelsā
Use RectangleSeriesDataLabelProvider to display values inside each bar. Override getText() to format the label from metadata:
dataLabelProvider: new (class extends RectangleSeriesDataLabelProvider {
getText(metadataSelector) {
const d = metadataSelector.getMetaData();
const total = `${formatNumber(d.to, ENumericFormat.Engineering, 2)}$`;
if (d.profit === 0) return total;
const delta = `${d.profit > 0 ? "+" : ""}${formatNumber(d.profit, ENumericFormat.Engineering, 2)}$`;
return `${total}\n${delta}`;
}
})({ skipMode: EDataLabelSkipMode.ShowAll, color: "white", ... })
For a full walkthrough of data labels, see the Data Labels API.
Attaching Metadataā
Pass your transformed data as metadata on the XyxyDataSeries. This makes the original objects available inside the PaletteProvider and DataLabelProvider callbacks:
const dataSeries = new XyxyDataSeries(wasmContext, {
xValues: ...,
yValues: waterfallData.map(d => d.from),
x1Values: ...,
y1Values: waterfallData.map(d => d.to),
metadata: waterfallData, // ā attached here
});
Inside any callback, retrieve the metadata for a data point with metadata?.profit (PaletteProvider) or metadataSelector.getMetaData() (DataLabelProvider).
For more about attaching and using metadata, see the Point Metadata API.
See Alsoā
- The Rectangle Series Type ā the underlying series that powers the Waterfall Chart
- Start Here - RenderableSeries Overview
- PaletteProvider API ā per-point coloring
- Data Labels API
- Point Metadata API