Quick answer: Call .convert_alpha() on every PNG you load. Without it, Pygame keeps the surface in a format that does not preserve per-pixel alpha, and transparent pixels render solid (or black) when blitted.
Here is how to fix Pygame loaded images that should be transparent but render with solid backgrounds. Your sprite art has clean alpha edges in the source PNG, but on screen the background of the sprite is solid black or magenta. The fix is the convert_alpha call that everyone forgets the first three times they write a Pygame loader.
The Symptom
A transparent PNG appears with a solid background after blit. The image data has alpha; you can verify in any image editor. Pygame just renders it as opaque.
What Causes This
Loaded without convert_alpha. Default load returns a surface in the source format. After the display is initialized, this format may not match what blit expects for alpha blending.
convert() instead of convert_alpha(). convert optimizes for the display but drops alpha entirely. Use convert_alpha for sprites with transparency.
Loaded before display init. If you load images before pygame.display.set_mode, Pygame may use a generic format that produces incorrect blending.
Color key conflict. Setting set_colorkey on a surface that also has per-pixel alpha can produce confusing results. Pick one mode.
The Fix
Step 1: Always convert_alpha after load.
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720)) # MUST be before convert_alpha
player_img = pygame.image.load("assets/player.png").convert_alpha()
enemy_img = pygame.image.load("assets/enemy.png").convert_alpha()
Step 2: Use convert for opaque images.
background = pygame.image.load("assets/bg.jpg").convert() # no alpha needed, faster
Step 3: Avoid color key with alpha.
# Avoid: mixing colorkey with per-pixel alpha
sprite = pygame.image.load("sprite.png").convert()
sprite.set_colorkey((255, 0, 255)) # magenta = transparent
# Prefer: per-pixel alpha
sprite = pygame.image.load("sprite.png").convert_alpha()
Step 4: For special blend modes, pass special_flags.
screen.blit(glow_sprite, (x, y), special_flags=pygame.BLEND_RGBA_ADD)
Useful for additive particles. Other useful flags: BLEND_RGB_MULT for multiplicative, BLEND_RGB_SUB for subtractive.
Step 5: Set per-surface alpha for fades.
fade_sprite = sprite.copy()
fade_sprite.set_alpha(128) # 50% transparent overall
screen.blit(fade_sprite, pos)
set_alpha applies a global multiplier on top of per-pixel alpha. Useful for fade transitions.
Common Pitfalls
Loading at module top before pygame.display.set_mode. The surface format is incorrect; subsequent blits show artifacts. Always init display first, load assets after.
Calling .convert_alpha() on JPEG sources. JPEG has no alpha; the call returns a surface with full opacity, which is fine but wasteful. Use .convert() for backgrounds.
“convert_alpha for transparent sprites. convert for opaque. After display init. Always.”
Related Issues
For draw order, see Sprite Group Draw Order. For Rect collisions, see Rect False Positives.
convert_alpha after load. Display first. The transparency works.