🎮 How to use input4j in your Java Game

A practical guide to adding gamepad support to your Java game

📅 Updated Mar 2026 📄 10 min read ⭐ Beginner

Setting up input4j

First, add input a dependency to your project:

For Gradle projects:

Gradle
dependencies {
    implementation 'de.gurkenlabs:input4j:1.1.1'
}

For Maven projects:

Maven
<dependency>
    <groupId>de.gurkenlabs</groupId>
    <artifactId>input4j</artifactId>
    <version>1.1.1</version>
</dependency>

Device Initialization

Initialize the input system when your game starts:

Java
import de.gurkenlabs.input4j.InputDevices;
import de.gurkenlabs.input4j.InputDevice;

public class Game {
    private List<InputDevice> controllers;
    
    public void init() {
        // Initialize the input system
        var deviceList = InputDevices.init();
        
        // Get all connected controllers
        controllers = deviceList.getAll();
        
        // Or get a specific controller by index
        // InputDevice controller = controllers.get(0);
        
        System.out.println("Found " + controllers.size() + " controllers");
    }
}

Polling vs Events

input4j supports two input handling modes:

📡

Event-Based (Recommended)

Register callbacks for button presses and axis changes. Better for event-driven architectures.

🔄

Polling

Query current input state each frame. Better for game loops with fixed update cycles.

Event-Based Example
// Event-based: Register callbacks
device.onButtonPressed(XInput.Button.A, () -> {
    player.jump();
});

device.onButtonReleased(XInput.Button.B, () -> {
    player.attack();
});

device.onAxisChanged(XInput.Axis.LEFT_X, value -> {
    player.setHorizontalMovement(value);
});
Polling Example
// Polling: Query in game loop
public void update(float deltaTime) {
    for (InputDevice device : controllers) {
        // Check button state
        if (device.isPressed(XInput.Button.A)) {
            player.jump();
        }
        
        // Get axis value (-1.0 to 1.0)
        float leftX = device.getAxisValue(XInput.Axis.LEFT_X);
        float leftY = device.getAxisValue(XInput.Axis.LEFT_Y);
        
        player.move(leftX, leftY);
    }
}

Integration with Game Loop

Here's a complete example of integrating input4j with a typical game loop:

Java
public class Game {
    private InputDevice controller;
    
    public void init() {
        var devices = InputDevices.init();
        this.controller = devices.getAll().stream()
            .findFirst()
            .orElse(null);
            
        if (controller != null) {
            // Use event-based for menu navigation
            controller.onButtonPressed(XInput.Button.START, () -> {
                startGame();
            });
            
            controller.onButtonPressed(XInput.Button.BACK, () -> {
                openMenu();
            });
        }
    }
    
    public void update(float deltaTime) {
        if (controller == null) return;
        
        // Polling for movement during gameplay
        float moveX = controller.getAxisValue(XInput.Axis.LEFT_X);
        float moveY = controller.getAxisValue(XInput.Axis.LEFT_Y);
        
        // Apply deadzone
        if (Math.abs(moveX) < 0.1f) moveX = 0;
        if (Math.abs(moveY) < 0.1f) moveY = 0;
        
        player.setVelocity(moveX * SPEED, moveY * SPEED);
    }
}

Controller Mapping

Different controllers map to different button definitions:

Controller Button Set Notes
Xbox XInput.Button A, B, X, Y, LB, RB, START, BACK, etc.
PlayStation DualShock4.Button CROSS, CIRCLE, SQUARE, TRIANGLE, etc.
Generic Button (0-127) Raw button indices

Force Feedback / Rumble

Add haptic feedback to enhance game feel:

Java
// Set vibration intensity (0.0 to 1.0)
device.setVibration(0.5f, 0.5f); // Left motor, Right motor

// Rumble on hit
device.setVibration(1.0f, 0.0f);
Thread.sleep(100);
device.setVibration(0.0f, 0.0f);

// Or use try-with-resources for auto-cleanup
try (var devices = InputDevices.init()) {
    for (var device : devices.getAll()) {
        device.setVibration(0.8f, 0.8f);
    }
}