Improving chart performance

This article is a work in progress. Please check back in in a while for more performance tips.

In this article we have collected some generic and specific tips for improving performance of your charts.

Generic tips

Animations

Enabling animations (e.g. via using "animated" theme), will dramatically increase CPU usage. That is OK for basic charts, but for ones that have a lot of data, it might be a good idea to disable animations.

Semi-transparency

When you have semi-transparent items (via fillOpacity and/or strokeOpacity), computer has to perform a lot of additional computations in order to render elements that are superimposed by a semi-transparent element.

Combine it with animations, and you have a lot of extra CPU juice required to run this setup.

Consider using solid fills/stroke (set them to 1).

Data

Dates

Do your data contain string based dates? The chart will have to parse each and every one of those into a proper Date object.

Consider converting them into Date objects, or timestamps.

[{
"date": "2019-01-01",
"value": 100
}, {
"date": "2019-01-02",
"value": 120
}, {
"date": "2019-01-03",
"value": 90
}]
[{
"date": new Date(2019, 0, 1),
"value": 100
}, {
"date": new Date(2019, 0, 2),
"value": 120
}, {
"date": new Date(2019, 0, 3),
"value": 90
}]
[{
"date": 1546293600000,
"value": 100
}, {
"date": 1546380000000,
"value": 120
}, {
"date": 1546466400000,
"value": 90
}]

All of the the three versions of data are identical in functionality, but some will be faster, because will eliminate the need of parsing strings.

Series

Line series

Normally, Line series is fast. However, those can be bogged down by tens or even hundreds of thousands data points.

Below are some suggestions that will help reclaim those precious CPU cycles.

Smoothed lines

Line series connect each point with a straight line, by default. If you use their tensionX/tensionY setting, you may create a sleek-looking smoothed lines, instead of jaggy ones.

This comes at a price, though. This kind of rendering requires a lot more CPU power.

If you have a lot of data points, consider disabling this feature: it makes the chart behave slower, while the smoothing effect is not that prominent with anything beyond a few hundred of data points.

Simplifying paths

Say we have 100,000 (one hundred thousand) data points. And we plot a Line series into container that is 1000px wide.

We end up with a curve that fits a hundred (!) points and line segments into a single pixel.

That's a complete waste of resources, since there's no way a user viewing the chart can see the fine details crammed like that.

The solution is to show only a point every X pixels, completely omitting ones in-between.

To do that, we have a global setting am4core.options.minPolylineStep. With default at 0.5 it means: "omit all line points if they are closer than X pixels to the last point drawn".

am4core.options.minPolylineStep = 5;
am4core.options.minPolylineStep = 5;
// It's not a chart-specific setting
// We need to set it as a global option
am4core.options.minPolylineStep = 5;

Consider these two versions of the same 50K-datapoint chart:

The first one draws thousands of points, most of them are invisible, or lost in the noise.

The second one retains the same quality of conveying data dynamics, but looks cleaner and works orders of magnitude faster - both in initial rendering and in subsequent zoom operations.

The beauty of this approach is that when user zooms in, more detail will start to emerge. This means you are losing the detail only on zoom levels that would make seeing the detail impossible, anyway.

See the Pen amCharts 4: data-heavy chart by amCharts team (@amcharts) on CodePen.0

Bullets

Hiding crammed bullets

Bullets are nice when there are only a handful. When there are a lot of them, they do not start to look a bit messy, but also significantly slow down the chart.

Say we have a Line series with 1000 data points. From performance stand-point it's super fast, since a line is a single path element.

Now, if we have bullets to the series, we now have 1001 separate objects, since each bullet is a separate element.

And on top of that, the chart will look ridiculous with a thousand circles on it:

Obvious solution would be to not use bullets.

However, we have a middle ground: bullets can be automatically disabled or enabled based on density of data points.

For that we have Series' minBulletDistance setting.

It basically means: "If the distance between data points is less than X pixels, hide all bullets".

series.minBulletDistance = 20;
series.minBulletDistance = 20;
{
// ...
"series": [{
// ...
"minBulletDistance": 20
}]
}

Now, when our 1K-datapoint chart starts, we'll see a bare line because each data point is closer than 20 pixels.

When we start zooming the chart in, we eventually get to the point where data points are sufficiently spaced out, for bullets to appear.

See the Pen amCharts 4: data-heavy chart by amCharts team (@amcharts) on CodePen.0

Using simple shapes

Common use of adding bullets to series is to use Bullet object or its derivatives like CircleBullet, LabelBullet, etc.

The issue with it is that all those elements are instances of a Container which adds ability to add any element into it, but also introduces some performance overhead.

If there are a lot of bullets, that millisecond overhead is multiplied.

What is less known, is that you can push any Sprite-based element into Series' bullets and it will just work.

So this code:

let bullet = series.bullets.push(new am4charts.CircleBullet());
bullet.circle.radius = 5;
var bullet = series.bullets.push(new am4charts.CircleBullet());
bullet.circle.radius = 5;
{
// ...
"series": [{
// ...
"bullets": [{
"type": "CircleBullet",
"circle": {
"radius": 5
}
}]
}]
}

Can be achieved using simple Circle object:

let bullet = series.bullets.push(new am4core.Circle());
bullet.radius = 5;
var bullet = series.bullets.push(new am4core.Circle());
bullet.radius = 5;
{
// ...
"series": [{
// ...
"bullets": [{
"type": "Circle",
"radius": 5
}]
}]
}

Visually, both will produce the same result, except the latter will do it a bit faster.

NOTE Predefined bullet types like CircleBullet and LabelBullet come pre-configured for best fit and placement. You may need to set extra settings for your "standalone" bullet elements, like for instance setting horizontalCenter for Label in order for it to appear centered.