Nearest-neighbor filtering can keep a sprite sharp while its pixels still look uneven. A one-pixel line may become three display pixels wide in one place and four in another. The image is crisp, but its grid no longer has a consistent rhythm.
For a deliberately low-resolution 2D game, choose an internal canvas size, enlarge it by a whole-number factor, and accept unused space when the window does not fit that grid. Filtering and scaling solve related but different problems.
Godot's multiple-resolutions documentation explains its stretch modes and integer scaling option. The arithmetic here helps you choose the underlying resolution before changing settings at random.
Two decisions sit between the sprite and the screen
Filtering decides how a texture is sampled. Linear filtering blends neighboring texels, which can soften a small pixel-art sprite. Nearest sampling chooses a texel without that blend.
Scaling decides where source pixels land in the output grid. If each source pixel becomes a 4-by-4 block, the enlarged grid is regular. At a scale of 3.75, display pixels cannot represent every block as exactly 3.75 physical pixels. Rasterization must distribute the available pixels somehow.
That is why “turn off filtering” is an incomplete answer to inconsistent pixel widths. It addresses blending, but it cannot make a fractional grid divide evenly.
The choice is artistic as well as technical. A game with high-resolution UI over low-resolution scenery may intentionally use multiple rendering layers. The simplest whole-canvas approach is useful when the entire world should share one pixel grid.
Calculate the largest fitting factor
For an illustrative internal resolution of 320 by 180, a 1280-by-720 content area fits a factor of four exactly. A 1366-by-768 area also fits only four times: the world occupies 1280 by 720, leaving 86 horizontal and 48 vertical pixels outside it.
Centering distributes that remainder as 43 pixels on either side and 24 above and below. That unused space is the cost of preserving the grid. Stretching to fill the entire area uses fractional factors and changes the promise.
The fitting rule is:
def integer_fit(base_w, base_h, available_w, available_h):
if min(base_w, base_h, available_w, available_h) <= 0:
raise ValueError("dimensions must be positive")
factor = min(available_w // base_w, available_h // base_h)
if factor < 1:
raise ValueError("available area is smaller than the base canvas")
return factor, (base_w * factor, base_h * factor)
Use the actual drawable content area, which may differ from the operating system's advertised display resolution. A window border, display scaling or another enclosing surface can change the pixels available to the game.
If the area is smaller than your base canvas, this function refuses the request. Your product still needs a policy: enforce a minimum size, select a smaller canvas or permit a documented downscale. A whole-number enlargement setting cannot solve a window that is already too small.
Choose Godot's stretch behavior deliberately
For a low-resolution world, Godot's viewport stretch mode renders at the base viewport size before scaling the result. Combine the appropriate aspect policy with integer scale mode where supported by your target Godot version. Use nearest texture filtering for pixel-art elements that require it.
canvas_items serves a different goal: 2D elements are rendered at the target resolution while their canvas transforms adapt. It can be appropriate for high-resolution content, but it does not automatically make the entire rendered world one low-resolution image.
Do not apply these settings indiscriminately to all text. A low-resolution font may suit the game's visual design, while a separate high-resolution interface can improve readability. If you combine them, test the boundary rather than assuming the world and UI share identical scaling requirements.
A correct scale can still show uneven motion
Watch a sprite and the camera move slowly. Fractional object positions, camera transforms and rotation can affect how pixel art is sampled. Integer enlargement preserves the final canvas grid; it does not guarantee that every moving shape is aligned to that grid internally.
Use a test pattern with alternating one-pixel stripes, a simple sprite and a slow camera pan. Inspect still frames and motion separately. If every still frame has consistent block sizes but movement appears to shimmer, investigate internal positioning and camera behavior.
When sharing the evidence, avoid a resized preview as your only record. Save a direct capture of your development build at its original dimensions, record the viewport settings, and note any later scaling. A chat application's thumbnail can hide the uneven grid you want someone else to inspect.
Check the awkward sizes
Try a window that exactly matches a scale factor, one just below the next factor, and a narrow aspect ratio. Confirm where unused space appears and whether interface controls remain usable. Include the smallest supported content area in your acceptance check.
The right result may have borders. Those borders are preferable when they preserve the visual contract you chose. If filling every display matters more, accept fractional scaling deliberately and assess how it looks; do not expect a texture-filter setting to make both choices identical.
Further reading from this publisher: Jitsumi's practical guides to games and developer tools.

Top comments (0)