• Open in:

Stacked Waterfall Chart

A Waterfall Chart (also known as Flying Bricks Chart or Mario Chart or Bridge Chart) helps viewers understand the cumulative effect of sequential events. Stacking series of events gives a clearer picture of the effect of multiple parallel series.

Key implementation details

The key to implementing waterfall charts with amCharts is using the openValueYField of ColumnSeries to “suspend” the columns in the air. To get the series appear as stacked (don’t confuse with actually stacked values) we just set its clustered property to false.

Related tutorials

Demo source

<!-- Styles -->
<style>
#chartdiv {
  width: 100%;
  height: 500px;
}
</style>

<!-- Resources -->
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>

<!-- Chart code -->
<script>
am5.ready(function() {

// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");


// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
  am5themes_Animated.new(root)
]);


// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
var chart = root.container.children.push(am5xy.XYChart.new(root, {
  panX: false,
  panY: false,
  wheelX: "panX",
  wheelY: "zoomX",
  layout: root.verticalLayout,
  paddingLeft: 0
}));


// Data
var data = [{
  category: "Stage #1",
  open1: 0,
  close1: 83,
  open2: 83,
  close2: 128
}, {
  category: "Stage #2",
  open1: 121,
  close1: 128,
  open2: 128,
  close2: 128
}, {
  category: "Stage #3",
  open1: 111,
  close1: 114,
  open2: 114,
  close2: 121
}, {
  category: "Stage #4",
  open1: 98,
  close1: 108,
  open2: 108,
  close2: 111
}, {
  category: "Stage #5",
  open1: 85,
  close1: 96,
  open2: 96,
  close2: 98
}, {
  category: "Stage #6",
  open1: 55,
  close1: 70,
  open2: 70,
  close2: 85
}, {
  category: "Stage #7",
  open1: 3,
  close1: 36,
  open2: 36,
  close2: 55
}, {
  category: "Stage #8",
  open1: 0,
  close1: 2,
  open2: 2,
  close2: 3
}];


// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var xRenderer = am5xy.AxisRendererX.new(root, {
  cellStartLocation: 0.1,
  cellEndLocation: 0.9,
  minGridDistance: 70,
  minorGridEnabled: true
});

var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
  categoryField: "category",
  renderer: xRenderer,
  tooltip: am5.Tooltip.new(root, {})
}));

xRenderer.grid.template.setAll({
  location: 1
});

xAxis.data.setAll(data);

var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
  calculateTotals: true,
  extraMax: 0.05,
  renderer: am5xy.AxisRendererY.new(root, {
    strokeOpacity: 0.1
  })
}));


// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function makeSeries(name, field, openField, total) {
  var series = chart.series.push(am5xy.ColumnSeries.new(root, {
    name: name,
    xAxis: xAxis,
    yAxis: yAxis,
    valueYField: field,
    openValueYField: openField,
    categoryXField: "category",
    clustered: false
  }));

  series.columns.template.setAll({
    tooltipText: "{name}, {categoryX}: {valueY}",
    width: am5.percent(95),
    tooltipY: 0
  });

  series.data.setAll(data);

  // Make stuff animate on load
  // https://www.amcharts.com/docs/v5/concepts/animations/
  series.appear();

  series.bullets.push(function() {
    var label = am5.Label.new(root, {
      text: "{valueY}",
      fill: root.interfaceColors.get("alternativeText"),
      centerY: am5.p50,
      centerX: am5.p50,
      populateText: true,
      textAlign: "center",
      oversizedBehavior: "hide"
    });

    label.adapters.add("text", function(text, target) {
      var val = Math.abs(target.dataItem.get("valueY") - target.dataItem.get("openValueY"));
      return val;
    });

    return am5.Bullet.new(root, {
      locationX: 0.5,
      locationY: 0.5,
      sprite: label
    });
  });

  if (total) {
    series.bullets.push(function() {
      var totalLabel = am5.Label.new(root, {
        text: "{valueY}",
        fill: root.interfaceColors.get("text"),
        centerY: am5.p100,
        centerX: am5.p50,
        populateText: true,
        textAlign: "center"
      });

      totalLabel.adapters.add("text", function(text, target) {
        var dataContext = target.dataItem.dataContext;
        var val = Math.abs(dataContext.close2 - dataContext.open1);
        return val;
      });

      return am5.Bullet.new(root, {
        locationX: 0.5,
        locationY: 1,
        sprite: totalLabel
      });
    });
  }
}

makeSeries("Budget #1", "close1", "open1", false);
makeSeries("Budget #2", "close2", "open2", true);


// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
chart.appear(1000, 100);

}); // end am5.ready()
</script>

<!-- HTML -->
<div id="chartdiv"></div>