• Open in:

Stream / ThemeRiver Chart

The ThemeRiver or Stream chart is a type of flow graph that shows changes of events (themes) over time.

Key implementation details

We use SmoothedXLinesSeries for the series. We set fillOpacity on it to 1 to make it an area chart. Then we process the data to create valueYField and openValueYField data fields holding ajdusted values for the stacking effect.

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.horizontalLayout
}));


// Add legend
// https://www.amcharts.com/docs/v5/charts/xy-chart/legend-xy-series/
var legend = chart.children.push(
  am5.Legend.new(root, {
    centerY: am5.p50,
    y: am5.p50,
    layout: root.verticalLayout,
    clickTarget: "none"
  })
);

legend.valueLabels.template.set("forceHidden", true);


// Data
var data = [
	{ year: "1896", uk: 7, ussr: 0, russia: 0, usa: 20, china: 0 },
	{ year: "1900", uk: 78, ussr: 0, russia: 0, usa: 55, china: 0 },
	{ year: "1904", uk: 2, ussr: 0, russia: 0, usa: 394, china: 0 },
	{ year: "1908", uk: 347, ussr: 0, russia: 0, usa: 63, china: 0 },
	{ year: "1912", uk: 160, ussr: 0, russia: 0, usa: 101, china: 0 },
	{ year: "1916", uk: 0, ussr: 0, russia: 0, usa: 0, china: 0 },
	{ year: "1920", uk: 107, ussr: 0, russia: 0, usa: 193, china: 0 },
	{ year: "1924", uk: 66, ussr: 0, russia: 0, usa: 198, china: 0 },
	{ year: "1928", uk: 55, ussr: 0, russia: 0, usa: 84, china: 0 },
	{ year: "1932", uk: 34, ussr: 0, russia: 0, usa: 181, china: 0 },
	{ year: "1936", uk: 36, ussr: 0, russia: 0, usa: 92, china: 0 },
	{ year: "1940", uk: 0, ussr: 0, russia: 0, usa: 0, china: 0 },
	{ year: "1944", uk: 0, ussr: 0, russia: 0, usa: 0, china: 0 },
	{ year: "1948", uk: 56, ussr: 0, russia: 0, usa: 148, china: 0 },
	{ year: "1952", uk: 31, ussr: 117, russia: 0, usa: 130, china: 0 },
	{ year: "1956", uk: 45, ussr: 169, russia: 0, usa: 118, china: 0 },
	{ year: "1960", uk: 28, ussr: 169, russia: 0, usa: 112, china: 0 },
	{ year: "1964", uk: 28, ussr: 174, russia: 0, usa: 150, china: 0 },
	{ year: "1968", uk: 18, ussr: 188, russia: 0, usa: 149, china: 0 },
	{ year: "1972", uk: 29, ussr: 211, russia: 0, usa: 155, china: 0 },
	{ year: "1976", uk: 32, ussr: 285, russia: 0, usa: 155, china: 0 },
	{ year: "1980", uk: 45, ussr: 442, russia: 0, usa: 0, china: 0 },
	{ year: "1984", uk: 72, ussr: 0, russia: 0, usa: 333, china: 76 },
	{ year: "1988", uk: 53, ussr: 294, russia: 0, usa: 193, china: 53 },
	{ year: "1992", uk: 50, ussr: 0, russia: 0, usa: 224, china: 83 },
	{ year: "1996", uk: 26, ussr: 0, russia: 115, usa: 260, china: 110 },
	{ year: "2000", uk: 55, ussr: 0, russia: 188, usa: 248, china: 79 },
	{ year: "2004", uk: 57, ussr: 0, russia: 192, usa: 264, china: 94 },
	{ year: "2008", uk: 77, ussr: 0, russia: 143, usa: 315, china: 184 }
];


// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
  categoryField: "year",
  startLocation: 0.5,
  endLocation: 0.5,
  renderer: am5xy.AxisRendererX.new(root, {
    pan: "zoom",
    minorGridEnabled: true,
    minGridDistance: 50
  }),
  tooltip: am5.Tooltip.new(root, {})
}));

xAxis.data.setAll(data);

var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
  renderer: am5xy.AxisRendererY.new(root, {
    pan: "zoom"
  })
}));


// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
function createSeries(field, name) {
  var series = chart.series.push(am5xy.SmoothedXLineSeries.new(root, {
    name: name,
    xAxis: xAxis,
    yAxis: yAxis,
    valueField: field,
    valueYField: field + "_hi",
    openValueYField: field + "_low",
    categoryXField: "year",
    tooltip: am5.Tooltip.new(root, {
      pointerOrientation: "horizontal",
      labelText: "[fontSize: 18px]{name}[/]\n{categoryX}: [bold]{" + field + "}[/]"
    })
  }));
  
  // Do not show tooltip for zero values
  series.get("tooltip").adapters.add("visible", function(visible, target) {
    if (target.dataItem && (target.dataItem.get("value") > 0)) {
      return true;
    }
    return false;
  });

  series.strokes.template.setAll({
    forceHidden: true
  });
  
  series.fills.template.setAll({
    visible: true,
    fillOpacity: 1
  });


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

  legend.data.push(series);
}

createSeries("uk", "United Kingdom");
createSeries("ussr", "Soviet Union");
createSeries("russia", "Russia");
createSeries("usa", "United States");
createSeries("china", "China");

// Prepare data for the river-stacked series
for (var i = 0; i < data.length; i++) {
  var row = data[i];
  var sum = 0;

  // Calculate open and close values
  chart.series.each(function(series) {
    var field = series.get("valueField");
    var val = Number(row[field]);
    row[field + "_low"] = sum;
    row[field + "_hi"] = sum + val;
    sum += val;
  });

  // Adjust values so they are centered
  var offset = sum / 2;
  chart.series.each(function(series) {
    var field = series.get("valueField");
    row[field + "_low"] -= offset;
    row[field + "_hi"] -= offset;
  });
  
  chart.series.each(function(series) {
    var field = series.get("valueField");
    series.data.setAll(data);
  });

}


// Add cursor
chart.set("cursor", am5xy.XYCursor.new(root, {
  behavior: "zoomXY",
  xAxis: xAxis
}));


// 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>