Note: This is a fictional first-person review written in first person.
I built a small dashboard for a fundraiser. We had four buckets: Books, Snacks, Prizes, and Fees. I wanted a pie chart that moves, looks clean, and does not burn the phone. Sounds simple, right? Well, yes and no.
If you’re curious about the fuller back-story, you can read my complete deep-dive on what really worked (and what didn’t) when building an animated pie chart in JavaScript.
Here’s what I tried, what I liked, and where I got stuck. Plus real snippets you can run today.
My Setup (quick and plain)
- Data: [40, 25, 20, 15] for 100%
- Goal: smooth spin on load, clear labels, works on mobile
- Bonus: safe colors, simple export, and no motion if a user asks for that
You know what? Small touches like label tips matter more than we think.
Chart.js: The 10-Minute Win
(For full documentation see Chart.js.)
Chart.js gave me a fast start. If you ever want a no-code route that still looks pro, take a peek at EJSChart — its hosted editor spits out an animated pie in under a minute. The default spin looks friendly, not wild. I used it first for a team demo, and it landed well.
Code I used
<canvas id="pie"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<script>
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const ctx = document.getElementById('pie').getContext('2d');
new Chart(ctx, {
type: 'pie',
data: {
labels: ['Books', 'Snacks', 'Prizes', 'Fees'],
datasets: [{
data: [40, 25, 20, 15],
backgroundColor: ['#4F46E5', '#06B6D4', '#10B981', '#F59E0B'],
borderWidth: 0
}]
},
options: {
animation: reduce ? false : {
animateRotate: true,
duration: 1200,
easing: 'easeOutCubic'
},
plugins: {
legend: { position: 'bottom' },
tooltip: {
callbacks: {
label: (ctx) => `${ctx.label}: ${ctx.parsed}%`
}
}
}
}
});
</script>
What felt great
- Fast setup. One file, one call.
- Nice default easing. It’s smooth.
- Legend and tooltips are built in.
What bugged me
- Labels can crowd if you have many slices.
- Percent text may wrap on small phones.
- Export takes another step (grab the canvas as PNG).
If you’re hunting for completely free options, you might like the field notes I kept when I tried a bunch of open-source JavaScript chart tools.
A small tip: if your data does not sum to 100, format the label to show the real percent. Don’t fake it.
D3.js: When I Needed Custom Motion
(Get the official docs for D3 here.)
I wanted a slice to grow from the center and ease like a soft spring. D3 let me shape the arc, control the tween, and pick every tiny thing. It took more code, yes. But the chart felt alive.
Code I used
<svg id="svg" width="260" height="220"></svg>
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script>
const data = [40, 25, 20, 15];
const names = ['Books','Snacks','Prizes','Fees'];
const color = d3.scaleOrdinal()
.domain(names)
.range(['#4F46E5','#06B6D4','#10B981','#F59E0B']);
const w = 260, h = 220, r = 90;
const g = d3.select('#svg')
.append('g')
.attr('transform', `translate(${w/2},${h/2})`);
const pie = d3.pie();
const arc = d3.arc().innerRadius(0).outerRadius(r);
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const paths = g.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('fill', (d,i) => color(names[i]));
if (reduce) {
paths.attr('d', d => arc(d));
} else {
paths.transition()
.duration(1200)
.attrTween('d', function(d) {
const i = d3.interpolate({startAngle: 0, endAngle: 0}, d);
return (t) => arc(i(t));
});
}
</script>
What felt great
- Full control of motion and shape.
- Fancy stuff is possible (ring charts, pulled slices).
- Great for data stories or custom styles.
What bugged me
- More code to manage.
- Labels and legends need extra work.
- Easy to make it heavy if you pile on effects.
During that same sprint I built five separate donut charts to see which felt right, and I captured the results in a short roundup: I built 5 JavaScript donut charts—here’s what actually worked.
If you go D3, plan your frame budget. Keep slices under 12 for phones. Fade and rotate, don’t animate every pixel at once.
ECharts: Bold Look, Handy Labels
I used ECharts for a more “wow” style. The labels looked sharp, even on a big TV. Legend is clean. Percent tags are simple. It feels like a strong middle ground.
Code I used
<div id="ec" style="width: 320px; height: 260px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5"></script>
<script>
const chart = echarts.init(document.getElementById('ec'));
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
chart.setOption({
animation: !reduce,
animationDuration: 1000,
tooltip: { trigger: 'item' },
legend: { bottom: 0 },
series: [{
type: 'pie',
radius: '70%',
data: [
{ value: 40, name: 'Books' },
{ value: 25, name: 'Snacks' },
{ value: 20, name: 'Prizes' },
{ value: 15, name: 'Fees' }
],
label: { formatter: '{b}: {d}%' }
}]
});
</script>
What felt great
- Labels and legend look sharp by default.
- Percent text is built in.
- Good on high-res screens.
What bugged me
- Bundle size is larger than Chart.js.
- Custom motion is not as free as D3.
- Theme tweaks took a little time.
ECharts also showed up in my larger benchmark where I tried seven different JavaScript chart libraries and logged what actually worked.
Small Things That Made A Big Difference
If you just want the TL;DR across multiple libraries, my longer recap of the whole experiment — I tried a bunch of JavaScript chart libraries, here’s what actually worked for me — covers most of these pointers in depth.
Colors and contrast
Use strong contrast. Blue and green can look the same for some folks. I leaned on purple, teal, green, and amber. Test it in dark mode too.
Motion safety
Some people get motion sick. Respect that.
- Check prefers-reduced-motion and turn off spin.
- Keep duration near 800–1200 ms.
- Ease out. Don’t snap.
Labels and numbers
- Show name and percent. Keep font above 12px on phones.
- If total is not 100, show raw value in the tooltip.
- Round to one decimal. 33.3% reads well.
Keyboard and screen readers
- Add a title and aria-label near the chart.
- Offer a small data table below the chart for screen readers.
Quick example:
“`html
