Quick answer: Query the native resolution with pygame.display.Info() before calling set_mode, or use the SCALED flag to render at a fixed design resolution and let Pygame upscale automatically. Hardcoded resolutions produce black bars or stretched output on mismatched monitors.
You ship your Pygame game and a player on a 2560×1440 monitor reports that fullscreen shows the game in a tiny box with black bars. Another player on a 1366×768 laptop says the game is cut off on the edges. You tested on your 1920×1080 monitor and it looked fine. The bug is that you passed your dev resolution to set_mode and assumed everyone has the same screen.
The Three Approaches
Approach 1: Match the native resolution.
import pygame
pygame.init()
# Query before set_mode
info = pygame.display.Info()
native_w, native_h = info.current_w, info.current_h
screen = pygame.display.set_mode((native_w, native_h), pygame.FULLSCREEN)
print(f"Running at {native_w}x{native_h}")
This gives you a fullscreen surface at the monitor’s native resolution. No stretching, no letterboxing. You render at whatever resolution the player has, which means your UI and game world scale with the monitor. Good for games that do not need a fixed pixel grid.
Approach 2: Use SCALED for fixed design resolution.
# Render at 640x360 logical, upscale to native
screen = pygame.display.set_mode(
(640, 360),
pygame.FULLSCREEN | pygame.SCALED
)
The SCALED flag (pygame 2.0+) creates a logical surface at your design resolution and handles the upscale to the display. Aspect ratio is preserved with letterboxing if needed. This is the best option for pixel-art games that want a fixed grid and do not want to worry about resolution math.
Approach 3: Borderless fullscreen window.
info = pygame.display.Info()
screen = pygame.display.set_mode(
(info.current_w, info.current_h),
pygame.NOFRAME
)
A borderless window at native resolution looks identical to fullscreen but avoids the mode switch that some GPUs handle poorly. Alt-tab is faster. The downside is that the OS taskbar may occasionally appear on top.
Common Pitfalls
Calling display.Info() after set_mode. After set_mode, display.Info() returns the mode you set, not the native resolution. Call it before the first set_mode to get the monitor’s actual capabilities.
Ignoring aspect ratio. A 16:9 game on a 16:10 monitor will have bars or be clipped. With SCALED, Pygame handles this. Without it, you must add letterboxing yourself: render to a surface at your design resolution, scale it to fit the screen maintaining aspect ratio, and blit it centered.
Multi-monitor confusion. FULLSCREEN always uses the primary monitor. On multi-monitor setups, players may want the game on a secondary screen. Pygame 2.1+ exposes display.get_desktop_sizes() to enumerate monitors, but targeting a specific one requires positioning the window with SDL_VIDEO_WINDOW_POS before going fullscreen.
Verifying the Fix
Add a debug overlay that prints the actual screen surface size and the logical design size. Test at three resolutions: 1366×768, 1920×1080, and 2560×1440 (change your display settings temporarily if you do not have multiple monitors). The game should fill the screen correctly at each one.
“Never hardcode a resolution in a fullscreen call. Query the display, adapt to it, or use SCALED and let the library worry about the math. Anything else breaks on someone’s monitor.”
Related Issues
For text rendering issues after scaling, see Pygame text rendering blurry after scale. For broader Pygame performance, see Pygame performance tips for indie developers.
Use SCALED for pixel-art games. It is one flag that eliminates every resolution bug you would otherwise write custom code to handle.