JavaFX gives you three different ways to run animations in your application. Which one you choose will depend on what you’re trying to animate.
In this article, I wanted to give you a resource on how to use each type of animation, but also about how to choose which animation you use as well. This article goes through how to use each object as well as how to get the most out of them.
Here are what I think are the relative strengths and weaknesses of each of the animation types.
Type | Controls | Strengths | Limitations |
---|---|---|---|
Transition | Nodes | – Targets elements of the scene graph. – Extensible interpolation. | You can only use them on nodes. In some cases, you’re limited shapes |
Timeline | WriteableValue objects like JavaFX properties | – Multiple properties in the same animation. – More complex animations – Extensible interpolation. | Fairly heavy-weight animation object |
AnimationTimer | None | – Direct access to the timestamp of the pulse | No default interpolation |
What you’ll get from this article
A lot of functionality is common to Transitions and Timelines – from the number of times they play, to how the animation interpolates the values. I’ll run through the common features first, so we don’t have to cover them twice.
Then, I’ll take you through the specifics of how to create and use Transition
, Timeline
and AnimationTimer
objects in detail. If you need the specifics now, feel free to jump ahead.
By the end of this article, you should be able to create animations and fire events from them. You’ll be able to change object properties and change how animations interpolate between those values. Finally, you’ll understand how animations work – so when you need to extend that functionality, you’ll have a good basis from which to start.
JavaFX Animations
All of these animations work by listening to an animation pulse that JavaFX sends out once per frame (default frame rate is 60 Hz). Then, every animation decides what to do with that information based on its function.
For animation timers, the only function is to run its one method. For more complex animations, the timestamp is converted into the proportion of the animation that’s already passed, and that result is used to update the animation value.

The frame rate of animations can vary, both from system to system, and based on the CPU load your computer’s under.
When you start up your application, JavaFX works in the background to calculate the right frame rate based on the environment you’re running in – a combination of the system and the screen.
On most desktop systems, JavaFX will run at about 60 FPS. Animation pulses aren’t exactly 60 FPS – they vary based on the application load, but the vast majority of pulses are concentrated around the right number.

The order that animations fire isn’t set, but every running animation is passed the same pulse time, which ensures that every animation is updated by the same amount, even when they’re updated in a different order.
JavaFX does it’s best, but it’s worth saying animations aren’t guaranteed to run smoothly. If you overload the scene, or run too many animations at once, you may see jitters or jumpy animations.
And finally, JavaFX is structured so that the two types of animation – Timeline
and Transition
– inherit from a higher Animation
object. That means there are a lot of features common to every animation in JavaFX. I’ll cover them here before covering the specifics of each type further down.
Common features of every animation
There are seven features that are common between every JavaFX animation:
- Cycle count (how many times an animation plays),
- An animation delay (i.e. the time between pressing play and it beginning),
- Auto-reverse,
- Rate,
- The ability to “jump” to a point in an animation,
- Firing events once an animation is complete, and
- Interpolation
Open up whatever you need, otherwise feel free to move on to the specifics of how to create animations.
Every animation in JavaFX has a cycle count, which means it can be repeated any number of times. You can set this feature using the setCycleCount()
method, which accepts an integer number of cycles.
If you want the animation to keep playing indefinitely, invoke setCycleCount(), passing the convenience variable Animation.INDEFINITE
. Once started, your animation to play until stopped.
FadeTransition fadeTransition = new FadeTransition(); fadeTransition.setCycleCount(Animation.INDEFINITE);
Usually, your animation will start to play once you invoke play()
or playFromStart()
. If you don’t want that to happen – that is, if you want JavaFX to wait for a specified amount of time before it starts your animation, use setDelay()
. Pass it a Duration object of the length you need.
RotateTransition rotateTransition = new RotateTransition(); rotateTransition.setDelay(Duration.millis(500));
If your animation has more than one ‘cycle’, its default behaviour at the end of one cycle will be to start the animation at the beginning.
You can set your animation so that instead of looping like that, it will play from back-to-front during alternating cycles – forwards, backwards, forwards, backwards…
FillTransition fillTransition = new FillTransition(); fillTransition.setAutoReverse(true);
Note: Of course, it will still only play for the number of cycles specified by the cycleCount
property. If it’s set to one, the animation will still finish at the end of the first cycle.
It can be really convenient to factory-make some animations like fades and movement, but play them at different speeds.
If you did this with durations, you’d be in for some trouble. With Transitions, especially with Timelines, where you’d need to recalculate the durations of every key frame (if you don’t know what a key frame is, don’t worry, we’ll get to key frames later)
An easier way is to set the rate of your animation. An animation with a rate of two will complete in half the duration set.
TranslateTransition moveTransition = new TranslateTransition(Duration.millis(2000), myNode); moveTransition.setRate(-1);
You can also get your animation to play backwards by setting the rate to a negative value.
Note: If you set it to -1 right out of the gate, with a cycle count of 1, nothing will happen. That’s because animations with a cycle count of 1 don’t loop. So, it starts at the beginning, tries to play backwards, works out it’s already at the “end”, and stops.
If you want to start from the end and run backwards for one cycle, jump to the end first.
If you need to get your animation to a certain point without playing it through to that point, you can jump to the right point in your animation by invoking jumpTo()
, passing a Duration
object of the right length.
Timeline timeline = new Timeline(); timeline.jumpTo(Duration.millis(4000));
Remember, you’re jumping to a point in the animation, so even if you’re a second in, passing Duration.seconds(2)
will jump you to two seconds, and not jump you forwards by 2 seconds.
Every animation has the functionality to fire events once it’s complete. To add an event at the end of any animation, invoke setOnFinished()
, passing in an EventHandler<ActionEvent>
object.
For convenience, you can define the event handler using the lambda notation, which just helps to make the code a little more concise.
TranslateTransition translateTransition = new TranslateTransition(Duration.millis(2000), myNode2); translateTransition.setOnFinished(event -> { //this code runs when the animation is complete });
Interpolation is the way an animation moves the value of the property its changing between the two set points you’ve given it. And, even though the Animation
class itself doesn’t use interpolation, both the Timeline
and Transition
classes make use of it, so I wanted to cover it more generally here.
I’m not even going to squeeze interpolation into an option drop-down, because I think it’s really important to understand how powerful it can be. You can literally use interpolation to simplify your animations – using fewer transition objects and key frames to achieve the same result.
You can either use the pre-defined interpolators, or you can create your own. The Interpolator class is an abstract class with a single abstract method curve()
. This converts the proportion of the animation that’s already passed into the proportion the value should have changed since the last frame.
Predefined Interpolators
JavaFX gives you five default interpolators: linear, discrete, ease-both, ease-in and ease-out. All Transition
animations by default use ease-both to give the animation a smooth feel. All Timeline
animations by default use linear.
Creating Animations
JavaFX gives you three types of class to support animation out-of-the-box: transition, timeline and animation timers. They’re all different, and fundamentally I’d break them down like this by:
- What’s they’re designed to do
- What values they can change by default
- What sort of animation complexity you can generate
Here’s a breakdown one by one:
Type | Design intention | Values it can change | Complexity |
---|---|---|---|
Transition | Smoothly change scene-graph properties like size, opacity and rotation. | Properties of Node objects (some specific to Shape ) | Define single transitions. Combine using utility classes. |
Timeline | Complex animations of groups of values requiring more precision. | Any WriteableValue (e.g. JavaFX properties) | Define multiple key frames to animate sets of values at each. |
AnimationTimer | Direct access to the timestamp of the animation frame | No values changed | Developer handles implementation |
In each of the next three sections, I’ll go through each animation type and how to use them.
Transitions
A transition is an animation designed to alter specific visible properties of nodes in a scene graph. For more complex transformations, you can combine multiple transitions by nesting them inside ParallelTransition
and SequentialTransition
objects.
You can also change the interpolation, which is how the value changes between the numbers you’ve set.
The default interpolator for any Transition is Interpolator.EASE_BOTH
. If you want to change the interpolator, invoke setInterpolator()
, passing in the Interpolator object you need.
There are eight Transition
classes in JavaFX, which support updating the appearance and transforms of nodes within a scene. Additionally, the ParallelTransition
and SequentialTransition
objects can be used to combine transitions.
Appearance Transitions
JavaFX is really well set-up to handle animations for Shape objects. If you’re trying to animate a Shape, you have access to the FadeTransition
, the FillTransition
, and StrokeTransition
,
But I’m going to level with you – if you want to change the properties of a Region, or Pane, you only have access to the FadeTransition
. That’s because Fill and Stroke are easy to change in Shapes, and much more complex to implement in nodes.
FadeTransition
The FadeTransition
animation is used to animate the opacity of a Node. Both the border and background are affected.

To fade out a node completely, regardless of its current opacity, use the fading from a value by a specific amount approach, setting the byValue
to -1.
FadeTransition fadeTransition = new FadeTransition(Duration.millis(500), myNode); fadeTransition.setByValue(-1); fadeTransition.play();
FillTransition
The FillTransition
animation is used to animate the colour of a Shape node’s inner area (or fill). The transition will smoothly interpolate red, green and blue values associated with the fromValue
color (default is the current color of the Shape) to the toValue
color.

Because this is such a simple animation, the interpolation from the fromValue
to the toValue
can take you through colors that don’t make the animation that appealing. In that case, you can create multiple color transitions and combine them using a SequentialTransition
animation.
This transition has one of the richest sets of constructor options. It won’t work unless you define a Shape
, but you can create the transition and define one later. If you don’t define the fromValue
, the transition will use the current color. Equally, if you don’t define a duration, the transition will use the default value of Duration.millis(400)
.
FillTransition() FillTransition(Duration duration) FillTransition(Duration duration, Color fromValue, Color toValue) FillTransition(Duration duration, Shape shape) FillTransition(Duration duration, Shape shape, Color fromValue, Color toValue)
StrokeTransition
The StrokeTransition
animation is used to animate the colour of a Shape node’s border (or stroke). This is similar to the FillTransition
in all of the options you can specify, varying only by the fact that this will effect the outline of an object, but not its fill.

Again, you can define this transition with no parameters, but to see an effect, you’ll need to define the Shape
and the toValue
as an absolute minimum.
StrokeTransition() StrokeTransition(Duration duration) StrokeTransition(Duration duration, Color fromValue, Color toValue) StrokeTransition(Duration duration, Shape shape) StrokeTransition(Duration duration, Shape shape, Color fromValue, Color toValue)
Transform Transitions
In JavaFX, the three basic transforms are translation, rotation and scale. Each of these is supported by a Transition
object, which provides a useful, minimal implementation to handle smoothly changing the transforms of a JavaFX Node
.
There aren’t any transitions for more complex transforms such as skewing, although you could make one (the Transition
object is fairly easy to extend).
Finally, the PathTransition
is an interesting composite Transition, which can move objects in much more complex shapes. It can also optionally update the rotation of an object as it completes this movement.
I’ll go through each of the transitions in turn and how to use them.
TranslateTransition
The TranslateTransition animation is designed to animate the position of a node within its parent. When JavaFX renders a node to the screen, it will factor into this calculation both the layout position and translate modification, meaning nodes can be positioned and animated separately.
If a start position for the animation is not specified, the animation will start from the current node position. The end position is then calculated either by using the to-properties (which define the end position), or using the by-properties (byX, byY, byZ), which define the size and direction of the movement. If both are specified, the to-properties will be used and the by-properties will be ignored.

Just be aware that once you start changing the translateX
, translateY
and translateZ
the position is a combination translate coordinates and the layout position. Generally, JavaFX was designed to use translate properties for fine adjustments and animation rather than absolute positioning.
If you create a TranslateTransition
without parameters, the default duration will be 400 milliseconds. You can define the Node during construction. All other parameters need to be defined using the setter methods for each property.
TranslateTransition(); TranslateTransition(Duration duration); TranslateTransition(Duration duration, Node node);
RotateTransition
The RotateTransition animation is used to animate the rotation angle of a node. The angles defined for this animation must be specified in degrees, rather than radians.
If a start angle is not provided for the animation, the rotation will begin at the current values of the node. As with the translate transition, the end of the rotation can be defined either using the byAngle
or the toAngle
properties, with toAngle
taking precedence.

I’ve only shown this in two dimensions, but of course JavaFX has fantastic support for 3D animations too.
To enable rotation in three dimensions, you’ll need to specify the rotation axis. Nodes already have a rotationAxis
property. That means you can either specify the axis on the node directly, or using the transition object. When you specify the rotation axis in the RotateTransition
, all it does is change the node’s rotationAxis
property anyway!
As always, if you create a RotateTransition
without parameters, the default duration will be 400 milliseconds. You can define the Node during construction. All other parameters need to be defined using the setter methods for each property.
RotateTransition(); RotateTransition(Duration duration); RotateTransition(Duration duration, Node node);
ScaleTransition
The ScaleTransition
animation is used to animate the size of a node. By specifying the start (optional) and end, or increase values, you can produce smooth animations in the size of part of the scene graph.
A note on scaling behaviour: Because scaling is usually multiplicative (if you have a scale of 2, you’ll get something two times as big), you might think that this would be how byX
, byY
and byZ
properties work. Just be warned they don’t!
If your fromX
value is 1 (it’s the right size), and your byX
value is 1, the two are added together (not multiplied) to get a final scale value of 2.
So, to shrink things, you will need to use negative numbers (not decimals like 0.5 as you might think).
As with other transitions, the toX
, toY
and toZ
properties will override any byX
, byY
and byZ
values that have been set.

If you create a ScaleTransition
without parameters, the default duration will be 400 milliseconds. As always, to make it work, you’ll have to give it a Node
and some from-properties to see a visible change in the scene.
ScaleTransition(); ScaleTransition(Duration duration); ScaleTransition(Duration duration, Node node);
Just remember, because you’re scaling your node, the scaling will also be applied to children. This transition doesn’t change the size of the node in terms of width and height (although they will visibly change). It alters the scaling factor that’s applied to the node – and all of the node’s children.
PathTransition
The PathTransition
animation is used to animate the location of objects over more complex paths. It can optionally be used to alter the rotation too, by setting the Orientation
property to ORTHOGONAL_TO_TANGENT
. This makes the y-axis of the shape line up with the direction of the path the object is following.

A note on translation and rotation: Later on, I’ll go through ways to combine transitions. Just be aware that the PathTransition
doesn’t do anything special when it comes to moving your object – it just updates the translate and rotate values.
When I tested this, if you try and combine it with a rotate or translate animation in a ParallelTransition
, the animation displayed currently favours the transition you’ve registered last – presumably because the current implementation updates the values in that order. That’s not guaranteed to stay unchanged, though, so try not to combine conflicting animations.
As with other transitions, the PathTransition
can be created with no parameters, but it won’t do anything. To make it work, you’ll need to define a Path
object, and the Node
as a minimum.
PathTransition(); PathTransition(Duration duration, Shape path); PathTransition(Duration duration, Shape path, Node node);
If you want your node to change its direction based on its direction of travel, don’t forget to set the Orientation
property to ORTHOGONAL_TO_TANGENT
Timeline
Timelines are the most complex animations in JavaFX, with several classes like the ClipInterpolator
and TimelineClipCore
that it delegates to to get the job done. But with that complexity is an animation framework that packs a punch.
The JavaFX Timeline
object is an Animation
designed to update multiple properties over a series of key frames, using multiple interpolation types across an animation. Not all properties must be defined in each key frame, and interpolation is based on the interpolation defined at the end of an interval.
Timelines can be created without any parameters, as well as by specifying keyframes during construction.
Timeline(); Timeline(double targetFramerate); Timeline(double targetFramerate, KeyFrame... keyFrames); Timeline(KeyFrame... keyFrames);
You can also change the framerate of the animation be setting the targetFramerate
property. You can’t make an animation run more quickly than the highest framerate of the JavaFX animation pulse. However, you can make it fire more slowly.
Key frames are the backbone of Timeline animations. In the next two sections, I’ll cover how to create and modify key frames and key values.
What is a Key Frame?
A key frame is a snapshot in time that captures the states of multiple properties, allowing an animation to move between them. In JavaFX, key frames also manage the interpolation of the time period between the previous key frame and the present one.

A simple animation with two key frames
Here’s an example of an animation with two key frames. It’s easy to define a key frame – we just pass in the time at which it occurs (in the form of a Duration object) and the properties and values it should keep track of.
We’ll add these key frames into the animation by creating the Timeline and passing in each KeyFrame object in a row. I’ll go into more detail about key frames in a minute but they’re essentially an object that holds a reference to a property and a value you want that property to equal.
Rectangle myNode = new Rectangle(100, 100, 100, 100); myNode.setFill(Color.web("#375fcb")); pane.getChildren().add(myNode); myNode.setArcHeight(25); myNode.setArcWidth(25); Timeline timeline = new Timeline( new KeyFrame( Duration.ZERO, new KeyValue( myNode.widthProperty(), 50d), new KeyValue( myNode.heightProperty(), 150d ) ), new KeyFrame( Duration.seconds(1.5), new KeyValue( myNode.widthProperty(), 150d ), new KeyValue( myNode.heightProperty(), 50d ) ) ); timeline.setAutoReverse(true); timeline.setCycleCount(Animation.INDEFINITE); timeline.play();
The output of this simple code is a phenomenal tour-de-force in Oscar-winning animation….
Okay, maybe it’s not all the way to the Academy Awards.. Alright, onto key values.
How to modify a key frame
The Timeline
class stores key frames as an ObservableList
of KeyFrame
objects. You can access this list by invoking getKeyFrames()
on your timeline.
It’s actually a TrackableObservableList
, which automatically updates the inner workings of the timeline when a change is made, so you can add key frames to this list directly. Internally, the Timeline has efficiencies built in so it won’t manually recompute everything if you add a key frame at either the beginning or the end.
Worth noting: However! When you add a key frame your animation will stop. You won’t be able to start it again by calling play()
, so instead you’ll have to restart your animation my calling timeline.playFromStart()
.
What is a Key Value?
The KeyValue is an object that stores a reference to a WriteableValue
alongside the numerical value. Key values are stored in KeyFrame
objects, which combine this information with the time at which the WriteableValue should equal this number.

The JavaFX animation framework then updates the WriteableValue
object over the course of an animation to smoothly move between these values.
Key values can also hold a reference to an Interpolator
object, which defines how the value should be interpolated between the previous key frame and this one. This can either be added during construction, or by invoking the setter method setInterpolator()
later.
KeyValue(WritableValue<T> target, T endValue) KeyValue(WritableValue<T> target, T endValue, Interpolator interpolator)
The final part of JavaFX’s animation package is the AnimationTimer
object.
AnimationTimer
Unlike the other animation classes, there are no default implementations inside the AnimationTimer
to update values or nodes inside a Scene. What it does give you access to is the value of the timestamp associated with every animation frame.
The AnimationTimer
is an object that listens to the underlying Animation Pulse of the JavaFX animation thread. Once per frame, it calls executable code, defined by the developer, with access to the timestamp of that frame.
This has several uses:
- As all animations in JavaFX are given the same timestamp this can be really useful for reporting the timestamp of linked events on separate animations.
- Access to the timestamp means you can calculate the duration of the previous frame. This is essential for creating game loops, or other animations where smoothness is essential.
The AnimationTimer
is an abstract class with one abstract method. So, to create an AnimationTimer
, we have to override the abstract method. That’s the method that will get called every frame, so any executable code you want to rungoes here.
AnimationTimer timer = new AnimationTimer() { @Override public void handle(long now) { pollUserInput(); updatePlayerPositions(); updateCanvas(); } }; timer.start();
Warning! You can start the animation timer easily enough by invoking start()
, however the stop()
method is a little misleading. If you call stop(), the animation timer will stop listening for the pulse, and therefore stop executing your code. However, the timestamps don’t stop!
So, when you start your timer again, if you’re using the timestamps to calculate anything, it’ll be like your animation never stopped!
With a little modification, you can extend the AnimationTimer
to add in some proper pausing, stopping and restarting functionality. If you’re interested take a look at my article here, which runs you through how alongside the modified AnimationTimer
code.
Conclusions
There are three types of objects in the JavaFX animation portfolio: the Transition
classes, Timeline
objects and animation timers. Each of these is designed to handle specific use cases, which help developers create beautiful animations.
Transition classes are specifically designed to animate nodes within a scene graph. They specifically alter the appearance and transforms of the nodes. This can be limiting, but generally provides a handful of really useful classes for creating simple animations.
Timelines – the most complex JavaFX animation class, is capable of animating a much broader range of objects. I’ve used them to create complex animations of nodes, but you can also use them to animate any WriteableValue – essentially any JavaFX property.
Finally, the AnimationTimer
class is the simplest, but in some ways the most versatile. By giving developers access to the value of the timestamp for each frame, we can create beautiful, smoothed animations and even game loops.