Under the hood

How fx2048 is built and packaged.

fx2048 is a small modular Java app with a JavaFX UI, a custom desktop runtime image, native installers, optional JPro browser deployment, and a deliberately tight runtime footprint. Its recent modernization was planned, implemented, debugged, and documented with GitHub Copilot.

Project at a glance

Current source and runtime facts from the repository and latest published release.

2,468 Main Java lines
24 Java source files
32 MB Configured max heap
~43 MB Typical installer size

Built with GitHub Copilot

GitHub Copilot helped drive the current version of fx2048 end to end, from gameplay polish to signed releases and this website.

Game and UI improvements

Copilot helped iterate on the desktop experience, including controls, overlays, status information, layout fixes, and visual refinements.

Release automation

Copilot assisted with Maven, GitHub Actions, jlink, jpackage, platform artifacts, version bumps, changelog updates, and release workflow fixes.

macOS signing diagnostics

Copilot helped diagnose and fix hardened runtime, JVM JIT, JavaFX native library validation, DMG signing, notarization, and Gatekeeper issues.

Documentation and agent skills

Copilot authored the GitHub Pages site and captured the macOS JavaFX signing lessons as a reusable agent skill for future projects.

Technology stack

The app is intentionally simple: Java modules, JavaFX controls and animation APIs, Maven automation, platform-native packages, and an optional JPro browser runtime.

Application runtime

Java 25, JavaFX 25.0.3, and the JPMS module fxgame. The launcher enters io.fxgame.game2048.AppLauncher.

UI and animation

JavaFX scene graph, CSS styling, keyboard and swipe input, tile move/merge animations, overlays, score effects, and a lightweight status bar.

Game model

The game rules live in Java classes for grid traversal, tile state, move/merge flow, scoring, game-over checks, persistence, and records.

Build and packaging

Maven builds the app, jlink creates a custom runtime, and jpackage produces DMG, MSI, and DEB installers.

Browser deployment

JPro can run the JavaFX application on a server and stream the UI to a browser, using a separate Maven profile from the desktop packaging path.

Libraries and tools

The dependency list is short, which keeps the desktop app easy to audit and package.

Category Technology
Language and UI framework Java 25 with JavaFX 25.0.3 modules: javafx-base, javafx-graphics, and javafx-controls. JavaFX source lives in the OpenJDK jfx repository, while JavaFX binaries are published by Gluon at openjfx.io.
Tests JUnit Jupiter 5.11.4 through Maven Surefire 3.5.3.
Build Maven wrapper, Maven Compiler Plugin 3.15.0, JavaFX Maven Plugin 0.0.8, Maven JAR Plugin 3.5.0, jpackage Maven Plugin 1.7.4, and JPro Maven Plugin 2026.1.1.
Packaging jlink strips the runtime image, and jpackage creates native installers from that runtime.
Browser runtime JPro serves the JavaFX application over HTTP from the Maven jpro profile, with platform profiles selecting JPro-compatible JavaFX native artifacts.

JPro browser deployment model

JPro is a separate runtime path for trying the JavaFX game in a browser without rewriting the UI in HTML and JavaScript.

Server-side JavaFX

The JavaFX application runs in a JVM on the server. JPro exposes it at http://localhost:8080/index.html during local development and streams the UI to the browser.

Different entry point

The desktop launcher uses io.fxgame.game2048.AppLauncher, but JPro is configured with io.fxgame.game2048.Game2048 because JPro needs the class that directly extends javafx.application.Application.

Maven profiles

Run JPro with the shared jpro profile plus one platform profile: jpro-macos-aarch64, jpro-linux-amd64, or jpro-linux-aarch64. Windows is not part of this JPro setup.

Lifecycle

jpro:run starts an attached development server. jpro:restart starts a background server and writes its process id to RUNNING_PID.

Runtime profile

fx2048 is tuned for a small desktop-game footprint rather than server-style throughput.

Setting or behavior Value
Java heap -Xms8m, -Xmx32m, -XX:MinHeapFreeRatio=10, and -XX:MaxHeapFreeRatio=20
Garbage collector -XX:+UseG1GC with -XX:G1PeriodicGCInterval=10000 and -XX:+G1PeriodicGCInvokesConcurrent
Native footprint limits -Xss512k thread stacks, -XX:ReservedCodeCacheSize=12m, -XX:CICompilerCount=2, and -XX:TieredStopAtLevel=1.
Status bar Shows live CPU, memory, and garbage-collection count while the game is running.
Persistence Saves local settings, session state, elapsed time, score, and best-score records under the user's home directory.

Memory use across 2048 implementations

A 2048 board is tiny; most memory comes from the runtime that hosts it. These local snapshots compare three ways to ship the same kind of game.

Takeaway

fx2048's packaged JavaFX build measured about 189 MB on macOS: larger than a small Cocoa/WebKit wrapper, but lower than the same kind of game running inside a full Chrome tab.

The 32 MB Java heap cap is a JVM tuning boundary, not total process memory. Native runtime, graphics, code cache, stacks, and libraries sit outside the heap.

Chrome Task Manager screenshot showing the play2048.co tab consuming 282 MB of memory.
Chrome Task Manager showing the play2048.co tab at 282 MB, the browser baseline used below.
Implementation Observed memory Runtime What it means
fx2048 packaged app ~189 MB physical footprint JavaFX app with a custom jlink runtime and tuned JVM options. A focused desktop JVM profile, including more than just the Java heap.
2048 Game.app ~117 MiB RSS Cocoa/AppKit shell with WebKit hosting bundled HTML, CSS, and JavaScript assets. A compact WebKit wrapper, not Electron, Java, Node, or a fully custom native renderer.
play2048.co in Chrome 282 MB The JavaScript game hosted inside a full Chrome browser tab. The largest baseline here because the full browser runtime comes with the tab.

Runtime footprint snapshot

2048 Game.app RSS ~117 MiB
fx2048 app footprint ~189 MB
play2048.co Chrome tab 282 MB

The chart is normalized to the Chrome tab's 282 MB measurement. RSS, physical footprint, and Chrome Task Manager are different measurements, so treat this as a practical comparison rather than a lab benchmark. The 32 MB Java heap cap is not plotted because it is a tuning limit, not process memory.

Code and package size

The page keeps the source stats static and reads current release package sizes from GitHub when available.

Main Java 2,468 lines across 21 files
Tests 223 Java lines across 3 files
Styling 392 lines of JavaFX CSS
Tracked source/config total 3,084 lines
Latest release packages Loading package sizes...

Release pipeline

GitHub Actions builds platform installers and publishes signed releases.

macOS

Builds an Apple Silicon DMG, signs the app with hardened runtime entitlements, signs the disk image, notarizes it with Apple, staples it, and validates Gatekeeper.

Windows and Linux

Builds a Windows x64 MSI and Linux amd64/arm64 DEB packages, each bundling the custom runtime needed by the game.