Often, you don’t want to settle for the OS-dependant background most JavaFX applications default to. Luckily, JavaFX is incredibly flexible in the way it allows developers to set their backgrounds as images, colors, gradients, and patterns.

A background fill can be used to increase the visual appeal of an app
Without background (left), and with (right)

Because of the way JavaFX layers its applications from the Stage upwards, there are multiple ways to set backgrounds. In fact, multiple Background fills and colors can be blended using transparency, creating a really rich user experience.

Background fills and colors are rendered within the scene graph
All Region objects in JavaFX can have multiple background images and fills

The simplest way to set the JavaFX Scene background color or image is by invoking the Scene‘s setFill() method, which can accept a color, gradient or image pattern. A more flexible way to set the background of a scene is to set the root node’s background, which can accept multiple images and fills. This can be done in both Java code and by using CSS.

As a special case, you can also set the Scene to be completely transparent. In that case, you’ll need to set the Stage as transparent, because by default this is filled with an off-white color:

By default the Stage has an off-white fill. This can be modified using the initStyle property, although StageStyle.TRANSPARENT will also remove windowing features.

Over the course of this article, I’ll go through each in depth.

What you’ll get from this article:

One of JavaFX’s greatest strengths (and weaknesses…) is the number of ways it lets you do things.

In the case of the app background, there are two ways – using the Scene object and the root node of the scene graph (that’s all the nodes in the scene).

This article goes through every way to do it, from the simplest (setting it on the Scene itself) to the most flexible (setting it on the root node using CSS):

The background of the scene can either be set using the Scene object, or the root node

When to use each: If you’re wondering when to use each of these options, here’s a quick idea:

ElementCapabilityBenefitsLimitations
SceneSingle color, gradient or image pattern.Set in a single place when using dynamic scene graphCan only be set in Java code (not CSS)
Root NodeMultiple images, fills, gradients and patternsComplex, rich backgrounds created on a single node (region)Can only be used if the root node is a Region

(Cannot be set on a Group)

CSS Vs Java Code: You can set backgrounds on the root node (and in fact any Region!) using both CSS and Java code. I massively favour doing it using CSS, because it’s more concise, you can automate it through attributes in the FXML, and there are a lot more utility values you can dip into. However, there are instructions and comparisons on how to do both in code examples below.

Overall, JavaFX provides some flexible and robust ways to fill your scenes with content that make for a rich user experience without needing to cramp the scene graph with ImageView and Canvas objects.

Here’s an example of an animated background created using only one VBox as a root node, and 6 layered background images:

Lines of code used:

Java:14
CSS:4
FXML:1
Total:19

The code for this is right here.

Setting the background of the Scene object

The scene itself can have a background, which can be set by invoking setFill(Color color) on the scene object itself, passing in the Color object you want to use.

The setFill() method only accepts a single Paint object, which means you cannot use this method to create layered effects. Nor can you set images as a background, with the exception of the ImagePattern object, which extends Paint.

By default, all the layout panes above that make up your scene are transparent, so the absolute simplest way to set the background color of a Scene is to use this method.

That single Paint value can be a gradient, or image pattern, but you can only specify one.

Image
Linear Gradient
Radial Gradient

Here’s how to do each:

1. Color

You can still create visually appealing apps with just a single flat color. To do this, you can use either one of the default named colors such as Color.RED, or you can create a color using one of the supplied functions.

Available functions:
//Constructors available:
new Color color(double red, double green, double blue);

//Static functions available:
Color.rgb(int red, int green, int blue, double opacity);
Color.rgb(int red, int green, int blue);

Color.hsb(double hue, double saturation, double brightness, double opacity);
Color.hsb(double hue, double saturation, double brightness);

Color.web(String colorString, double opacity);
Color.web(String colorString);

// I won't include all the 'gray' static methods here...
Example:
scene.setFill(Color.web("#81c483"));

2. Linear Gradient

If you need a different look, you can also create a linear gradient by passing a LinearGradient object to the setFill() method. Unlike in CSS, there’s no defaults, so we need to specify all positional, cycling and color stop arguments in series.

Available functions:
//Constructors available:
new LinearGradient(
            double startX, double startY, double endX, double endY,
            boolean proportional,
            CycleMethod cycleMethod,
            Stop... stops);

new LinearGradient(
            double startX, double startY, double endX, double endY,
            boolean proportional,
            CycleMethod cycleMethod,
            List<Stop> stops);

//Static functions available:
LinearGradient.valueOf(String value); //accepts CSS String.
Example:
scene.setFill(new LinearGradient(
        0, 0, 1, 1, true,                      //sizing
        CycleMethod.NO_CYCLE,                  //cycling
        new Stop(0, Color.web("#81c483")),     //colors
        new Stop(1, Color.web("#fcc200")))
);

2. Radial Gradient

Or, you could create a radial gradient using the RadialGradient object. Again, this is not the most concise to define.

Available functions:
//Constructors available:
new RadialGradient(
            double focusAngle, double focusDistance,
            double centerX, double centerY,
            double radius, boolean proportional,
            CycleMethod cycleMethod,
            Stop... stops)

new RadialGradient(
            double focusAngle, double focusDistance,
            double centerX, double centerY,
            double radius, boolean proportional,
            CycleMethod cycleMethod,
            List<Stop> stops)

//Static functions available:
RagialGradient.valueOf(String value); //accepts CSS String.
Example:
scene.setFill(new RadialGradient(
        0, 0, 0, 0, 1, true,                  //sizing
        CycleMethod.NO_CYCLE,                 //cycling
        new Stop(0, Color.web("#81c483")),    //colors
        new Stop(1, Color.web("#fcc200")))
);

Limitations: There are limitations to setting the background of your app directly on the Scene object:

  • It must be set in Java code
  • It cannot be automated through the FXML file
  • It’s not very concise (no defaults like in CSS)

For that reason, I usually set my backgrounds on the root node of my scene. Because layouts naturally stretch to fit their container (the Scene), this usually has the same effect.

I choose to do it with CSS because I find it more concise and convenient, but I’ve shown both ways below so you can decide.

Setting Backgrounds of your root nodes:

A much more flexible approach to setting backgrounds is to apply them to the root node.

Because the JavaFX scene graph sits on top of the Scene and Stage objects, any fills or images applied to your root nodes will paint over the Scene anyway.

Root note image patterns and fills are a great way to set the scene background

For these situations, CSS is 100% my recommended method. It’s more succinct, there are a significant number of useful defaults you can make use of. That being said, you can achieve the same results using pure Java too.

It just takes a little longer.

Backgrounds can be set as both images and fills (colors and gradients). So this section will be split according to how you want to set the background.

Click the links to jump to the section you need, or just read all the way through for both.

Background Images
Background images patterns and fills

Warning: Not all root nodes will expand to fill the Scene by default. For example the GridPane, will by default not stretch to fit its container.

If you want more details on how each Layout works, check out the JavaFX Layouts guide I’ve put together here.

So, assuming you’re using a layout pane that expands to fill the Scene, we can use background images to paint across the entire window.

Setting Background Images in JavaFX

A really efficient way to set the background of the JavaFX scene is by using images. These can be set on any Region object, so if you set them on the root node (assuming it’s a Region not a Group), you can use them as the base layer of your Scene.

The Background image, or images, of the root node can be set using CSS using the -fx-background-image JavaFX CSS property. Alternatively, it can be set by invoking the setBackground() method on the root node in Java code, although this is generally less concise.

Any number of images can be used, and will be rendered in the order they’re specified, meaning you can layer up your images like this:

That’s a great way to add some drama to your scene without having to worry about multiple ImageViews to hold all those images.

Here’s what we’ll be working towards in this section. I’ll go over how to complete this using both CSS and a Java-only approach.

The root node of a scene can be set to have a series of images as a background
A Scene with one root node (here, a VBox), which has six layered background images.

Comparison of Java-only and CSS approaches:

Below are coded examples of how to create the above Scene using background images both with and without CSS.

In this case, using CSS saves a significant amount of code, utilising FXML to create the scene graph, and CSS defaults to create concise code.

Java only:

Lines of code used:

Java:37 (23)*
CSS:0
FXML:0
Total:37 (23)
*Including optimisations with utility method
Java & CSS:

Lines of code used:

Java:5*
CSS:11
FXML:1
Total:17
*Including the code to load the Stage

Setting Background Images using CSS

Setting background images using JavaFX’s CSS syntax is my suggested approach. It has the following benefits over hardcoding it in Java:

  1. The CSS syntax itself has a lot of useful default values not available in Java code, meaning it’s almost always more concise.
  2. The reference to the CSS file can be coded into your FXML document, so you don’t need to manually attach it.
  3. The behaviour of the scene graph is automatically delegated to the Controller (can also be set in FXML), allowing complex behaviour to be encapsulated away from other code

To achieve the effect above, we’ll need to create a View using FXML. Because it’s such a simple scene graph (one node) we strictly only need one line of code.

<VBox stylesheets="@../css/styles.css"
      xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"/>

Then, inside the CSS file styles.css, we can define the background images, as well as the size, position and repeat-behaviour of the images.

Really worth noting the CSS defaults we’re making the use of here: contain, bottom and repeat-x, all of which mean we can define our image behaviour in a really small amount of code.

.root{
    -fx-background-size: contain, contain, contain, contain, contain, contain;
    -fx-background-position: bottom, bottom, bottom, bottom, bottom, bottom;
    -fx-background-repeat: repeat-x, repeat-x, repeat-x, repeat-x, repeat-x, repeat-x;

    -fx-background-image:
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_06_1920x1080.png"),
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_05_1920x1080.png"),
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_04_1920x1080.png"),
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_03_1920x1080.png"),
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_02_1920x1080.png"),
        url("https://edencoding.com/wp-content/uploads/2021/03/layer_01_1920x1080.png");
}

Images are specified as a URL (see my JavaFX CSS guide here for more information). These can be both local and online files – I’ve made these available online so you can just copy and paste that CSS code without worrying where they are in your project structure.

On top of that, because the code is so concise, we can make the most of it to add functionality.

Animating Backgrounds using CSS:

One additional benefit of coding this with CSS is the ease with which you can individually alter the background position using in-line styles for the -fx-background-position JavaFX CSS property.

To do this, we’ll need to add an fx:id attribute and a controller to the FXML file:

<VBox xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"
      fx:id="content"
      fx:controller="com.edencoding.controllers.ExampleController"
      stylesheets="@../css/styles.css">
</VBox>

Then, by creating a Controller using just a few lines of code, we can shift each background image by different amounts each frame.

public class ExampleController {

    public Region content;

    public void initialize() {
        DoubleProperty xPosition = new SimpleDoubleProperty(0);
        xPosition.addListener((observable, oldValue, newValue) -> setBackgroundPositions(content, xPosition.get()));

        Timeline timeline = new Timeline(
                new KeyFrame(Duration.ZERO, new KeyValue(xPosition, 0)),
                new KeyFrame(Duration.seconds(200), new KeyValue(xPosition, -15000))
        );
        timeline.play();

    }

    void setBackgroundPositions(Region region, double xPosition) {
        String style = "-fx-background-position: " +
                "left " + xPosition/6 + "px bottom," +
                "left " + xPosition/5 + "px bottom," +
                "left " + xPosition/4 + "px bottom," +
                "left " + xPosition/3 + "px bottom," +
                "left " + xPosition/2 + "px bottom," +
                "left " + xPosition + "px bottom;";

        region.setStyle(style);
    }
}

I think it creates a pretty interesting parallax background effect.

In this case, it’s an example I’ve taken right out of a JavaFX CSS reference I’m building. Specifically, the example in the entry for -fx-background-image.

Setting background images without CSS

Setting background images with Java code is a little less pretty, but because every CSS property is backed by a JavaFX object, we can still do it without many issues.

If we want to do this entirely in Java code – without a controller – we can simply create the VBox in our Application’s start() method just before we show the Stage.

Then, we can set the Background object of the content Region. In doing so, we’ll pass in a series of BackgroundImage objects. Each BackgroundImage will need to be defined in full, as there are no useful presets for Java-only code.

public class App extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        VBox content = new VBox();
        content.setBackground(new Background(
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_06_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ),
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_05_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ),
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_04_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ),
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_03_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ),
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_02_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ),
                new BackgroundImage(
                        new Image("https://edencoding.com/wp-content/uploads/2021/03/layer_01_1920x1080.png"),
                        BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                        new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                        new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true)
                ))
        );

        Scene scene = new Scene(content, 800, 450);
        stage.setScene(scene);
        stage.getIcons().add(new Image(getClass().getResourceAsStream("/com/edencoding/img/EdenCodingIcon.png")));
        stage.setTitle("Scene Background");
        stage.setScene(scene);
        stage.show();
    }
}

We can make this more concise by creating a utility function to deal with the fact that we’re unable to hook into the CSS defaults.

public class App extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        VBox content = new VBox();
        content.setBackground(new Background(
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_06_1920x1080.png"),
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_05_1920x1080.png"),
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_04_1920x1080.png"),
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_03_1920x1080.png"),
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_02_1920x1080.png"),
                        createImage("https://edencoding.com/wp-content/uploads/2021/03/layer_01_1920x1080.png")
                )
        );
        Scene scene = new Scene(content, 800, 450);
        stage.setScene(scene);
        stage.getIcons().add(new Image(getClass().getResourceAsStream("/com/edencoding/img/EdenCodingIcon.png")));
        stage.setTitle("Scene Background");
        stage.setScene(scene);
        stage.show();
    }

    private static BackgroundImage createImage(String url) {
        return new BackgroundImage(
                new Image(url),
                BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT,
                new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true),
                new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true));
    }
}

Again, I’ve included online references to the required pictures so you should be able to copy-and-paste the code. However, the files can also be stored locally in your project.

Just remember any style changes throughout your application will have to be changed at the Java source, rather than using a central stylesheet.

Overall, I think this is a less clean way to achieve our goal, but it will have the same effect.

Setting Background Colors and Fills in JavaFX

Often, you don’t want or need images as a background to your app. In fact, more frequently, you may just want to set a single gradient.

FXML can be combined with CSS and Java code to create a stylish new window

Background colors, gradients and image patterns of the root node can be set using the -fx-background-color JavaFX CSS property on the root element. Alternatively, it can be set by invoking the setBackground() method on the root node in Java code, although this is generally less concise.

Here’s what we’ll be working towards in this section. It actually includes three layers:

  1. A linear gradient of blue to pink (top to bottom)
  2. An image pattern (in this case providing stars)
  3. A radial gradient going from white to black, with transparency, to provide a vignette effect
The root node of a scene can be set to have a series of fills as a background

Comparison of Java-only and CSS approaches:

Below are coded examples of how to create the above Scene using background images both with and without CSS.

Again, using CSS saves a significant amount of code, utilising FXML to create the scene graph, and CSS defaults and utility functions to create concise code.

Java only:

Lines of code used:

Java:31*
CSS:0
FXML:0
Total:31
*Including the code to load the Stage
Java & CSS:

Lines of code used:

Java:5*
CSS:3 (1)**
FXML:1
Total:9 (7)
*Including the code to load the Stage
**Can be coded on one line. Separated for clarity in code blocks below.

Setting Background Colors and Fills using CSS

As with the background images, for this scene we’ll need the simplest View – a VBox as the root node, so it will stretch to the size of the container.

We can write this in a single line of XML code, although I will separate the stylesheets attribute onto a separate line so it’s easier to see.

<VBox xmlns="http://javafx.com/javafx/10.0.2-internal"
      stylesheets="@../css/styles.css"/>

Now, we can include the linear gradient, repeating image pattern and radial gradients we need in a really concise way within the CSS file.

Really worth noticing here the JavaFX CSS parser understands the default linear gradient proceeds from top to bottom, so we need only specify the colors. Also the repeating-image-pattern() function provides a one-parameter function to specify our stars!

.root{
    -fx-background-color:
            linear-gradient(#4568DC, #B06AB3),
            repeating-image-pattern("https://edencoding.com/resources/wp-content/uploads/2021/02/Stars_128.png"),
            radial-gradient(center 50% 50%, radius 50%, #FFFFFF33, #00000033);
}

The repeating-image-pattern() function accepts both local and online files, but I’ve specified it as an online file I’m hosting myself so you can just copy and paste that code.

Setting Background Colors and Fills Without CSS

If we’re creating this using only JavaFX code, we can create our VBox and add the background fills purely from within our Java code without the need for FXML.

public class App extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        VBox content = new VBox();
        content.setBackground(new Background(
                new BackgroundFill(
                        new LinearGradient(0, 0, 0, 1, true,
                                CycleMethod.NO_CYCLE,
                                new Stop(0, Color.web("#4568DC")),
                                new Stop(1, Color.web("#B06AB3"))
                        ), CornerRadii.EMPTY, Insets.EMPTY
                ),
                new BackgroundFill(
                        new ImagePattern(
                                new Image("https://edencoding.com/resources/wp-content/uploads/2021/02/Stars_128.png"),
                                0, 0, 128, 128, false
                        ), CornerRadii.EMPTY, Insets.EMPTY
                ),
                new BackgroundFill(
                        new RadialGradient(
                                0, 0, 0.5, 0.5, 0.5, true,
                                CycleMethod.NO_CYCLE,
                                new Stop(0, Color.web("#FFFFFF33")),
                                new Stop(1, Color.web("#00000033"))),
                        CornerRadii.EMPTY, Insets.EMPTY
                )
        ));

        Scene scene = new Scene(content, 800, 450);
        stage.setScene(scene);
        stage.getIcons().add(new Image(getClass().getResourceAsStream("/com/edencoding/img/EdenCodingIcon.png")));
        stage.setTitle("Scene Background");
        stage.setScene(scene);
        stage.show();
    }
}

The benefit of this is that we can create a really simple one-class program, which should run out of the box. However, none of the behaviour is outsourced to a Controller, and any style changes will need to be hard-code applied later.

Nonetheless, it does achieve the same effect.

Creating a transparent Scene in JavaFX

Sometimes, especially when you’re creating a completely custom view, you often want your Stage to be transparent.

When you’re aiming to create a fully transparent view in JavaFX, it’s absolutely worth remembering that the root node is rendered above two other layers of JavaFX window infrastructure – the Stage and the Scene object itself.

In JavaFX, the Scene can be made transparent by invoking setFill(Color.TRANSPARENT) on the Scene object, and initStyle(StageStyle.TRANSPARENT) on the underlying Stage object. Note that StageStyle.TRANSPARENT will also remove windowing features such as the minimise and close buttons.

In this example, we’ll create a really simple view using FXML:

<VBox xmlns="http://javafx.com/javafx/10.0.2-internal" 
      xmlns:fx="http://javafx.com/fxml/1" 
      alignment="CENTER" spacing="25.0" 
      stylesheets="@../css/styles.css" >
    <Label text="Transparent App Window" />
    <ImageView>
        <Image url="@../img/EdenCodingIcon.png" />
    </ImageView>
   <padding>
      <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
   </padding>
</VBox>

Here, the style sheet is not important, but without any styles, we won’t be able to see our layout. I’ve also added some basic layout attributes to make it a little more pleasant.

I’ll keep the CSS simple – a semi-transparent background color, and a border so we can se where the Scene begins and ends.

.content{
    -fx-border-color: black;
    -fx-border-width: 1px;
    -fx-background-color: #F3F9F322;
}

Finally, we can load our window in the Application start() method, which is also where we’ll set the Scene and Stage to be transparent.

public class App extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        //Create view
        FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/LabelExample.fxml"));
        Parent root = loader.load();

        //Create Scene and set transparent
        Scene scene = new Scene(root, 300, 250);
        scene.setFill(Color.TRANSPARENT);

        //Set Stage as transparent and launch
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

And here’s what it looks like:

A transparent window with application code visible underneath

Conclusions

JavaFX provides multiple ways to set the background of a Scene, and it can be accomplished in both Java code and by using CSS.

Because of the way JavaFX creates the scene graph, it is possible to set the background of the Scene by using either the Scene object itself or – usually – the root node of the Scene graph.

However, setting the background on the Scene object itself does have some limitations:

  1. It must be set in Java code
  2. It cannot be automated through the FXML file
  3. It’s not very concise (no defaults like in CSS)

For a more flexible approach, you can set the background of a layout pane that expands to fit the Scene. This way, multiple background images and fills can be rendered (and these can even be combined to create scene graphs with images and fills

Combining the approaches above to give the root node both image and paint backgrounds can produce an even more dramatic effect.