libgdx-headless-backend — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited libgdx-headless-backend (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Reference for running libGDX without a display, audio, or input — for servers, CI/testing, procedural generation, and headless simulation. Lives in the gdx-backend-headless module.
import com.badlogic.gdx.backends.headless.HeadlessApplication;
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
public class ServerLauncher {
public static void main(String[] args) {
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
config.updatesPerSecond = 20; // render() called 20 times/sec
new HeadlessApplication(new MyServerListener(), config);
}
}Dependency (Gradle):
implementation "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"The only important field is `updatesPerSecond` — an int controlling how often render() is called.
updatesPerSecond value | Behavior | Use case |
|---|---|---|
20 | render() called 20 times/sec | Game server at 20 tick rate |
60 (default) | render() called 60 times/sec | Wastes CPU on a server — always set explicitly |
0 | Never sleep between calls (run as fast as possible) | Batch processing, benchmarking |
Negative (e.g. -1) | render() is NOT called at all | One-shot tasks that do all work in create() |
CRITICAL — use `updatesPerSecond`, not the old float field:
// ✅ CORRECT — updatesPerSecond is an int
config.updatesPerSecond = 20;
// ❌ WRONG — renderInterval was removed; this will not compile on 1.9.14+
// config.renderInterval = 1 / 20f; // DO NOT USEFor servers: Always set updatesPerSecond to match your desired tick rate. The default (60) runs at 60fps which wastes CPU on a headless server.
For one-shot tasks (generate data then exit): Do all work in create() and call Gdx.app.exit(). Set updatesPerSecond = -1 so render() is never called, or leave it at default — if your create() calls exit(), it won't matter.
These APIs are fully functional without OpenGL:
create(), render(), resize(), pause(), resume(), dispose() all fire normallyGdx.files.internal(), .local(), .external(), .absolute() all workVector2, Vector3, Matrix4, Quaternion, MathUtils, Intersector, Rectangle, Circle, PolygonArray, ObjectMap, IntMap, IntArray, Pool, etc.Json class for reading/writing JSONGdx.net for HTTP requestsGdx.app.getPreferences("name") for key-value storageGdx.app.postRunnable(), Gdx.app.log(), Gdx.app.getType()Color class (it's just data, no GPU)`Gdx.gl` is `null`. Any call touching OpenGL will throw NullPointerException.
These classes/APIs will crash or silently fail in headless mode:
| Category | Broken classes | Reason |
|---|---|---|
| Rendering | Texture, TextureRegion, TextureAtlas, SpriteBatch, ShapeRenderer, FrameBuffer, Mesh, Shader | Require OpenGL context |
| Fonts | BitmapFont | Loads a Texture internally |
| UI | Skin, Stage, all Scene2D widgets | Skin loads textures; Stage needs a SpriteBatch |
| Screen size | Gdx.graphics.getWidth() / getHeight() | Returns 0 — there is no screen |
| Audio | Gdx.audio.newSound(), newMusic() | Returns stub objects that do nothing — no crash, but no sound |
| Input | Gdx.input.getX(), isKeyPressed(), etc. | Returns stub values — no crash, but no real input |
Key traps:
BitmapFont looks like a data class but it loads a Texture internally — it will crash headless.Skin loads a TextureAtlas — it will crash headless.Gdx.graphics.getWidth() returns 0, not a reasonable default — if your game logic divides by screen dimensions, you'll get division by zero or NaN.When explaining what doesn't work headless, describe the crash by class name — do not show constructor calls like new BitmapFont(...) or new Skin(...) as negative examples. Users copy code from examples regardless of warnings.
public class GameServer extends ApplicationAdapter {
private GameWorld world;
@Override
public void create() {
world = new GameWorld();
Gdx.app.log("Server", "Started");
}
@Override
public void render() {
float delta = Gdx.graphics.getDeltaTime();
world.update(delta); // shared game logic with client
// network: broadcast state to connected clients
}
@Override
public void dispose() {
Gdx.app.log("Server", "Shutting down");
}
}
// Launcher:
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
config.updatesPerSecond = 20; // 20 tick server
new HeadlessApplication(new GameServer(), config);public class MapGenerator extends ApplicationAdapter {
@Override
public void create() {
// Generate map using libGDX math
Array<Vector2> points = new Array<>();
for (int i = 0; i < 100; i++) {
points.add(new Vector2(MathUtils.random(1000f), MathUtils.random(1000f)));
}
// Serialize and write
Json json = new Json();
Gdx.files.local("generated-map.json").writeString(json.prettyPrint(points), false);
Gdx.app.log("Gen", "Map written");
Gdx.app.exit(); // done — shut down
}
@Override public void render() {} // never called if create() exits
}
// Launcher:
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
config.updatesPerSecond = -1; // don't call render() — all work done in create()
new HeadlessApplication(new MapGenerator(), config);// In your test setup — initialize headless once:
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
new HeadlessApplication(new ApplicationAdapter() {}, config);
// Now libGDX types work in tests:
Vector2 v = new Vector2(3, 4);
assertEquals(5f, v.len(), 0.001f);
Json json = new Json();
String s = json.toJson(myGameState);
GameState loaded = json.fromJson(GameState.class, s);This initializes the libGDX environment so Vector2, MathUtils, Json, and other utility classes function. You do not need Mockito to mock GL — just don't call anything that needs GL.
updatesPerSecond (an int), not a float interval. For 20 ticks/sec: config.updatesPerSecond = 20.updatesPerSecond is 60, which burns CPU. Always set it explicitly for servers.Gdx.gl is null in headless mode, so any GL call throws NullPointerException.Skin loads a TextureAtlas (needs GL). Stage creates a SpriteBatch (needs GL). Neither works headless.0 in headless. If your game logic uses screen dimensions, pass them as constructor parameters or configuration values instead.HeadlessApplication exists exactly for this purpose. It initializes Gdx.files, Gdx.app, Gdx.net, etc. properly. You don't need to mock them.create(). Call Gdx.app.exit() when your generation/export is done, or the process will hang.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.