Choosing Between FitViewport and ExtendViewport in LibGDX: A Practical Guide
Learn how FitViewport and ExtendViewport scale your LibGDX game world across devices.
11 Jul 2026, 17:41 UTC

Why Viewports Matter in LibGDX
LibGDX runs on everything from a 720×1280 phone to a 4K laptop. Hard‑coding sprite positions in pixels will cause clipping or mis‑placement on other screen sizes. A viewport solves this by keeping a fixed virtual world size and letting the library map that world to whatever the real screen is.
Virtual World vs. Real Screen
Choose a virtual width and virtual height that represent the “design space” of your game. All actors, sprites, and UI elements are positioned in that space. The viewport takes the real screen dimensions (in pixels) and calculates a scaling factor that tells the camera how to stretch or shrink the virtual world so it fits.
Key Terms
- Camera: Projects world coordinates to screen coordinates.
- Stage: Manages Scene2D actors and automatically applies the viewport’s camera when drawing.
- unproject(): Converts screen pixel coordinates (e.g., from a touch event) into world coordinates.
FitViewport vs. ExtendViewport
Both viewports preserve the aspect ratio of the virtual world, but they handle the extra space differently.
FitViewport
Scales the virtual world uniformly until one dimension (width or height) exactly matches the real screen. The other dimension is padded with black bars (letterboxing). This guarantees that the entire designed world is always visible and that no part of the world is hidden or stretched.
ExtendViewport
Also scales uniformly, but once the smaller dimension matches the screen, it extends the larger dimension by adding extra world space. Instead of bars, the viewport shows more of the world on devices that are wider or taller than the design aspect ratio.
When to Use Which?
- FitViewport is ideal when the exact layout matters – e.g., platformers where you want to avoid off‑screen content or games that rely on precise collision boundaries.
- ExtendViewport is useful for games that can display extra scenery on larger screens, like side‑scrollers or top‑down shooters where extra space simply shows more of the level.
Concrete Example: A Simple 2D Runner
Below is a minimal setup that shows how to create an ExtendViewport with a base virtual resolution of 800×480, set up a Stage, and handle touch input correctly.
public class RunnerGame extends ApplicationAdapter {
private Stage stage;
private ExtendViewport viewport;
private SpriteBatch batch;
@Override
public void create() {
batch = new SpriteBatch();
viewport = new ExtendViewport(800, 480, new OrthographicCamera());
stage = new Stage(viewport, batch);
// Add a simple actor that stays in the bottom‑left corner
Image runner = new Image(new Texture(Gdx.files.internal("runner.png")));
runner.setPosition(0, 0); // virtual coordinates
stage.addActor(runner);
}
@Override
public void resize(int width, int height) {
viewport.update(width, height, true); // center camera
}
@Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Update logic
stage.act(Gdx.graphics.getDeltaTime());
// Draw everything
stage.draw();
// Example of unprojecting a touch
if (Gdx.input.isTouched()) {
Vector3 touch = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
viewport.unproject(touch);
Gdx.app.log("Touch", "World coords: " + touch.x + ", " + touch.y);
}
}
@Override
public void dispose() {
stage.dispose();
batch.dispose();
}
}
Key points to notice:
- The
ExtendViewportis created with a virtual size of 800×480. - In
resize(), we callviewport.update()with the real screen width and height. - All actors are positioned using virtual coordinates; the viewport ensures they appear at the same relative spot regardless of device.
- Touch coordinates are converted with
viewport.unproject()before being used.
Trade‑Offs and Limitations
- Extra World Exposure – ExtendViewport can reveal parts of the level that were never designed to be seen. If you have background edges or off‑screen enemies, they may appear on wide tablets or desktop browsers.
- UI Scaling – If you mix a
Stagefor UI with a separateStagefor the game world, you must keep their cameras separate. Using the sameSpriteBatchwithout resetting the projection matrix can cause UI elements to render at the wrong size. - Input Mismatch – Forgetting to unproject touch coordinates will shift interactions on high‑DPI screens, leading to a frustrating user experience.
Practical Checklist Before You Ship
- Run the game on a 720×1280 emulator and a 1080×1920 emulator. Log
viewport.getWorldWidth()andviewport.getScreenWidth()to verify the scaling factor. - Toggle between
FitViewportandExtendViewportin the same project and resize the window. Observe whether black bars appear or extra world content shows up. - Touch the screen on a pixel‑dense device and confirm the log shows world coordinates that match the actor positions.
- Check that UI elements in a separate
Stageremain centered and do not bleed into the extra world space.
Conclusion
Choosing the right viewport is a design decision that balances visual fidelity with device versatility. FitViewport gives you a predictable layout with letterboxing, while ExtendViewport offers a more flexible canvas at the cost of potentially exposing unintended content. By setting up a single ExtendViewport with a clear virtual resolution, updating it in resize(), and always unprojecting input, you can keep your game logic resolution‑independent and ready for the wide range of devices that LibGDX supports.
Next time you ship a LibGDX game, start by defining a logical virtual resolution, pick the viewport that matches your design goals, and run the quick checklist above to ensure a smooth experience across all screens.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.