Animated Pie Chart JavaScript: What Worked For Me, What Didn’t

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