Smooth Live Charts with Chart.js’ Built‑in Update API
Want real‑time data flowing across a Chart.js line chart without reinventing animation? Learn how to use the public chart.update() method, tweak animation timing, selectively refresh datasets, keep zoom levels, and avoid pitfalls like layout thrashing or hidden‑tab lag.
30 May 2026, 21:33 UTC

Problem: Live Dashboards Need Smooth, Low‑Latency Charts
When building a monitoring dashboard, data often arrives every few seconds. Developers typically redraw the entire chart on each tick, which can cause stutter, flicker, or missed frames, especially on mobile or low‑end hardware. The question is: how can we animate the transition between the old and new data while keeping the UI responsive?
Thesis: Chart.js’ Public chart.update() API is the Right Tool
Chart.js exposes a chart.update() method that accepts a new data payload and optional configuration. It automatically recalculates scales, applies the configured animation, and redraws only the changed elements. By tuning its parameters, developers can achieve snappy, smooth updates without writing custom animation loops.
Section 1 – Basic Update Flow
First, create a line chart and keep a reference to the Chart instance:
// Assume a in the DOM
const ctx = document.getElementById('liveChart').getContext('2d');
const liveChart = new Chart(ctx, {
type: 'line',
data: { labels: [], datasets: [{ label: 'CPU', data: [], borderColor: '#3e95cd', fill: false }] },
options: {
animation: { duration: 200 }, // 200 ms for a snappy feel
scales: { y: { beginAtZero: true } }
}
});
When new data arrives, call chart.update() with the updated dataset:
function pushData(value) {
liveChart.data.labels.push(new Date().toLocaleTimeString());
liveChart.data.datasets[0].data.push(value);
// Keep only the last 60 points to avoid memory blow‑up
if (liveChart.data.labels.length > 60) {
liveChart.data.labels.shift();
liveChart.data.datasets[0].data.shift();
}
liveChart.update(); // triggers a smooth 200 ms transition
}
Because the chart’s animation.duration is set to 200 ms, each call to update() animates the line segment from the previous point to the new one. The browser’s compositor handles the frame updates, keeping the UI responsive.
Section 2 – Tuning Animation Timing
Animation duration is a key lever. A very short duration (e.g., 50 ms) gives a jittery feel, while a long duration (e.g., 600 ms) can make the chart feel sluggish. Test with your data density and desired UX. If you need a more polished look, set:
liveChart.options.animation.duration = 600;
liveChart.update();
To confirm the change, observe the chart in the browser’s dev tools and note the visible lag or speed.
Section 3 – Updating Only the Needed Datasets
When a chart has many datasets, re‑rendering all of them on each update can be wasteful. chart.update() accepts an array of dataset indices to refresh:
// Suppose dataset 2 is the one that changes
liveChart.update([2]);
This tells Chart.js to recalculate and animate only dataset 2, leaving the rest untouched. On a chart with 20 datasets, this can reduce CPU usage noticeably. Verify performance by logging the frame rate before and after enabling this feature.
Section 4 – Preserving Zoom Levels
Chart.js supports zoom via the chartjs-plugin-zoom plugin. When you call update(), the current zoom scale is preserved automatically. However, if you change the options.scales or options.plugins.zoom properties, the zoom resets. To avoid that, capture the current scale options, update data, then restore them:
const currentScales = { ...liveChart.options.scales };
// Update data
liveChart.data.datasets[0].data.push(newValue);
// Restore scales
liveChart.options.scales = currentScales;
liveChart.update();
Testing: open the chart, zoom in, push data, and confirm the view remains at the same zoom level.
Section 5 – Using onComplete for Side Effects
The animation config accepts an onComplete callback that fires after the animation finishes. This is useful for logging, DOM updates, or triggering subsequent data fetches:
liveChart.options.animation.onComplete = function () {
console.log('Chart updated at', new Date().toISOString());
};
Remember that the callback runs on the main thread; heavy work here can negate the performance benefits of the smooth animation.
Trade‑off: Avoiding Layout Thrashing
Calling update() in a tight loop (e.g., dozens of times per second) can cause layout thrashing, especially with large data sets. Two mitigations:
- Throttle updates: use
setTimeoutorrequestAnimationFrameto limit calls to, say, 5 Hz. - Batch changes: use
chart.update('none')to apply data changes without animation, then callchart.update()once when ready.
Hidden Tabs: If the chart is inside a hidden tab or collapsed element, the browser may pause animations until the element becomes visible. Ensure the element is visible before calling update() or use chart.update('none') to apply instant changes.
Actionable Take‑aways
- Use
chart.update()for live data; setanimation.durationto match your UX goals. - When only one dataset changes, pass its index to
update()to save CPU. - Preserve zoom by backing up
options.scalesbefore mutating them. - Leverage
animation.onCompletefor post‑animation logic, but keep it lightweight. - Throttle updates or batch them with
update('none')on high‑frequency streams. - Test in the target browser, monitor frame rates, and watch for hidden‑tab lag.
By following these patterns, you can deliver smooth, responsive charts that keep users informed without sacrificing performance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.