Skip to main content

Compiling and Distributing

FlixelGDX supports three platforms out of the box: desktop (Windows, macOS, Linux via LWJGL3), web (via TeaVM), and Android. Each platform has its own Gradle subproject and launcher class. This page covers how to run your game during development and how to package and distribute it for each target.

Project generator

It's highly advised you use the project generator. It automatically generates all of the configurations, plugins and modules of each platform for you. Refer to the project's generated README.md file for more info on how to set everything up with your project.


Desktop

Running during development

The :lwjgl3:run Gradle task starts the game with the working directory set to assets/ so file paths behave identically in the IDE and in a packaged build:

./gradlew :lwjgl3:run

Most IDEs (IntelliJ IDEA, VS Code with the Gradle extension) also list this task in their Gradle tool window.


Packaging with Construo

Construo is a Gradle plugin that bundles a trimmed JVM runtime with your game into a self-contained folder. Users do not need to install Java. The generated lwjgl3/build.gradle already includes four targets:

Gradle taskOutput
:lwjgl3:packageLinuxX64Linux x86_64 folder
:lwjgl3:packageWinX64Windows x86_64 folder
:lwjgl3:packageMacOsX64macOS Intel folder
:lwjgl3:packageMacOsAarch64macOS Apple Silicon folder

All packages land in dist/ at the root of the project. Zip a folder and upload it directly to itch.io, GitHub Releases, or any other platform:

./gradlew :lwjgl3:packageLinuxX64
Cross-compilation

Construo downloads a Temurin JDK for the target OS during the first build. You can produce a Windows package from macOS, or vice versa, with no extra tools installed.


Executable fat JAR

For simple distribution without an embedded runtime, build the self-contained fat JAR:

./gradlew :lwjgl3:jar

The JAR is written to lwjgl3/build/libs/<gameid>-<version>.jar. It bundles all dependencies and assets at the classpath root. Running it requires Java 17 or later on the user's machine:

java -jar mygame-1.0.jar
JARs and macOS

On macOS, double-clicking a .jar only works when the system JRE is installed and associated with .jar files. The Construo runtime package is a friendlier distribution format for most users.


GraalVM native image (advanced)

GraalVM native image compiles the game ahead-of-time into a single native binary with no JVM dependency. The binary starts instantly and uses less memory, but the compilation step takes a few minutes and requires a GraalVM JDK.

1. Install a GraalVM JDK

Download GraalVM for JDK 21 from graalvm.io. On GraalVM 21 and later, native-image is bundled in the distribution and does not need a separate install step. When downloading manually, pick the GraalVM Community (free) or Oracle GraalVM build for JDK 21.

IntelliJ IDEA also ships a GraalVM vendor option in its JDK downloader (File > Project Structure > SDKs > Add JDK from disk, or use the IDE's JDK download wizard).

2. Enable the flag in gradle.properties

enableGraalNative=true

The lwjgl3 module already has the GraalVM build tools plugin applied. This flag activates the nativeimage.gradle script that configures it.

3. Point Gradle at the GraalVM JDK

Set GRAALVM_HOME (or JAVA_HOME) to the GraalVM installation directory:

export GRAALVM_HOME=/path/to/graalvm-jdk-21

Alternatively, set the project JDK in your IDE to the GraalVM installation.

4. Compile

./gradlew :lwjgl3:nativeCompile

The binary lands in lwjgl3/build/native/nativeCompile/. Compilation typically takes two to five minutes on a modern machine.

Libraries that use JNI or reflection

If you add a library that relies on JNI or reflection, run the config generator after enabling enableGraalNative=true:

./gradlew :lwjgl3:generateNativeConfig

This launches your game with the GraalVM tracing agent attached. Play through every feature you want to cover, then close the window. The task writes the recorded configuration to src/main/resources/META-INF/native-image/ automatically so the next nativeCompile picks it up.


Web (TeaVM)

FlixelGDX compiles to the browser via TeaVM, a Java-to-JavaScript transpiler. Your game runs and compiles seamlessly in any modern browser via the framework's custom plugin.

Running during development

./gradlew :teavm:run

This starts a local development server, with the URL opening automatically. If it doesn't open, you can do it manually by clicking the URL printed in the terminal (typically http://localhost:8080) to play the game in your browser.

Build output

Every :teavm:run writes static files to build/generated/teavm/:

build/generated/teavm/
assets/ -- copied from assets/
js/ -- the compiled JavaScript bundle
scripts/ -- extracted native library scripts
index.html -- the game entry page

The JavaScript bundle name comes from the gameId property in gradle.properties.

Hosting a web game

Copy the contents of build/generated/teavm/ to any static web host:

  • itch.io — zip the output and upload it as an HTML game. Select "This file will be played in the browser" in the upload settings.
  • newgrounds.com — zip the output, navigate to "Upload Your Creations", choose "Game", enter your game's details and drag your .zip output into the uploader slot.
  • GitHub Pages — push the folder to a gh-pages branch and enable Pages in your repository settings.
CORS headers for multi-threaded features

Some browsers require Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers to enable features like shared memory. itch.io sets these automatically. If you are self-hosting and the game fails to start, configure your server to send both headers on every response.


Android

Android is the most involved target because it requires the Android Gradle plugin and its own toolchain.

Android Studio is strongly recommended. It bundles the Android SDK and NDK, manages emulators, installs APKs to physical devices over USB, and provides a guided release signing wizard. Using the command line alone is possible but tedious compared to the IDE workflow.


Testing on an emulator

  1. Open Android Studio and open your project root.
  2. Open the Device Manager (View > Tool Windows > Device Manager).
  3. Click Create Virtual Device. A Pixel phone with the API 35 (Android 15) system image is a sensible default. Download the system image if prompted.
  4. Start the emulator by clicking the green play button next to the device entry.
  5. Select the android run configuration from the run toolbar and click Run (the green play arrow). Android Studio builds the debug APK, installs it on the emulator, and starts the activity.
GPU acceleration on the emulator

The software renderer is slow. In the AVD settings set Graphics to Hardware - GLES 2.0 for a large speed improvement. If the emulator is still too slow to be useful, test on a physical device instead.


Testing on a physical device

  1. On your Android device, go to Settings > About Phone and tap Build number seven times to unlock Developer Options.
  2. Go to Settings > Developer Options and enable USB Debugging.
  3. Connect the device to your computer with a USB cable. When prompted on the device, tap Allow to authorize the RSA fingerprint.
  4. In Android Studio, the device appears in the run target dropdown. Select it and click Run.

You can also deploy and launch from Gradle once USB debugging is authorized:

./gradlew :android:installDebug
adb shell am start -n com.example.mygame/.android.MyGameAndroidLauncher

Generating a release build

Debug builds are fine for development, but distributing publicly or uploading to the Play Store requires a signed release build.

Google Play App Signing and the upload key

Google Play uses a two-key model for all new apps. Google holds the app signing key (the key actually used to sign APKs delivered to users) and you sign your bundle locally with an upload key before sending it to Play Console. Google verifies the upload key, strips it, and re-signs with the app signing key before distribution.

This means:

  • Losing your upload key is recoverable — you can request an upload key reset from Google Play support.
  • The app signing key is managed by Google and is never exposed to you.
Never commit your upload key or its passwords to version control.

Create an upload key in Android Studio:

  1. Go to Build > Generate Signed Bundle / APK.
  2. Choose Android App Bundle (AAB) (required for Play Store).
  3. Click Create new keystore and fill in the alias, passwords, and validity period. Save the .jks file outside of your project directory.
  4. Complete the wizard. The signed AAB appears in android/release/.

Enable Play App Signing in Play Console:

The first time you upload an AAB to a new app in Play Console, you will be prompted to opt into Play App Signing. Accept the prompt — Google generates and stores the app signing key for you. All subsequent uploads use your upload key; Google handles the rest.

APK vs AAB

FormatBest for
APKDirect download, sideloading, Amazon Appstore
AABGoogle Play Store (required for new apps since 2021)

The Play Store re-signs and optimizes AABs per device architecture, which reduces download sizes. For any distribution path outside the Play Store, an APK is simpler.


Publishing to Google Play

  1. Create a Google Play Console account at play.google.com/console.
  2. Create a new app and fill in the store listing (description, screenshots, content rating).
  3. Upload your signed AAB in the Production release track. The Internal testing track is useful for sharing the build with a small group before going public.
  4. Submit for review. Google typically reviews new apps within a few hours to a few days.
Choosing a minimum SDK version

minSdk 24 in the example targets Android 7.0, which covers over 97% of active devices as of 2026. Lowering it increases compatibility but may require workarounds for older Android APIs. Raising it simplifies your code at the cost of excluding older hardware. Choose based on your target audience.