love.graphics.draw Shows Nothing: A Diagnostic Guide for Missing Images in LÖVE
Your LÖVE window opens but love.graphics.draw shows nothing. An ordered diagnostic guide covering failed image loads, off-screen coordinates, leftover canvases, transparent colors, and shaders — with fixes tied to each finding.
15 Jun 2026, 04:03 UTC

Your LÖVE window opens, the background color clears fine, but the sprite you draw with love.graphics.draw never appears. This is one of the most common LÖVE problems, and it almost always comes down to one of six causes: the image failed to load, the draw position or scale puts it off-screen, the image has zero dimensions, a canvas is still active, the draw color is transparent, or a shader is swallowing the output. This guide walks through checks in order of likelihood so you can isolate the cause in minutes instead of guessing.
Recognizable condition
The symptom is specific: love.graphics.clear works (you see your background color), other drawing like love.graphics.print may work, but one or more images drawn with love.graphics.draw are invisible. The game does not crash, and there is no error in the console. If you do get an error, it usually points directly at the cause (a bad path or a nil image), so this guide covers the silent-failure case.
Cause and diagnostic table
| Likely cause | How to detect it | Fix |
|---|---|---|
| Image failed to load (nil) | Check return of love.graphics.newImage; call love.filesystem.getInfo(path) | Fix the path; confirm the file is inside the project directory |
| Draw position or scale off-screen/zero | Print x, y, sx, sy at draw time | Move coordinates inside the window; ensure scale is non-zero |
| Image dimensions are zero | Print img:getWidth(), img:getHeight() | Convert the asset to PNG or JPG; or load via love.image.newImageData |
| A canvas is still active | love.graphics.getCanvas() returns non-nil | Call love.graphics.setCanvas() with no arguments to restore the screen |
| Draw color alpha is 0 | Check love.graphics.getColor() | Call love.graphics.setColor(1, 1, 1, 1) before drawing |
| A shader discards the output | Temporarily call love.graphics.setShader() to disable | Fix or remove the shader |
Ordered checks
Work through these in order. Each check is a few lines you can paste into love.draw or love.load. Run your game from a terminal (or use an IDE console) so print output is visible.
1. Confirm the image actually loaded
In LÖVE 11.x, love.graphics.newImage raises an error on a missing file, but a wrapped or pcall'd load can silently produce nil. Verify both the file and the object:
local path = "assets/player.png"
local info = love.filesystem.getInfo(path)
print("file exists:", info ~= nil, info and info.type or "none")
playerImage = love.graphics.newImage(path)
print("image:", playerImage, "size:", playerImage:getWidth(), playerImage:getHeight())
If getInfo returns nil, the path is wrong. Remember that LÖVE paths are relative to the project root (where main.lua lives), use forward slashes, and are case-sensitive on some platforms even when your editor is not. If the image loads but reports a width or height of 0, the file is likely a format LÖVE cannot decode — re-export it as PNG or JPG.
2. Log the draw parameters
A sprite drawn at x = 5000 or with a scale of 0 is technically rendering — just nowhere you can see it. Print the values right before the draw call:
print("draw at", x, y, "scale", sx, sy)
love.graphics.draw(playerImage, x, y, 0, sx, sy)
Compare x and y against love.graphics.getWidth() and love.graphics.getHeight(). On high-DPI displays, window coordinates and pixel coordinates can differ; check love.window.getSafeArea() if things appear shifted on such screens. Also remember that love.graphics.draw positions the image by its top-left corner unless you pass origin offsets (ox, oy), so a large origin offset can push the sprite off-screen even when x and y look correct.
3. Check for a leftover canvas
If any code calls love.graphics.setCanvas(myCanvas) and never restores the default target, everything you draw afterward goes to that off-screen canvas instead of the window. This is the classic cause of "my HUD/particles vanished after I added post-processing." Add this at the top of love.draw:
print("canvas:", love.graphics.getCanvas()) -- should print nil when drawing to screen
If it prints a Canvas object, find the setCanvas call that was not paired with a restore. The safe pattern is:
love.graphics.setCanvas(myCanvas)
-- draw to the canvas
love.graphics.setCanvas() -- no arguments: back to the screen
4. Check the draw color
love.graphics.setColor multiplies everything drawn afterward, including images. If earlier code set the color with an alpha of 0 (for a fade-out, for example) and never reset it, your sprite is drawn fully transparent. In LÖVE 11.x color components are 0–1 floats, so the reset is:
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(playerImage, x, y)
Note for older projects: LÖVE 0.10 and earlier used 0–255 color values, and version 11.0 changed the default. If you ported an old game and everything looks tinted or invisible, a stale setColor(255, 255, 255) (which clamps to full white, fine) is harmless, but setColor(r, g, b, 0) anywhere in the frame is not.
5. Rule out shaders and transforms
An active shader with a bug (or one written for a different coordinate space) can discard every pixel. Disable it and test:
love.graphics.setShader() -- no arguments: default shader
love.graphics.origin() -- reset any push/pop/translate/rotate state
If the image appears after these two lines, re-enable the shader and transforms one at a time to find the culprit. A stray love.graphics.translate without a matching love.graphics.pop shifts everything drawn later in the frame.
Fixes tied to findings
- Image nil: correct the path, verify with
love.filesystem.getInfo, and reload. Confirm the file is actually packaged — when running a fused executable or a .love archive, files outside the archive are not visible. - Off-screen or zero scale: clamp x and y to the window, and assert
sx > 0andsy > 0. Negative scale flips the image around its origin, which can also move it off-screen. - Zero dimensions: convert the asset to PNG or JPG, or decode it yourself with
love.image.newImageDataand build the texture withlove.graphics.newImage(imageData). - Canvas active: restore with
love.graphics.setCanvas(), then draw the canvas itself to the screen if that was the intent. - Transparent color: set
love.graphics.setColor(1, 1, 1, 1)before the draw, or wrap state changes inlove.graphics.push("all")/love.graphics.pop(). - Shader: disable it, then fix the shader code or replace it with a minimal pass-through while debugging.
Confirming the fix
After each change, draw a known-good reference next to the sprite:
love.graphics.setColor(1, 0, 0, 1)
love.graphics.rectangle("fill", x, y, 32, 32)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(playerImage, x, y)
If the red rectangle shows but the image does not, the problem is the image or color state, not the coordinates. You can also check love.graphics.getStats().drawcalls before and after the draw call — it should increase by one, confirming the draw is being submitted at all.
Escalation criteria
If every check passes — the image is non-nil with sane dimensions, coordinates are on-screen, no canvas or shader is active, color is opaque, and the draw call count increases — the issue is likely environmental rather than code. At that point: test with a minimal project containing only main.lua and one PNG; test on another machine to rule out a driver problem; and state your LÖVE version (love.getVersion()) when asking for help, since error behavior around image loading and color ranges differs between 0.10, 11.x, and 12.x. Behavior described here assumes LÖVE 11.3 or later; verify against your installed version before relying on the exact API details.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.