Skip to content
Chart.js Pie, Doughnut, Radar & Polar Area — Complete Guide

Chart.js Pie, Doughnut, Radar & Polar Area — Complete Guide

DodaTech Updated Jun 6, 2026 10 min read

Pie, doughnut, radar, and polar area charts excel at showing proportions, part-to-whole relationships, and multi-dimensional comparisons — things bar and line charts struggle with.

What You’ll Learn

By the end of this tutorial, you’ll build pie and doughnut charts with custom cutout sizes and percentage tooltips, add center text to doughnut charts using custom plugins, create radar/spider charts for comparing multiple variables across subjects, configure radial scales with point labels and grid lines, and build polar area charts with varying radii.

Prerequisites: You should understand Chart.js basics — the configuration object structure.

When to Use Circular and Radial Charts

Each chart type has a specific job:

  • Pie chart: Shows how a total is divided into parts. Best with 2-5 slices. Example: market share by company.
  • Doughnut chart: Same as pie, but the center hole can display summary information. Generally preferred over pie because the hole improves readability.
  • Radar chart: Compares multiple variables across subjects. Example: comparing employee skills across 6 dimensions.
  • Polar area: Like a pie chart but with equal angles and radii proportional to values. The varying radii make small values harder to miss.

Rule of thumb: If you need to compare values to each other, use a bar chart. If you need to show how a total is divided, use a doughnut. If the categories are the same across two or more subjects, use a radar.

Real-world use: Durga Antivirus Pro uses doughnut charts to show the breakdown of detected threats by type (malware 45%, phishing 30%, ransomware 15%, other 10%). A radar chart compares the detection performance across different operating systems (Windows, macOS, Linux, Android, iOS) to identify which platforms need improvement.

Where This Fits in Your Learning Path

    flowchart LR
    A["Chart.js Basics"] --> B["Line, Bar & Mixed Charts"]
    B --> C["**Pie, Doughnut, Radar & Polar**"]
    C --> D["Scatter & Bubble Charts"]
    D --> E["Chart.js Advanced"]
    style C fill:#f97316,stroke:#c2410c,color:#fff
    style A fill:#e5e7eb,stroke:#9ca3af,color:#374151
    style E fill:#22c55e,stroke:#16a34a,color:#22c55e
  

Pie Chart

A pie chart divides a circle into proportional slices. The angle of each slice is proportional to its value.

new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ['Red', 'Blue', 'Yellow', 'Green'],
    datasets: [{
      label: 'Color Distribution',
      data: [30, 50, 15, 25],
      backgroundColor: [
        'rgb(255, 99, 132)',
        'rgb(54, 162, 235)',
        'rgb(255, 205, 86)',
        'rgb(75, 192, 192)'
      ],
      hoverOffset: 10  // Pull slice outward on hover
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: {
        position: 'right',
        labels: { padding: 20 }
      },
      tooltip: {
        callbacks: {
          label: (context) => {
            const total = context.dataset.data.reduce((a, b) => a + b, 0)
            const pct = Math.round(context.parsed / total * 100)
            return context.label + ': ' + context.parsed + ' (' + pct + '%)'
          }
        }
      }
    }
  }
})

Why the tooltip callback is important: Without it, the tooltip shows only the raw number. Adding the percentage helps viewers understand the proportion at a glance.


Doughnut Chart

Nearly identical to pie, but with a hole in the center:

type: 'doughnut',
options: {
  cutout: '50%'  // 0% = pie, 50% = standard donut, 90% = thin ring
}

Doughnut with Center Text

One of the most requested features: display a total or summary in the center of the doughnut. Chart.js doesn’t support this natively, but you can add it with a custom plugin:

const centerTextPlugin = {
  id: 'centerText',
  beforeDraw: function(chart) {
    const { width, height, ctx } = chart
    ctx.restore()
    const fontSize = (height / 150).toFixed(2)
    ctx.font = fontSize + 'em sans-serif'
    ctx.textBaseline = 'middle'

    const total = chart.data.datasets[0].data.reduce((a, b) => a + b, 0)
    const text = total.toString()
    const textX = Math.round((width - ctx.measureText(text).width) / 2)
    const textY = height / 2

    ctx.fillText(text, textX, textY)
    ctx.save()
  }
}

new Chart(ctx, {
  type: 'doughnut',
  data: { /* ... */ },
  options: { cutout: '70%' },
  plugins: [centerTextPlugin]
})

Multi-Dataset Doughnut (Nested Rings)

Add multiple datasets to create concentric rings:

datasets: [{
  data: [30, 50, 20],
  backgroundColor: ['red', 'blue', 'yellow']
}, {
  data: [40, 30, 30],
  backgroundColor: ['pink', 'lightblue', 'lightyellow']
}]

Useful for comparing two levels of categorization — e.g., outer ring = category, inner ring = subcategory breakdown within the selected category.


Radar Chart

Radar charts (also called spider charts) plot values on multiple axes radiating from a center point. Each axis represents a variable.

new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ['Speed', 'Strength', 'Agility', 'Endurance', 'Accuracy', 'Strategy'],
    datasets: [
      {
        label: 'Player A',
        data: [90, 75, 85, 70, 80, 65],
        backgroundColor: 'rgba(54, 162, 235, 0.2)',
        borderColor: 'rgb(54, 162, 235)',
        pointBackgroundColor: 'rgb(54, 162, 235)',
        pointRadius: 4
      },
      {
        label: 'Player B',
        data: [70, 85, 60, 90, 75, 80],
        backgroundColor: 'rgba(255, 99, 132, 0.2)',
        borderColor: 'rgb(255, 99, 132)',
        pointBackgroundColor: 'rgb(255, 99, 132)',
        pointRadius: 4
      }
    ]
  },
  options: {
    responsive: true,
    plugins: {
      title: { display: true, text: 'Player Comparison' }
    },
    scales: {
      r: {
        beginAtZero: true,
        max: 100,
        ticks: { stepSize: 20, backdropColor: 'transparent' },
        pointLabels: { font: { size: 12 }, color: '#666' },
        grid: { color: 'rgba(0,0,0,0.1)' },
        angleLines: { color: 'rgba(0,0,0,0.1)' }
      }
    }
  }
})

Reading a radar chart: Player A (blue) is strong in Speed and Accuracy. Player B (red) is strong in Strength and Endurance. The overlap reveals their relative strengths at a glance.

Key Radar Options

scales: {
  r: {
    beginAtZero: true,      // Always true for honest comparison
    min: 0, max: 100,
    ticks: {
      stepSize: 10,          // Grid line interval
      callback: (v) => v + '%',
      backdropColor: 'rgba(255,255,255,0.8)'  // Background behind tick labels
    },
    pointLabels: {
      font: { size: 12, weight: 'bold' },
      color: '#333',
      padding: 20             // Distance from edge
    },
    grid: {
      circular: false,        // false = polygonal, true = circular
      color: 'rgba(0,0,0,0.1)'
    },
    angleLines: {
      display: true,
      color: 'rgba(0,0,0,0.1)'
    }
  }
}

Polar Area Chart

Similar to a pie chart, but each segment has the same angle and the radius varies with the value. This makes small values more visible than in a pie chart.

new Chart(ctx, {
  type: 'polarArea',
  data: {
    labels: ['Red', 'Green', 'Yellow', 'Blue', 'Purple'],
    datasets: [{
      label: 'My Dataset',
      data: [11, 16, 7, 3, 14],
      backgroundColor: [
        'rgba(255, 99, 132, 0.7)',
        'rgba(75, 192, 192, 0.7)',
        'rgba(255, 205, 86, 0.7)',
        'rgba(54, 162, 235, 0.7)',
        'rgba(153, 102, 255, 0.7)'
      ],
      borderWidth: 1
    }]
  },
  options: {
    responsive: true,
    plugins: { legend: { position: 'right' } },
    scales: {
      r: {
        beginAtZero: true,
        grid: { color: 'rgba(0,0,0,0.05)' }
      }
    }
  }
})

Common Options for Circular Charts

options: {
  cutout: '50%',           // Doughnut hole size (pie = 0)
  radius: '100%',          // Chart radius
  rotation: 0,             // Start angle in degrees
  circumference: 360,      // Sweep angle (360 = full circle, 180 = half)
  animation: {
    animateRotate: true,   // Spin on load
    animateScale: false    // Grow from center on load
  }
}

Common Mistakes Beginners Make

1. Too Many Pie Slices

Pie charts with more than 5-7 slices become unreadable. Group small values into “Other” or switch to a bar chart.

2. Pie Without Percentage Tooltips

Without showing percentages, viewers can’t compare slice sizes accurately. Always add the tooltip callback shown above.

3. Radar Chart With Too Many Variables

More than 8-10 axes create a cluttered “spider web” that’s hard to read. Each additional axis reduces clarity.

4. Doughnut Cutout Too Large

For cutout values above 80%, the ring becomes too thin for effective visual comparison. Keep cutout between 40-70%.

5. Forgetting beginAtZero on Radar Axis

Radar charts without beginAtZero can exaggerate small differences. Always start radial axes at zero.


Practice Questions

  1. What is the difference between a pie chart and a doughnut chart? A pie chart has a solid center (cutout: 0). A doughnut has a hole (cutout: '50%'). Doughnuts are preferred because the center can display summary info.

  2. How do you display the total value in the center of a doughnut chart? Chart.js doesn’t have built-in support. Use a custom plugin with a beforeDraw hook that draws text at the center of the canvas.

  3. What does the hoverOffset property do in a pie chart? It pulls the slice outward from the center when the user hovers over it, creating a “pop-out” effect.

  4. What’s the maximum recommended number of slices for a pie chart? 5-7. Beyond that, switch to a bar chart or group small values into “Other.”

  5. When should you use a polar area chart instead of a pie chart? When you want small values to be more visible. In a polar area chart, each segment has the same angle but different radius, making all segments equally distinguishable.

Challenge

Create a skills assessment dashboard with:

  • A radar chart comparing “Current” vs “Target” skill levels across 6-8 skills
  • A doughnut chart showing time allocation (Learning, Building, Reviewing, Planning, Testing)
  • Percentage values in the doughnut tooltips
  • A slider that adjusts the target skill levels in the radar chart dynamically

FAQ

How do I sort pie slices by size?

Chart.js renders slices in data array order. Sort the data array (and corresponding labels) before passing them to the chart configuration.

How many slices is too many for a pie chart?

5-7 maximum. More slices should be grouped into an “Other” category. Beyond that, switch to a bar chart for readability.

Can a radar chart use a time scale?

No. Radar charts use a radial scale with categorical labels. For time-based data, use a line chart.

How do I add labels inside pie slices?

Use the chartjs-plugin-datalabels plugin. It positions text labels inside each slice with percentage, value, or custom text display.

What does circular: false do on a radar chart grid?

It makes the grid polygonal (straight lines between axes) instead of circular. Polygonal grids make it easier to read values along each axis.

Try It Yourself: Skills Assessment Dashboard

Compare current vs target skill levels on a radar chart, and see your time allocation on a doughnut chart.

<!DOCTYPE html>
<html>
<head>
  <title>Skills Assessment Dashboard</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; max-width: 800px; }
    .card { border: 1px solid #ddd; border-radius: 8px; padding: 15px; }
    .card h3 { margin: 0 0 10px 0; font-size: 14px; color: #666; }
  </style>
</head>
<body>
  <h2>Skills Assessment Dashboard</h2>
  <div class="dashboard">
    <div class="card"><h3>Skill Levels (Radar)</h3><canvas id="radarChart"></canvas></div>
    <div class="card"><h3>Time Allocation (Doughnut)</h3><canvas id="doughnutChart"></canvas></div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script>
    new Chart(document.getElementById('radarChart'), { type: 'radar', data: { labels: ['JavaScript', 'Python', 'CSS', 'SQL', 'DevOps', 'Testing'], datasets: [{ label: 'Current', data: [85,70,75,60,40,55], backgroundColor: 'rgba(54,162,235,0.2)', borderColor: 'rgb(54,162,235)', pointBackgroundColor: 'rgb(54,162,235)' }, { label: 'Target', data: [90,85,80,80,70,75], backgroundColor: 'rgba(255,99,132,0.2)', borderColor: 'rgb(255,99,132)', pointBackgroundColor: 'rgb(255,99,132)' }] }, options: { responsive: true, scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20 } } } } })
    new Chart(document.getElementById('doughnutChart'), { type: 'doughnut', data: { labels: ['Learning', 'Building', 'Reviewing', 'Planning', 'Testing'], datasets: [{ data: [35,30,15,12,8], backgroundColor: ['rgb(54,162,235)', 'rgb(75,192,192)', 'rgb(255,205,86)', 'rgb(153,102,255)', 'rgb(255,159,64)'] }] }, options: { responsive: true, cutout: '60%', plugins: { legend: { position: 'right' }, tooltip: { callbacks: { label: (ctx) => { const total = ctx.dataset.data.reduce((a,b)=>a+b,0); return ctx.label + ': ' + Math.round(ctx.parsed / total * 100) + '%' } } } } } })
  </script>
</body>
</html>

What to expect: Left side shows a radar chart comparing current skills (blue) against target skills (red) across 6 dimensions. Right side shows a doughnut chart of time allocation with percentage tooltips on hover.


What’s Next

TutorialWhat You’ll Learn
Scatter & Bubble ChartsX/Y scatter plots, three-variable bubble charts
Chart.js AdvancedAnimations, interactions, custom plugins

Related topics: Chart.js Line & Bar, Data Visualization best practices.

What’s Next

Congratulations on completing this Chartjs Pie Doughnut tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro