Relationship / Intermediate

Bubble Chart

Compare three numeric variables with position and bubble size.

Back to collection

Interactive Chart Preview

JAVASCRIPT
Live, interactive exampleChart.js

Chart.js Code Example

JAVASCRIPT
import Chart from 'chart.js/auto';

// Illustrative data used by the live preview.
const chartData = {
  "pointLabels": {
    "name": "Company",
    "x": "Revenue ($B)",
    "y": "Growth (%)",
    "radius": "Radius (px)"
  },
  "datasets": [
    {
      "label": "Company",
      "data": [
        {
          "x": 20,
          "y": 30,
          "r": 15,
          "company": "Company A"
        },
        {
          "x": 40,
          "y": 10,
          "r": 10,
          "company": "Company B"
        },
        {
          "x": 30,
          "y": 40,
          "r": 20,
          "company": "Company C"
        },
        {
          "x": 60,
          "y": 35,
          "r": 12,
          "company": "Company D"
        },
        {
          "x": 50,
          "y": 25,
          "r": 18,
          "company": "Company E"
        },
        {
          "x": 35,
          "y": 15,
          "r": 8,
          "company": "Company F"
        },
        {
          "x": 25,
          "y": 50,
          "r": 22,
          "company": "Company G"
        }
      ],
      "backgroundColor": [
        "rgba(255, 99, 132, 0.6)",
        "rgba(54, 162, 235, 0.6)",
        "rgba(255, 205, 86, 0.6)",
        "rgba(75, 192, 192, 0.6)",
        "rgba(153, 102, 255, 0.6)",
        "rgba(255, 159, 64, 0.6)",
        "rgba(199, 199, 199, 0.6)"
      ],
      "borderColor": [
        "rgba(255, 99, 132, 1)",
        "rgba(54, 162, 235, 1)",
        "rgba(255, 205, 86, 1)",
        "rgba(75, 192, 192, 1)",
        "rgba(153, 102, 255, 1)",
        "rgba(255, 159, 64, 1)",
        "rgba(199, 199, 199, 1)"
      ],
      "borderWidth": 2
    }
  ]
};

function createConfig(chartData) {
    let chartType = 'bar';
    let options = {
        responsive: true,
        maintainAspectRatio: false,
        plugins: {
            legend: {
                position: 'bottom',
            },
            tooltip: {
                mode: 'nearest',
                intersect: false,
            },
        },
    };
    chartType = 'bubble';
    const labels = chartData.pointLabels;
    options.scales = {
        x: {
            type: 'linear',
            position: 'bottom',
            title: { display: true, text: labels.x },
        },
        y: {
            type: 'linear',
            title: { display: true, text: labels.y },
        },
    };
    options.plugins.legend.display = false;
    options.plugins.tooltip = {
        callbacks: {
            label: function (context) {
                const point = context.raw;
                return [
                    `${labels.name}: ${point.company}`,
                    `${labels.x}: ${point.x}`,
                    `${labels.y}: ${point.y}`,
                    `${labels.radius}: ${point.r}`,
                ];
            },
        },
    };
    return { type: chartType, data: structuredClone(chartData), options };
}

const chart = new Chart(document.getElementById('myChart'), createConfig(chartData));

Preview and code follow the same library selection.

Run This Example

Install the package below in a browser project such as Vite. Put the copied code in a client JavaScript module and add this container to the page before the module runs.

npm install chart.js@4.5.1
<div style="height:320px"><canvas id="myChart"></canvas></div>

The example includes its sample data. When removing the chart, destroy or dispose of its instance and disconnect any resize observer.

Reading This Example

The default Chart.js example uses seven fictional companies. Company D has the largest x value, 60, while Company G has the largest y value, 50, and radius, 22 pixels. The axes illustrate revenue in billions and growth in percent; these are synthetic values, not real company measurements.

All library tabs use these illustrative values. Rendering styles and library APIs differ, so use the table to compare exact numbers.

About this example & sample data
Sample values used in the chart
CompanyRevenue ($B)Growth (%)Radius (px)
Company A203015
Company B401010
Company C304020
Company D603512
Company E502518
Company F35158
Company G255022

READ THE CHART

Compare Two Values and a Size

A bubble chart extends a scatter plot with differently sized markers. Position encodes two numeric variables, and size adds a third. Each bubble still represents one observation, so every encoding needs a clear label.

01

Name All Three Measures

State what the horizontal position, vertical position, and bubble size mean. Use a label or tooltip to identify each observation. Avoid adding more encodings unless they help answer the question.

02

Scale Area Carefully

If size represents a quantity, make area proportional to that quantity. Radius must then grow with its square root. The Chart.js example supplies r directly in pixels, so doubling r makes the bubble area four times larger.

03

Manage Overlap

Use transparency and restrained sizes so large bubbles do not hide smaller ones. A size legend helps readers interpret area. For crowded data, separate groups or use a simpler scatter plot.

04

Read Position Before Size

Find high and low x and y values before comparing bubbles. Size alone does not indicate better performance, and association does not establish causation. A scatter plot is enough when the third measure is not useful.

Edit Chart Data