Quick answer: Use convert_alpha() instead of convert() when loading images with transparency. convert() strips alpha for speed. For hard-edged sprites, set_colorkey(color) is a faster alternative to full alpha.
Here is how to fix Pygame Surface blit alpha not transparent. You load a PNG with transparent background. You blit it onto your main surface. The image appears with a solid black (or whatever) background instead of transparency. Pygame has three different transparency modes and picking the wrong one produces opaque sprites, partial transparency, or weird artifacts.
The Symptom
A PNG (or other format with alpha) renders with a visible background color instead of transparency when blitted in Pygame. Common variants:
- Character sprite shows a black or magenta rectangle around it
- UI overlay appears as a solid box instead of transparent frame
- Alpha appears to work but is only 0 or 255 (binary transparency)
What Causes This
convert() strips alpha. After pygame.image.load(), the common pattern is .convert() to convert to the display’s format for faster blitting. But .convert() converts to the display’s color format, which may not have alpha. .convert_alpha() preserves the alpha channel.
No convert call at all. If you skip conversion entirely, blit is slower and alpha behavior depends on source format. Works but slow — and on some platforms, alpha rendering is incorrect without explicit conversion.
Colorkey conflict. If you call set_colorkey((255, 0, 255)) expecting magenta transparency, but your PNG has alpha channel instead of a magenta background, nothing transparent happens. Colorkey and alpha channel are mutually exclusive.
Wrong display mode. The main surface created via pygame.display.set_mode() by default has no alpha. Blitting an alpha-enabled sprite onto a non-alpha surface works fine (the sprite blends over the opaque background) but creating an intermediate alpha surface requires pygame.SRCALPHA.
The Fix
Step 1: Use convert_alpha for transparent images.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
# Correct: preserves alpha
sprite = pygame.image.load("player.png").convert_alpha()
# Wrong: strips alpha
# sprite = pygame.image.load("player.png").convert()
screen.blit(sprite, (100, 100))
pygame.display.flip()
Always convert_alpha() for PNGs with transparent backgrounds. The result is a Surface with per-pixel 8-bit alpha.
Step 2: Use set_colorkey for retro sprites. For pixel art with hard edges and a dedicated transparent color (magenta #FF00FF is traditional):
sprite = pygame.image.load("retro_hero.bmp").convert()
sprite.set_colorkey((255, 0, 255))
# or colorkey pixel (0, 0)
sprite.set_colorkey(sprite.get_at((0, 0)))
Colorkey is faster than per-pixel alpha and perfect for crisp pixel art. Do not combine with convert_alpha — pick one.
Step 3: Create alpha-enabled surfaces explicitly. For a custom overlay surface that needs alpha:
# Create a transparent surface for custom drawing
overlay = pygame.Surface((400, 300), pygame.SRCALPHA)
overlay.fill((255, 0, 0, 128)) # red, 50% opacity
screen.blit(overlay, (200, 150))
Without SRCALPHA, the Surface constructor creates an opaque surface and alpha fill is ignored.
Step 4: Surface-level alpha for simple fades. For a whole sprite at reduced alpha (e.g. ghost mode):
sprite.set_alpha(128) # 50% transparent
screen.blit(sprite, (100, 100))
# Reset to full opacity
sprite.set_alpha(255)
Surface-level alpha applies uniformly to the whole sprite. Use for fades and opacity changes. Combine with convert_alpha for full per-pixel + uniform transparency.
Performance Tips
Per-pixel alpha is 2–4x slower than opaque or colorkey blit. For bullet-hell games with hundreds of sprites on screen:
- Use set_colorkey if sprite edges are hard
- Cache transformed sprites (rotated/scaled) rather than transforming every frame
- Use pygame.sprite.Group with dirty rect tracking for UI-heavy scenes
Profile with pygame.time.Clock and measure FPS before and after alpha changes. Convert_alpha everywhere is convenient but not always performant.
Common Pitfall: Loading Before pygame.display.set_mode
Calling convert_alpha() before display.set_mode() works but is inefficient — without a display mode, conversion has no target format. Always initialize display first, then load images. Some platforms log warnings for the reverse order.
“convert_alpha for transparency. convert for opaque. set_colorkey for retro. Pick one per surface and commit.”
Related Issues
For general Pygame resource handling, see the broader Best Bug Tracking Tools for Solo Developers. For cross-engine transparency patterns, Unity LineRenderer Not Visible covers related transparency issues.
convert_alpha() for PNGs. Surface with SRCALPHA flag for overlays. The difference is free performance if you match to your use case.