Lab: Publish your game
Package the complete game with PyInstaller, verify the build runs without Python installed, and write a one-page itch.io description.
- Audit and fix all hardcoded asset paths to use pathlib.Path
- Bundle the game and its assets with PyInstaller
- Verify the standalone executable launches without a Python installation
- Write a concise itch.io page description
This lab walks through the full publishing pipeline from source to itch.io page. Work through each step on your completed game. If you do not have a finished game yet, the code snippets apply to any pygame project.
These are shell/terminal commands and Python edits. Run shell commands in your terminal in the root directory of your game project.
Step 1 — Audit paths
Search your codebase for hardcoded string paths:
grep -rn "open(" src/
grep -rn "pygame.image.load(" src/
grep -rn "pygame.mixer" src/
grep -rn "pygame.font.Font(" src/Any call that uses a string literal like "assets/player.png" is a candidate
for a bug. Replace all of them with pathlib.Path as shown in the previous
lesson.
Add the asset_path helper at the top of your entry-point file:
import sys
from pathlib import Path
def asset_path(relative):
"""Works in development and after PyInstaller bundling."""
base = Path(getattr(sys, '_MEIPASS', Path(__file__).parent))
return base / relativeThen replace every asset load:
# Before
img = pygame.image.load("assets/sprites/hero.png")
# After
img = pygame.image.load(asset_path("assets/sprites/hero.png"))Run the game from the project root and confirm it still works before proceeding.
Step 2 — Rename assets to lowercase
Check for uppercase letters in your assets directory:
find assets/ -name "*[A-Z]*"Rename any offending files. Update the corresponding load calls. Run the game again on Linux if possible (a Docker container is enough):
docker run --rm -v "$PWD:/game" python:3.12-slim \
bash -c "pip install pygame && cd /game && python game.py"A FileNotFoundError here reveals a case-sensitivity bug caught before it hits
a player.
Step 3 — Convert audio to .ogg
List audio files that are not .ogg:
find assets/ -name "*.mp3" -o -name "*.wav" | head -20Convert with ffmpeg (install once: sudo apt install ffmpeg or brew install ffmpeg):
for f in assets/sfx/*.mp3; do
ffmpeg -i "$f" "${f%.mp3}.ogg" && rm "$f"
doneUpdate load calls to reference the new filenames.
Step 4 — Run PyInstaller
Install PyInstaller if needed:
pip install pyinstallerFirst build — let PyInstaller auto-detect everything:
pyinstaller --onefile --windowed \
--add-data "assets:assets" \
--name "MyGame" \
game.pyOn Windows replace : with ; in --add-data. Check dist/MyGame (or
dist/MyGame.exe) was created.
Step 5 — Edit the .spec for repeatability
Open the generated MyGame.spec and verify the datas list includes all asset
folders. Add any that are missing:
datas=[
('assets', 'assets'),
('fonts', 'fonts'),
('data', 'data'), # add if you have a save/config folder
],Rebuild from the spec:
pyinstaller MyGame.specCommit MyGame.spec to version control. This is now your canonical build
recipe — anyone who clones the repo can run pyinstaller MyGame.spec and get
an identical output.
Step 6 — Smoke test on a clean machine
Copy dist/MyGame (and nothing else) to a machine without Python installed, or
test inside a clean VM / Docker container:
# Linux test in a minimal container
docker run --rm -v "$PWD/dist:/dist" ubuntu:22.04 /dist/MyGameConfirm:
- The window opens.
- All images, sounds, and fonts load.
- The game plays through at least one full session.
- There are no console errors.
Step 7 — Package for distribution
Create a distribution archive:
# macOS / Linux
cd dist && zip -r MyGame-linux.zip MyGame
# Windows (PowerShell)
Compress-Archive -Path dist\MyGame.exe -DestinationPath MyGame-windows.zipInclude a README.txt in the zip:
My Game v1.0
=============
Controls:
Arrow keys — move
Space — jump
H — attack
Linux: chmod +x MyGame then ./MyGame
macOS: right-click → Open (first time only)
Windows: double-click MyGame.exe
Report bugs at: https://yourusername.itch.io/mygameStep 8 — Publish on itch.io
- Create an account at itch.io.
- Click Create new project.
- Set kind to Downloadable.
- Upload your zip file(s). Mark each with the correct OS.
- Write a short description. A good template:
A 2D platformer featuring physics-based movement, AI enemies with
pathfinding, and particle effects.
Controls: Arrow keys to move, Space to jump, H to attack.
Built with Python and pygame.- Add at least one screenshot (take one with pygame's
pygame.image.save(screen, "screenshot.png")). - Set the price (free or pay-what-you-want for your first release).
- Click Save and view page to confirm everything looks correct.
- Publish.
itch.io provides a free "butler" CLI tool for automated uploads:
butler push dist/MyGame-linux.zip yourusername/mygame:linux
This is useful if you want to integrate uploads into a CI pipeline.
Congratulations — you have shipped a game.