This guide walks you through a complete libGDX setup in Java: selecting the right JDK 17 runtime, running the gdx-setup tool to generate a Gradle-based project, understanding the multi-module structure that tool produces, and writing the minimum ApplicationListener code to render your first screen.

If you’re wondering how to set up libGDX from scratch in Java without hitting the configuration pitfalls that most tutorials skip, you’re in the right place. We’ve structured this so you go from zero environment to a running desktop window in one sitting.

Quick Answer: Setting up libGDX from JDK installation to a running first screen takes approximately 30 to 45 minutes for a developer already familiar with Java.

What libGDX Is and Why JDK Setup Matters

libGDX is a cross-platform Java game development framework that targets desktop (Windows, macOS, Linux), Android, iOS, and HTML5 from a single shared codebase. It wraps OpenGL ES (a graphics API for embedded systems, also available on desktop via LWJGL) and gives you high-level abstractions like SpriteBatch, Texture, and Sound without hiding the underlying hardware access you’ll need for performance-sensitive game logic.

libGDX runs on the JVM. The JVM (Java Virtual Machine) is the runtime that executes compiled Java bytecode. The JDK (Java Development Kit) includes the compiler, the JVM, and all the tools you need to build and run Java programs. The JRE (Java Runtime Environment) is a subset that only runs already-compiled programs. You need the full JDK installed, not just the JRE, because the libGDX build process compiles your code. Getting this wrong is the single most common first-hour mistake we see.

JDK version selection affects which libGDX features work and whether your Gradle build succeeds at all. The current stable libGDX releases (1.11.x and 1.12.x) require JDK 11 as a minimum, but we recommend JDK 17 LTS for new projects. JDK 17 is the current long-term support release with the widest tooling compatibility across IntelliJ IDEA, Android Studio, and Gradle 8.x. The examples in this guide use libGDX 1.12.1 and JDK 17.

Which JDK Version Does libGDX Require?

libGDX 1.12.x requires JDK 11 or later. We recommend JDK 17 LTS for new projects because it pairs well with Gradle 8.x, receives long-term security patches, and introduces sealed classes and records that you may want in your game logic code. JDK 21 LTS also works, but some Gradle plugins lag behind on 21 compatibility, so verify your specific plugin versions before committing to it.

libGDX VersionJDK 11JDK 17JDK 21Gradle
1.11.x✓✓~7.x
1.12.x✓✓✓8.x

After installing JDK 17 from Adoptium or the Oracle download page, verify the installation from your terminal before doing anything else:

java -version
javac -version

Both commands should report version 17.x. If java -version shows 17 but javac -version shows a different version or fails, your JAVA_HOME environment variable points to a JRE installation rather than a full JDK. Set JAVA_HOME to the JDK root directory, for example /usr/lib/jvm/temurin-17 on Linux or C:\Program Files\Eclipse Adoptium\jdk-17.0.x on Windows, then restart your terminal.

Downloading and Running the libGDX Setup Tool

The libGDX project generator is a JAR file called gdx-setup.jar. Download it from the official libGDX GitHub releases page. The file name follows the pattern gdx-setup.jar and is attached as a release asset to each tagged version. Get the asset from the 1.12.1 release tag to match the examples in this guide.

Once downloaded, run it from your terminal. Do not try to double-click the JAR on systems where JAR files don’t have a default handler registered:

java -jar gdx-setup.jar

This opens a GUI. The setup tool UI does change between releases, so if your screen looks different from any screenshots you’ve seen, check the official libGDX changelog. The fields that matter are consistent across recent versions:

  • Name: Your project name, for example MyGame. This becomes the root Gradle project name.
  • Package: Your Java package, for example com.yourname.mygame. Follow standard reverse-domain convention.
  • Game Class: The name of your main ApplicationListener class, for example MyGame.
  • Destination: The directory where the project will be generated.
  • Android SDK: Leave blank if you’re targeting desktop only for now.
  • Sub-projects: Check Core and Desktop. Uncheck Android, iOS, and HTML to keep the initial project simple.

Click Generate. The tool creates the project directory and writes the Gradle build files. It does not download libGDX dependencies at this point. That happens during the first Gradle sync in your IDE, which is why the first import takes several minutes on a fresh machine.

What Gradle Downloads During the First Sync

Gradle resolves dependencies declared in the generated build.gradle files by fetching them from Maven Central and the libGDX snapshot repository. For a Core plus Desktop project, this includes the libGDX core JAR, the LWJGL3 backend JARs (LWJGL3 is the desktop OpenGL binding libGDX uses), and native library JARs for Windows, macOS, and Linux. These native JARs contain compiled C++ code that LWJGL3 loads at runtime to talk to your GPU.

Gradle caches all downloaded files in ~/.gradle/caches. The first sync on a clean machine downloads roughly 150 to 200 MB. Subsequent syncs are fast because Gradle reads from cache. If you’re behind a corporate proxy or firewall, dependency resolution will fail silently or with a connection timeout error. Add proxy settings to your gradle.properties file:

systemProp.http.proxyHost=your.proxy.host
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=your.proxy.host
systemProp.https.proxyPort=8080

Understanding the Generated Project Structure

The generated project is a Gradle multi-project build. Understanding which files to edit and which to leave alone saves you from breaking the build configuration in the first hour.

The top-level directory contains:

  • settings.gradle: Declares which sub-projects Gradle includes. Don’t edit this unless you’re adding or removing platform targets.
  • build.gradle: The root build file with shared configuration. libGDX version numbers live here.
  • gradlew / gradlew.bat: The Gradle wrapper scripts. Always use these instead of a globally installed Gradle to ensure version consistency.
  • gradle/wrapper/gradle-wrapper.properties: Specifies the exact Gradle version the project uses.

Module Breakdown: What Each Directory Does

The generated sub-project directories each serve a distinct role:

  • core: Contains all platform-agnostic game logic. Your ApplicationListener implementation lives here. This is where you write game code every day. About 90% of your code will be written in this module.
  • desktop: Contains the DesktopLauncher.java class that creates the application window and starts the JVM with the correct backend. You edit this only to change window settings like title, width, and height.
  • android: Android-specific launcher and manifest. You can ignore this entirely if you’re building for desktop only.
  • ios: iOS-specific launcher using RoboVM. Skip this unless you’re targeting Apple platforms.
  • html: GWT-based web target. Complex to configure and slow to iterate on. Leave it alone until you need web deployment.

The assets folder lives inside the core module directory by default. This is where you place textures, sounds, and fonts. The desktop launcher is configured to look for assets relative to its working directory, which Gradle sets to the assets folder during a desktop run task.

Importing the Project into IntelliJ IDEA

IntelliJ IDEA handles Gradle projects better than Eclipse in our experience, and it’s the IDE we recommend for libGDX development. Android Studio also works and is worth considering if you plan to target Android later.

  1. Open IntelliJ IDEA and select File > Open.
  2. Navigate to the generated project directory and select the root build.gradle file.
  3. IntelliJ will ask whether to open the file or the project. Choose Open as Project.
  4. In the Gradle import dialog, confirm that the Gradle JVM is set to JDK 17. This is the setting most people miss.
  5. Click OK and wait for the Gradle sync to complete. Watch the Build output panel at the bottom.

A successful sync ends with BUILD SUCCESSFUL in the Build output and shows the core and desktop modules in the Project panel on the left. A failed sync shows BUILD FAILED with a stack trace. The most common cause is a JDK version mismatch between the IDE project SDK and the toolchain version declared in build.gradle. Fix it by going to File > Project Structure > Project SDK and selecting JDK 17.

Writing and Running Your First Screen

The ApplicationListener interface is the entry point for all libGDX game logic. libGDX calls its methods at specific points in the application lifecycle. Your generated game class in the core module already implements this interface. Open it and replace the generated content with this minimum implementation:

package com.yourname.mygame;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;

public class MyGame implements ApplicationListener {

    @Override
    public void create() {
        // Called once when the application starts.
        // Load assets and initialize game objects here.
    }

    @Override
    public void render() {
        // Called every frame. Clear the screen, then draw.
        Gdx.gl.glClearColor(0.39f, 0.58f, 0.93f, 1f); // cornflower blue
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    }

    @Override
    public void resize(int width, int height) {
        // Called when the window is resized. Update cameras here.
    }

    @Override
    public void pause() {
        // Called when the app loses focus (minimized, phone call, etc.).
    }

    @Override
    public void resume() {
        // Called when the app regains focus.
    }

    @Override
    public void dispose() {
        // Called when the application shuts down.
        // Release all resources loaded in create() here.
    }
}

Understanding the Lifecycle Methods

The order libGDX calls these methods matters for resource management. create() fires once at startup. render() fires every frame at the rate your display allows. dispose() fires when the window closes. Load textures and sounds in create(), draw them in render(), and call dispose() on every loaded resource in dispose(). Skip the dispose step and you’ll leak native memory, which doesn’t get cleaned up by the JVM’s garbage collector because native resources sit outside the managed heap.

Running the Desktop Launcher

Open DesktopLauncher.java in the desktop module. It should look like this:

package com.yourname.mygame.desktop;

import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.yourname.mygame.MyGame;

public class DesktopLauncher {
    public static void main(String[] args) {
        Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
        config.setForegroundFPS(60);
        config.setTitle("MyGame");
        config.setWindowedMode(800, 600);
        new Lwjgl3Application(new MyGame(), config);
    }
}

Right-click DesktopLauncher in IntelliJ and select Run ‘DesktopLauncher.main()’. A window should appear with a solid cornflower-blue background. If it does, your libGDX setup is working correctly.

Common Setup Mistakes and How to Fix Them

Why Does My Gradle Sync Fail After Generating a libGDX Project?

The most frequent cause is a JDK version mismatch. Symptom: the Build output shows an error like Unsupported class file major version 61 or Could not resolve com.badlogicgames.gdx:gdx:1.12.1. Root cause: either the Gradle JVM in IntelliJ is set to a JDK older than 11, or your network is blocking Maven Central. Fix: go to Settings > Build, Execution, Deployment > Build Tools > Gradle and set the Gradle JVM to JDK 17. For network issues, add proxy settings to gradle.properties as shown earlier.

Why Does the Desktop Launcher Crash With a Native Library Error?

Symptom: the application starts then immediately throws an UnsatisfiedLinkError referencing an LWJGL native. Root cause: Gradle didn’t download the native JARs for your operating system, or they’re cached in a corrupted state. Fix: delete the ~/.gradle/caches directory and re-sync. If the error persists, check that your build.gradle includes the natives configuration for your platform:

natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"

Why Can’t the Desktop Launcher Find the Assets Folder?

Symptom: a FileNotFoundException when loading a texture or sound, even though the file exists. Root cause: the working directory for the run configuration isn’t set to the assets folder. Fix: in IntelliJ, edit the run configuration for DesktopLauncher and set the Working directory to $MODULE_WORKING_DIR$/../../assets or the absolute path to your assets folder. Alternatively, run the desktop task through Gradle with ./gradlew desktop:run, which sets the working directory correctly by default.

What Happens if JAVA_HOME Points to a JRE?

You’ll see this error when running gdx-setup.jar or during the Gradle build:

error: invalid source release: 17
or
The supplied javaHome seems to be invalid. I cannot find the java executable.

The fix is to set JAVA_HOME to the JDK root, not the JRE subdirectory. On Windows, the JDK root is typically C:\Program Files\Eclipse Adoptium\jdk-17.x.x-hotspot. On macOS with Homebrew, run /usr/libexec/java_home -v 17 to get the correct path. Then set the variable and restart your terminal session.

What to Build Next After Your First Screen

A solid-color screen confirms your libGDX setup works. The immediate next step is drawing an image. SpriteBatch and Texture are the two classes you’ll use for almost everything visual in a 2D libGDX game. The pattern is always the same: load in create(), draw in render(), dispose in dispose().

private SpriteBatch batch;
private Texture img;

@Override
public void create() {
    batch = new SpriteBatch();
    img = new Texture("badlogic.jpg"); // place this file in your assets folder
}

@Override
public void render() {
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.begin();
    batch.draw(img, 0, 0);
    batch.end();
}

@Override
public void dispose() {
    batch.dispose();
    img.dispose();
}

This pattern, load, draw, dispose, is the foundation of all libGDX asset management. Get it wrong and you’ll see memory grow over time as textures accumulate without being released. The libGDX official simple game tutorial builds directly on this pattern and is the logical next step after you’ve confirmed your project runs. It introduces InputProcessor for keyboard and mouse handling and shows how to move sprites based on user input.

From there, the libGDX API covers camera management with OrthographicCamera, physics with Box2D integration, and audio with the Sound and Music interfaces. The core module is where all of that code lives, and the mental model you’ve built about the project structure in this guide carries forward directly into every feature you add.

Download our free libGDX Project Setup Checklist, a one-page reference covering JDK version compatibility, required Gradle settings, and the five most common setup errors with fixes. And if you want to continue building on this project, the next article in this series covers the libGDX game loop, input handling, and sprite rendering in depth. Subscribe to the javalimit.com newsletter to get it in your inbox when it publishes.

Founder and Chief Editor at  |  + posts

Jodie Bird is the founder and principal author of the Java Limit website, a dedicated platform for sharing insights, tips, and solutions related to Java and software development. With years of experience in the field, Jodie leads a team of seasoned developers who document their collective knowledge through the Java Limit journal.