Introduction to data visualization with C3.js and Glitch

Finished project

Recently I started taking an online course on data visualization with D3.js. I’ve been quite enjoying the course, but I was a bit surprised how hands-on D3.js is. There is nothing wrong with this approach, but since I’m more interested in learning how to create charts using interesting data, rather than how to use a specific library, I decided to have a look at C3.js as well. This is a library that works alongside of D3.js, adding a much simpler API.

To illustrate how they differ, consider this short snippet from the C3.js website.

const chart = c3.generate({
    data: {
        columns: [
            ["data1", 30, 200, 100, 400, 150, 250],
            ["data2", 50, 20, 10, 40, 15, 25]
        ]
    }
});

Pair that with a div element that has an id chart, and you’re all set. In my mind, C3.js gets a lot closer to the idea of “data-driven documents”, because it lets you focus on the data when writing your code.

With D3.js, a similar chart would be set up like this:

const data = [30, 200, 100, 400, 150, 250];

const x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 400]);

d3.select("#chart")
    .selectAll("div")
    .data(data)
    .enter().append("div")
    .style("width", (d) => `${ x(d) } px`)
    .text((d) => d);

Now, D3.js is definitely worth learning, but for my own purposes, I’m fine with taking a shortcut.

In this tutorial I’m going to walk you through a C3.js starter project I set up on Glitch and share some useful resources for data visualization.

Sample project: Can Money Buy Happiness?

For my first C3.js project I decided to use the World Happiness Report dataset on Kaggle to try to answer a simple question: Can money buy happiness? Let’s see what we can find out by comparing the self-reported happiness score to a dataset of median incomes published by Gallup.

Now, preparing data for your visualization would be a topic for a whole another tutorial, so go ahead and download the combined dataset here.

Next, we’ll remix my C3.js starter project. A few things to note here. You don’t need to create an account to use Glitch to follow along. This starter project comes with quite a few files (you can read the project’s README where I get into technical details), but in this tutorial we will only work with two of them:

  • src/scripts/scripts.js
  • views/home.handlebars

When your project is ready, go to the assets folder in the left sidebar.

From here, you can use the Add asset button to find the dataset you downloaded and upload it to your project.

Once the file is uploaded, click it and copy the URL.

Next, find the file scripts.js inside the src/scripts/ folder. Initially it will look like this:

helpers.ready(() => {
    helpers.loadData({
      url: "", // URL to your dataset
      type: "" // json, csv, or whatever the format of your data is 
    }, function (err, data){
      if (data){
        console.log(data);
      }
    });
  });
  

Notice that I am using two helper functions. helpers.ready lets me wait until the page is fully loaded before I start doing anything, and helpers.loadData loads the data I want to use in my project. It also caches the data in the browser, which is particularly useful for large datasets. (The data is cached for ten minutes, you can change this value inside the helpers/general.js file on line 25.)

Use the URL of your dataset as the value for url, and for type use csv, which is the extension of the dataset file. (Here’s an explanation on what CSV files are, if you’re not familiar with this format.)

Your scripts.js file should now look like this:

helpers.ready(() => {
    helpers.loadData({
        url: "https://cdn.glitch.com/805a7324-2ed4-4473-9d39-0a1ce8ce096e%2F2017-happiness-income.csv?1548391741428",
        type: "csv"
    }, (err, data) => {
        if (data){
            console.log(data);
        }
    });
});

This is already enough for us to start exploring the data. Click  the Show button near the top left corner to see your page and open your browser’s console.


So far so good. We have our data, now we can visualize it.

Open the views/home.handlebars file and replace its content with the following code:

<div id="chart"></div>

Now back to your scripts.js file. This is the basic code we need to generate a chart from our dataset:

const chart = c3.generate({
    data: {
      json: data,
      axes: {
          income: "y",
          happiness: "y2"
      },          
      keys: {
        x: "country",
        value: ["happiness", "income"]
      }
    },
    axis: {
      x: {
        type: "category"
      },
      y: {
        show: true,
      },
      y2: {
        show: true
      }          
    }
  });

What we’re doing here is taking the dataset you saw in your browser’s console, rendering it as a chart, and setting up the axes of the chart. If we preview our page, the chart looks —

like it needs a bit more work. One problem is that we have too many countries, and that makes the country labels impossible to read. For now, let’s just remove them using the onrendered function. I also tweaked the size of the chart a little bit.

const chart = c3.generate({
    data: {
      json: data,
      axes: {
          income: "y",
          happiness: "y2"
      },          
      keys: {
        x: "country",
        value: ["happiness", "income"]
      }
    },
    axis: {
      x: {
        type: "category"
      },
      y: {
        show: true,
      },
      y2: {
        show: true
      }          
    },
    size: {
      height: (window.innerWidth < 800 ? 500 : 300)
    },
    onrendered: () => {
      d3.selectAll(".c3-axis.c3-axis-x .tick text").style("display", "none");
    }
  });

Great, this is already looking pretty good. We can still hover over the chart to reveal the country for each data point.

Finally, let’s update the axis object to label the axes of our chart.

const chart = c3.generate({
    data: {
        json: data,
        axes: {
            income: "y",
            happiness: "y2"
        },          
        keys: {
            x: "country",
            value: ["happiness", "income"]
        }
    },
    axis: {
        x: {
            type: "category"
        },
        y: {
            show: true,
            label: {
                text: "Median income",
                position: "outer-middle"
            },
            tick: {
                format: d3.format("$,")
            }
        },
        y2: {
            show: true,                
            label: {
                text: "Happiness",
                position: "outer-middle"
            },
            tick: {
                format: d3.format(",")
            }
        }
    },
    size: {
        height: (window.innerWidth < 800 ? 500 : 300)
    },
    onrendered: () => {
        d3.selectAll(".c3-axis.c3-axis-x .tick text").style("display", "none");
    }
});

Not too bad.

There’s definitely more that could be done here, but this should be enough to get your started with C3.js. Be sure to read through the library’s documentation and browse the examples.

You can look at my finished project (source code) where I also added a search field so that you can filter by country, and made the page look a bit nicer.

Now, as for the chart itself, it is interesting to see that while there is clearly a correlation between the median income and self-reported level of happiness, we can see that there is a lot of outliers in either direction. All this goes in hand with studies that show that higher income does generally make people happier, but there is a cut off point, that money buys only a certain kind of happiness, and strong family relationships are a more important factor than money when it comes to being happy.

I hope this tutorial helped you understand the basics of working with C3.js and got you interested in trying it yourself. I’d be happy to answer any questions over email or via Twitter, but feel free to also share what you made.

And as promised, here are some useful resources:

More tutorials

A tinted screenshot of two charts, one showing the popularity of various fediverse platforms (with Mastodon far ahead of the rest), and the other chart showing distribution of domain creation dates, mostly clustered around 2023.
A tinted screenshot showing the @mtaupdates Mastodon profile and a few example posts with subway status alerts.
A tinted screenshot showing the finished bot posting a flag and asking about a corresponding capital, followed by a reply from me with a correct answer, and the bot accepting it.

💻 Browse all