Cross-platform concerns
Windows, macOS, and Linux differ in path separators, filesystem case sensitivity, sound format support, and executable permissions — know these before shipping.
- Use pathlib.Path instead of string concatenation to avoid path separator bugs
- Explain why macOS hides case-sensitivity bugs that Linux exposes
- Know that .ogg is the safest sound format and why .mp3 can cause problems
- Set executable permission bits on Linux binaries
A game that works perfectly on your development machine may break silently on a player's machine if they are on a different operating system. Most cross-platform bugs fall into four categories.
Path separators and pathlib
Windows uses backslashes (\) as path separators. macOS and Linux use forward
slashes (/). Hardcoding paths as strings is the fastest route to a
platform-specific bug:
# Breaks on Windows:
image = pygame.image.load("assets/sprites/player.png")
# Also breaks (now on Linux/macOS):
image = pygame.image.load("assets\\sprites\\player.png")The fix is pathlib.Path, which uses the correct separator for the current OS
automatically:
from pathlib import Path
ASSETS = Path(__file__).parent / "assets"
image = pygame.image.load(ASSETS / "sprites" / "player.png")
font = pygame.font.Font(ASSETS / "fonts" / "monogram.ttf", 24)Path objects convert to strings automatically when passed to pygame's loaders.
Use / as the path join operator regardless of platform.
Case sensitivity
macOS's default filesystem (HFS+) is case-insensitive: Player.png and
player.png refer to the same file. Linux's ext4 is case-sensitive: they
are different files, and loading the wrong one raises FileNotFoundError.
A common workflow bug: you name your sprite Player.png but load it as
player.png. It works on your Mac. It fails on every Linux machine.
The fix: adopt a consistent naming convention (all lowercase with hyphens:
player-idle.png) and enforce it. Run your game on Linux — or in a Linux
Docker container — before release.
# Quick check: list all asset filenames containing uppercase letters
find assets/ -name "*[A-Z]*"Sound format compatibility
.mp3 has historically had patent issues on Linux. While the patent situation
has largely expired, some minimal Linux builds still lack the codec. Use
.ogg (Vorbis) for sound effects and music — it is open, patent-free, and
pygame has first-class support on all platforms.
# Preferred:
pygame.mixer.Sound("assets/sfx/jump.ogg")
pygame.mixer.music.load("assets/music/theme.ogg")
# Avoid if cross-platform support matters:
# pygame.mixer.Sound("assets/sfx/jump.mp3")Most audio editors (Audacity, LAME) export .ogg directly. For batch
conversion, ffmpeg -i input.mp3 output.ogg is fast and free.
Executable permissions on Linux
A PyInstaller binary on Linux requires the executable bit to be set. If you ZIP and unzip the build on Linux, the permissions are usually preserved. If you transfer the file via certain methods (NTFS drives, some cloud storage), the permission bit may be stripped.
Players running your game see Permission denied when they try to execute it.
The fix is a one-liner:
chmod +x MyGame
./MyGameDocument this in your game's README. Alternatively, provide a small launcher shell script:
#!/bin/sh
chmod +x "$(dirname "$0")/MyGame"
"$(dirname "$0")/MyGame"macOS has an additional wrinkle: Gatekeeper blocks executables from unidentified
developers. Players must right-click → Open the first time, or you can instruct
them to run xattr -cr MyGame.app in Terminal. Code signing with an Apple
Developer certificate eliminates this for public releases.
Where to go next
Next: lab — publish game — fixing hardcoded paths, running PyInstaller, smoke-testing the build, and writing an itch.io page.