JavaFX comes with 8 charts out of the box. This resource focusses on the charts JavaFX provides by default, rather than how to extend them. I’ll look at how to create them, update them, format them.
A JavaFX Chart
is a node designed to display data graphically. Charts display data as symbols such as bars (the BarChart
), lines (LineChart
), slices (PieChart
), points (ScatterChart
) or a combination of these.
All charts work by using extra nodes to represent data points and lines. These are laid out in Pane referred to as the plot area, rather than using a Canvas.
How to use this resource
I’ll cover three basic areas in creating and maintaining graphs in JavaFX.
Types of Chart
JavaFX provides 8 default charts to display data. 7 of these have axes and are designed to show data in two dimensions along those axes.

Charts with Axes: 7 of the 8 JavaFX charts display data on a two-axis chart. These all extend the XYChart
object, a sub-class of Chart
, which is responsible for displaying the axes. Each of the chart instances is responsible for placing data points, connecting lines, bars and boxes.
Charts without axes: the PieChart
is the only chart that comes with JavaFX that doesn’t use axes. PieChart
directly extends the Chart
class and is responsible for all of its own plotting requirements.
Most operations you’ll do on a chart will depend on whether that chart has axes. So, in each section, I’ll deal with pie charts first, mostly because they’re simpler, and then how to do the same operations on charts with axes.
How to create a Chart
JavaFX charts are a node, like any other UI element, which means you can add it to a layout just like anything else. If you haven’t checked out my article on layouts, I would definitely recommend it. I tried to create a resource that explains every layout and how it sizes and positions it’s children. And, honestly, the time I’ve saved just referring back to it myself while I’m designing programs made it time well-spent.
If you’re creating a pie chart, you can create either an empty PieChart
, or optionally initialize it with data.
However, if you’re creating a chart with axes, you’ll need to create your chart with its axes. You can then optionally also create it with data. An XYChart
cannot be created without axes.
Creating a Pie Chart.
OK this couldn’t be simpler.
PieChart pieChart = new PieChart();
You can also create them with data, but I’ll cover data a little further down, and then you can loop back and put the data in the constructor if you want. Here’s the method prototype for creating a PieChart with data, though:
PieChart pieChart = new PieChart(ObservableList<PieChart.Data> data)
Creating charts with axes
Any chart with axes is going to require a little more effort, because you will need to specify those which axes you’re plotting against in the constructor.
The absolute simplest way to do this is by creating unparameterized axes as you create your chart
AreaChart<Number, Number> areaChart = new AreaChart<>( new NumberAxis(), new NumberAxis() );
To be honest, I wouldn’t I’d always create my axes separately, parameterize them, and pass them in. But for a quick-start, that will work on every type of axis-based chart.
On top of that, you can also provide data and (where appropriate) a category gap.
Chart | Axes | Data (as a list) | Category gap |
---|---|---|---|
AreaChart | Required | Optional | |
BarChart | Required | Optional | Optional |
BubbleChart | Required | Optional | |
LineChart | Required | Optional | |
ScatterChart | Required | Optional | |
StackedAreaChart | Required | Optional | |
StackedBarChart | Required | Optional | Optional |
It doesn’t actually matter to JavaFX what’s on the axes – they can be numbers, or they can be ‘categories’. In fact any graph can have any two types of axes, but some combinations are more sensible than others.
Line charts generally show a trend, so showing a line graph with two Category axes is possible, but probably has limited uses.
Equally, you can make a bar chart with a numerical X-axis, but this isn’t the expectation. The BarChart
and StackedBarChart
expect that one axis will be categorical. In fact, whether it’s a horizontal or vertical bar chart will depend on which axis is categorical.
Creating the chart with FXML
Similarly charts can be created using FXML. This time, the axes should be defined using the <xAxis>
and <yAxis>
tags.
<LineChart fx:id="worldPopulationChart"> <xAxis> <NumberAxis fx:id="yearAxis" label="Year"/> </xAxis> <yAxis> <NumberAxis fx:id="populationAxis" label="Population (billion)" lowerBound="-1e9" upperBound="7e9" autoRanging="false" tickUnit="1e9"/> </yAxis> </LineChart>
Warning: If you define an axis in FXML and you want to set the upper and lower bounds yourself, you will also need to specify autoRanging="false"
as FXML-created number axes are created by default with auto-ranging enabled.
Axes
There are a lot of benefits to parameterizing axes, which isn’t core to creating charts. If you’re interested, the dropdown has much more detail on how to make axes that work for you.
Axes are responsible for the range that they cover and the way that they display information in that range.
For example, number axes can instruct plots to fill the gaps between tick marks in a zebra-stripe pattern which can make data more visually useful to readers.
Here’s how to make exes work for you.
Creating a NumberAxis
To create a number axis, we can either create an empty NumberAxis
and set the label subsequently, or pass the name of the axis along with its range at instantiation.
NumberAxis axis = new NumberAxis();
To create an axis that shows the population of the world over the last 50 years, we’d create a NumberAxis
with a range that spans from zero to 7 billion.
NumberAxis populationAxis = new NumberAxis(); populationAxis.setLabel("Population", -1e9, 7e9, 1e9);
On top of the basic name and range, the NumberAxis is also responsible for tick marks (but not gridlines), and can force the range of the axis to extend to make sure that zero is displayed.
Forcing zero into range won’t override lower and upper bounds you’ve set yourself, but it’s useful for auto-ranging.
Creating a CategoryAxis.
As with the NumberAxis, a category axis can be created as an empty axis, or with a list of defined categories.
CategoryAxis xAxis = new CategoryAxis();
Warning: if you’re using categories, it’s definitely worth bearing in mind that JavaFX does not consider categories ordered. If you don’t provide them at construction, categories are added to the axis in the order that you add them as data points even if the object that you use to define your categories implements comparable.
That means if you want to order your categories before you load your data in, it’s definitely safest to order them in a list yourself and load them in when you create the axis.
ObservableList<String> weekdays = FXCollections.observableArrayList("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"); CategoryAxis categoryAxis = new CategoryAxis(weekdays);
Because you can’t interpolate between categories, the CategoryAxis
object doesn’t handle ranging in the same way as the NumberAxis
. However, you can still effect the positioning of items on the axis by setting the margin at the beginning and end of the axis, as well as the gap between categories.
Updating your Chart
Updating a chart can either be used to add initial data if you created your chart without data, or to update a chart that already has data by adding or removing data.
For pie charts, invoking getData()
on an instance of the chart will return an ObservableList
of PieChart.Data
objects that we can modify.
For charts with axes, invoking getData()
on an instance will return an ObservableList
of XYChart.Series
objects that we can modify to add or remove series. We can also modify each XYChart.Series
object to add or remove data.
I’ll take you through the datatypes as I go through updating each chart type.
Updating a pie chart
Pie charts don’t have axes, and don’t support visualising more than one set of data. That makes pie chart data relatively simple.
Adding data to a Pie Chart
Invoking getData()
on an instance of the chart returns a modifiable ObservableList
of PieChart.Data
objects.
Every PieChart.Data
object represents one slice of the pie. A PieChart.Data
object is responsible for the name of the slice, the value of the slice, and holds the node that will be used to display the slice on the plot area.
The name of a slice must be a String, and the value must be a double. You need to define both of these when you create the data object, although you can update them later.
PieChart.Data data = new PieChart.Data("Monday", 10);
Adding data to the pie chart is just a case of passing in one or more PieChart.Data objects to the ObservableList
using methods common to all observable lists:
//add data maintaining a strong reference to it PieChart.Data tuesday = new PieChart.Data("Tuesday", 12); chart.getData().add(tuesday); //add data, losing the reference chart.getData().addAll( new PieChart.Data("Wednesday", 15), new PieChart.Data("Thursday", 17), new PieChart.Data("Friday", 4) ); //add data at a specific point in the list chart.getData().add( 0, //add data to the start of the list new PieChart.Data("Monday", 5));
Removing data from a Pie Chart
Again by invoking getData()
on an instance of the chart returns a modifiable ObservableList
of PieChart.Data
objects. We can use this list to remove objects by either object reference or index.
chart.getData().remove(0); //remove the first item chart.getData().remove(tuesday);
Updating a chart with axes
One benefit of JavaFX’s decision to encapsulate all of the data maintenance in the XYChart
class is that in spite of the different ways charts can be displayed, all of the operations for updating and maintaining their data are the same.
Axis charts are designed to facilitate showing one or more data series of two dimensional data. Every series is defined as an XYChart.Series
object. Inside each series is an ordered list of XYChart.Data
objects.
If you’ve parameterized your chart, which I would always recommend you do, the Data objects inside each series will inherit that parameterization. This makes code more explicit, and reduces hard to detect errors.

You can update a chart either by adding and removing series, or by modifying series by adding and removing data points.
Adding a series
By invoking getData()
on an instance of any chart with axes returns a modifiable ObservableList
of XYChart.Series
objects. We can just add XYChart.Series
objects to this list
//add series losing reference from controller barChart.getData().add( new XYChart.Series<>() //this is an empty series, but doesn't have to be. ); //add maintaining strong reference XYChart.Series<String, Number> dailyEnergy = new XYChart.Series<>( FXCollections.observableArrayList( new XYChart.Data<>("Monday", 10), new XYChart.Data<>("Tuesday", 17), new XYChart.Data<>("Wednesday", 15), new XYChart.Data<>("Thursday", 3), new XYChart.Data<>("Friday", 45) ) ); barChart.getData().add(dailyEnergy);
Removing a series
Again let’s invoke getData()
to get a modifiable ObservableList
of XYChart.Series
objects. We can use this list to remove objects by either object reference or index.
barChart.getData().remove(0); //remove the first item barChart.getData().remove(dailyEnergy);
Adding data points
You can also individually add data points to a series that’s already being displayed on a chart. You can do this with or without a reference to the original series, although it is significantly easier with a reference to the series.
If you still have the reference to the series that holds the data points, you can update the series directly. The ordered list of data in XYChart.Series
is observable, so the chart will update if you change the list.
dataSeries.getData().add(new XYChart.Data<>("Monday", 1));
If we’ve lost the reference to the data series, we can still get it, but the method chaining can look a bit long. Bear with me.
We need to:
- Get the list of data series,
- Find the series we want (let’s get the first one),
- Return the data points from that series, and
- Add the data.
That looks like this:
barChart.getData().get(0).getData().add(XYChart.Data dataPoint)
It’s safe to say I’d always recommend keeping a reference to a series if you want to update it…
Removing old data from charts
Assuming we’ve kept the reference to the data series we want to modify, removing data can be done either by index or by object reference.
dataSeries.getData().remove(reference); dataSeries.getData().remove(0);
This can be really useful for real-time graphs, where you want to remove old data to avoid graphs becoming cluttered.
Creating Live Charts
By combining the steps above for adding and removing data, it’s a really easy next step to creating live charts. The two things we need to do to accomplish this are:
There are several really good ways to achieve this. In fact, if you’ve ever struggled to get something to update in JavaFX, I suggest you check out how to make sure your scene never freezes again.
What we’ll do here is based on the idea of using events to drive updates on the window. Rather than using executor services and having to dump the code into Platform.runlater()
calls, the event-handling process is purpose-built to manage updates to the UI.
To create a background process, let’s just create a Timeline that runs every second. We’ll instruct it to fire an ActionEvent every time.
Creating an event to update the Chart
Let’s create an EventHandler that will update our chart for us. Code in event handlers is always fired on the UI thread, so we’re guaranteed not to run into any thread-based problems.
First, we’ll create a member variable to track how many points we’ve plotted so far. Because we’ll start with 9 points, let’s set it to 9. You might want to find a more elegant way to do this, but it’s a good start.
int numberOfPoints = 9;
Now we’ll create an event handler that’s going to update the chart with a new point every time.
EventHandler<ActionEvent> chartUpdater = new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { Random random = new Random(); //add a new point to the chart revenueData.getData().add( new XYChart.Data<>(numberOfPoints++, random.nextDouble() * 1e8) ); //remove the first point, because that's the left-most. revenueData.getData().remove(0); } };
Firing the event every second
Once we’ve got that sorted, we just need to enter the code that’s going to fire our event every second. We can set this using the Duration
value passed to the KeyFrame
object, so you’re not limited to running events on a second-by-second basis.
Timeline updateChart = new Timeline(new KeyFrame(Duration.seconds(1), chartUpdater)); updateChart.setCycleCount(Timeline.INDEFINITE); updateChart.play();
This code uses JavaFX’s in-built animation support to fire an event every second. And it uses JavaFX’s in-built event support to update the chart on the UI thread.
Events are perfect when it comes to driving changes in your view. You could set this process to run when a user requests an update, or on a timer, both with events. To learn more about driving changes in your UI with events, check out this article on exactly that..

If you prefer your chart not to grope around like that while it finds where to plot the new point, you can turn animation off using chart.setAnimated(false)
.

If you’re pretty sharp, you might have noticed that the y-axis values are also formatted to show a double value with a “bn” units identifier. We can accomplish that with axis formatting.
Formatting your axis values
There are a lot of situations where it’s easier to pass raw values into a NumberAxis
, but you would like the axis itself to be formatted in some way.
To convert a raw number like a double or an integer into formatted String, we can use a StringConverter
object. The StringConverter
object allows you to convert both ways, and so requires you to define methods for toString()
and fromString()
when you create it.
The only method we’re interested in is toString()
as this will be used by the chart to display our number on the axis.
StringConverter
is parameterizable, and so as we create it, we’ll define it’s specific to Number objects. This is going to let us convert numbers to strings. Then, the toString() method takes that Number object and returns a String.
In this case, I’ll create a converter to test whether a number is greater than a thousand. If it is, we’ll divide that number by 1000 and add “k” to denote thousands instead.
StringConverter<Number> axisFomatter = new StringConverter<Number>() { @Override public String toString(Number axisValue) { if(axisValue.doubleValue() >= 1000){ return (axisValue.doubleValue() / 1e9) + " bn"; } else { return axisValue.toString(); } } @Override public Number fromString(String string) { return null; } };
Then, we just need to set it on the axis by invoking setTickLabelFormatter()
with our converter.
axis.setTickLabelFormatter(axisFomatter);
Conclusions
JavaFX provides 8 charts that can be used to visualise data. 7 of these charts display data plotted on two axes.
Creating charts is simpler than you might think. Even with charts taht require axes, these can be created either in Java code, or via FXML. Axes can be modified to specify the plot area in which to display data by setting lower and upper bounds.
It’s simpler than you think to combine these bits of code to create a simple live chart. By using JavaFX’s in-built animation Timeline, and its event handling framework, we can create an event to fire every second and use it to update our chart.
Finally, axis tick labels can be modified by setting a StringConverter
, which will manipulate the given axis value and return a formatted String, which the axis will use to display the label.