Mario Game in Python 2026: Free Source Code + Pygame Tutorial [Copy and Paste]

Building a Mario game in Python is one of the most rewarding capstone projects a BSIT student can take on. it teaches you the four core pillars of game programming in one project: the game loop, sprite collision, gravity-based physics, and tile-based level design. By the end of this tutorial, you’ll have a working platformer game with a controllable player, jumping mechanics, platforms to land on, Goomba-style enemies, a scoring system, and lives. Everything in under 400 lines of clean Python code using the pygame library.

This is the same Mario clone tutorial thousands of BSIT students have used to learn pygame since 2020. fully refreshed for 2026 with cleaner code, Python 3.10+ pattern matching where it helps, and fixes for the most common errors students hit during their thesis defense demos.

TL;DR: This is a pygame-based Mario clone in roughly 300-400 lines of Python. You’ll need Python 3.10+ and pygame 2.5+. The full source code is included below in copy-paste sections. main loop, Player class, Platform class, Enemy class, and level layout. Total build time: 60-90 minutes if you type along, 15 minutes if you copy-paste and tweak.

What You’ll Build

By the end of this tutorial, you will have a fully playable 2D side-scrolling platformer game inspired by the classic Super Mario Bros. The game includes:

  • A controllable Mario-style player. moves left, right, and jumps with smooth gravity-based physics
  • Static platforms the player can stand on and jump between (tile-based level)
  • Goomba-style enemies that patrol back and forth and damage the player on contact
  • A coin/score system with on-screen HUD showing score and remaining lives
  • Lives counter with respawn logic when the player falls off the map or touches an enemy
  • A clean game loop running at a stable 60 FPS using pygame.time.Clock()

The whole thing fits in one Python file (or 2-3 if you split into modules later). It uses simple colored rectangles by default so you don’t need sprite art to get it running. but every part of the code is structured so you can drop in real Mario sprites later when you have them.

Screenshot placeholder: insert a screenshot of the running game showing Mario, platforms, an enemy, and the HUD.

Prerequisites

Before you start, make sure you have these in place:

  • Python 3.10 or newer. check with python --version. If you don’t have it yet, see our guide to installing Python on Windows.
  • pygame 2.5+. installed via pip install pygame (full command in the next section).
  • A code editor. VS Code, PyCharm, or Thonny all work. See our best Python IDE comparison for 2026 if you haven’t picked one.
  • Basic Python knowledge. variables, functions, classes, and if/else. You do NOT need to know game development. We’ll explain every game-specific concept as it comes up.
  • 10 minutes of patience. pygame can be finicky on first install, especially on macOS with M-series chips. Stick with it; the second install always works.

Setting Up Your Environment

Create a project folder, set up a virtual environment, and install pygame:

# Windows (PowerShell)
mkdir mario_python
cd mario_python
python -m venv .venv
.venv\Scripts\activate
pip install pygame

# macOS / Linux
mkdir mario_python
cd mario_python
python3 -m venv .venv
source .venv/bin/activate
pip install pygame

Verify the install:

python -c "import pygame; print(pygame.version.ver)"
# Should print: 2.5.2 (or newer)

Project structure. keep it simple for now:

mario_python/
├── .venv/
├── mario.py          # the whole game (we'll split later if you want)
└── assets/           # optional, drop sprite PNGs here later

The Source Code

The full source is broken into sections below so you can read and understand each piece. Copy them in order into a single file called mario.py, or grab the complete file at the end.

1. Imports and Constants

import pygame
import sys

# Screen and game constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60

# Physics
GRAVITY = 0.8
JUMP_STRENGTH = -15
PLAYER_SPEED = 5

# Colors (R, G, B)
SKY = (135, 206, 235)
GROUND = (139, 69, 19)
PLATFORM = (160, 82, 45)
PLAYER_COLOR = (220, 20, 60)     # Mario red
ENEMY_COLOR = (101, 67, 33)      # Goomba brown
COIN_COLOR = (255, 215, 0)       # Gold coin
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

2. The Player Class (Movement, Jump, Gravity)

class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((30, 40))
        self.image.fill(PLAYER_COLOR)
        self.rect = self.image.get_rect(topleft=(x, y))
        self.vel_x = 0
        self.vel_y = 0
        self.on_ground = False
        self.lives = 3
        self.score = 0

    def update(self, platforms):
        # Horizontal input
        keys = pygame.key.get_pressed()
        self.vel_x = 0
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.vel_x = -PLAYER_SPEED
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.vel_x = PLAYER_SPEED

        # Jump
        if (keys[pygame.K_SPACE] or keys[pygame.K_UP]) and self.on_ground:
            self.vel_y = JUMP_STRENGTH
            self.on_ground = False

        # Apply gravity
        self.vel_y += GRAVITY
        if self.vel_y > 12:        # terminal velocity cap
            self.vel_y = 12

        # Move horizontally + check collision
        self.rect.x += self.vel_x
        self._collide(self.vel_x, 0, platforms)

        # Move vertically + check collision
        self.rect.y += self.vel_y
        self.on_ground = False
        self._collide(0, self.vel_y, platforms)

    def _collide(self, dx, dy, platforms):
        for p in platforms:
            if self.rect.colliderect(p.rect):
                if dx > 0:                          # moving right
                    self.rect.right = p.rect.left
                if dx < 0:                          # moving left
                    self.rect.left = p.rect.right
                if dy > 0:                          # falling down
                    self.rect.bottom = p.rect.top
                    self.vel_y = 0
                    self.on_ground = True
                if dy < 0:                          # jumping up
                    self.rect.top = p.rect.bottom
                    self.vel_y = 0

3. Platform and Coin Classes

class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y, w, h, color=PLATFORM):
        super().__init__()
        self.image = pygame.Surface((w, h))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=(x, y))


class Coin(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((16, 16))
        self.image.fill(COIN_COLOR)
        self.rect = self.image.get_rect(topleft=(x, y))

4. The Enemy Class (Goomba Patrol)

class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y, patrol_distance=120):
        super().__init__()
        self.image = pygame.Surface((28, 28))
        self.image.fill(ENEMY_COLOR)
        self.rect = self.image.get_rect(topleft=(x, y))
        self.start_x = x
        self.patrol_distance = patrol_distance
        self.speed = 2
        self.direction = 1     # 1 = right, -1 = left

    def update(self):
        self.rect.x += self.speed * self.direction
        if self.rect.x > self.start_x + self.patrol_distance:
            self.direction = -1
        elif self.rect.x < self.start_x:
            self.direction = 1

Section 5. Level Design (Tile-Based Layout)

def build_level():
    """Return platforms, enemies, coins, and the player start position."""
    platforms = pygame.sprite.Group()
    enemies = pygame.sprite.Group()
    coins = pygame.sprite.Group()

    # Ground (full width of screen)
    platforms.add(Platform(0, SCREEN_HEIGHT - 40, SCREEN_WIDTH, 40, GROUND))

    # Floating platforms (x, y, width, height)
    platforms.add(Platform(150, 450, 120, 20))
    platforms.add(Platform(350, 380, 120, 20))
    platforms.add(Platform(550, 300, 120, 20))
    platforms.add(Platform(200, 220, 100, 20))

    # Enemies (Goombas)
    enemies.add(Enemy(400, SCREEN_HEIGHT - 68, patrol_distance=200))
    enemies.add(Enemy(160, 430, patrol_distance=100))

    # Coins
    for cx in (180, 380, 580, 230):
        coins.add(Coin(cx + 50, cx % 200 + 180))

    player_start = (50, SCREEN_HEIGHT - 100)
    return platforms, enemies, coins, player_start

6. The Main Game Loop

def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("Mario Game in Python, itsourcecode.com")
    clock = pygame.time.Clock()
    font = pygame.font.SysFont("Arial", 22, bold=True)

    platforms, enemies, coins, player_start = build_level()
    player = Player(*player_start)

    running = True
    while running:
        clock.tick(FPS)

        # --- Event handling ---
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False

        # --- Updates ---
        player.update(platforms)
        enemies.update()

        # Coin pickup
        hit_coins = pygame.sprite.spritecollide(player, coins, dokill=True)
        player.score += len(hit_coins) * 10

        # Enemy collision = lose a life + respawn
        if pygame.sprite.spritecollideany(player, enemies):
            player.lives -= 1
            player.rect.topleft = player_start
            player.vel_y = 0

        # Fell off the map
        if player.rect.top > SCREEN_HEIGHT:
            player.lives -= 1
            player.rect.topleft = player_start
            player.vel_y = 0

        # Game over
        if player.lives <= 0:
            running = False

        # --- Drawing ---
        screen.fill(SKY)
        platforms.draw(screen)
        coins.draw(screen)
        enemies.draw(screen)
        screen.blit(player.image, player.rect)

        # HUD
        hud = font.render(
            f"Score: {player.score}   Lives: {player.lives}",
            True, BLACK,
        )
        screen.blit(hud, (10, 10))

        pygame.display.flip()

    # Game over screen
    screen.fill(BLACK)
    over = font.render(
        f"Game Over, Final Score: {player.score}",
        True, WHITE,
    )
    screen.blit(over, over.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)))
    pygame.display.flip()
    pygame.time.wait(2500)

    pygame.quit()
    sys.exit()


if __name__ == "__main__":
    main()

That’s the entire game in about 220 lines. Run it with python mario.py from inside your activated virtual environment. Use the arrow keys or WASD to move, Space or Up to jump, and Esc to quit.

Downloadable Source Code

How the Code Works

Understanding what the code does is more valuable than copy-pasting it. Here’s how each game-programming concept fits together:

Game loop

The game loop is the heartbeat of every game. Every frame (60 times per second), we do the same three things in order: handle events (keyboard, mouse, window close), update game state (move player, move enemies, check collisions), and draw everything to the screen. clock.tick(FPS) caps the loop at 60 frames per second so the game runs at the same speed on a fast PC and a slow laptop.

Gravity-based physics

Gravity-based physics is shockingly simple. Every frame, we add GRAVITY (0.8 pixels per frame) to the player’s vertical velocity (vel_y). That makes the player accelerate downward over time. exactly like real gravity. When the player jumps, we set vel_y = -15 (negative is up in pygame), so the player rises until gravity overwhelms the jump speed and pulls them back down. The terminal velocity cap (if self.vel_y > 12: self.vel_y = 12) prevents the player from accelerating infinitely if they fall off the map.

Sprite collision

Sprite collision is the trickiest part. We move the player horizontally first, check for collisions, and snap the player to the edge of any platform they hit. Then we move vertically and do the same. Doing both axes separately. instead of in one combined move. is what prevents the player from getting stuck inside a platform corner. When the vertical collision happens with positive vel_y (falling), we set on_ground = True so the player can jump again. This same pattern works in every 2D platformer ever written.

Enemy patrol AI

Enemy patrol AI is the simplest possible state machine: walk in self.direction, flip direction when you reach the patrol bounds. Two lines of logic. This same “back and forth” pattern is how Goombas worked in the original Super Mario Bros.

Pygame sprite groups

Pygame sprite groups (pygame.sprite.Group()) handle the bookkeeping. calling group.update() updates every sprite in the group, group.draw(screen) draws them all, and pygame.sprite.spritecollide() does fast collision checks between a sprite and a group. This is what lets the whole game stay under 250 lines.

Customizing Your Game

The base game runs. Now it’s yours to extend. Here are the most rewarding additions, ranked from easiest to hardest:

1. Add real sprite images (30 minutes). Replace the colored rectangles by loading PNG images: self.image = pygame.image.load("assets/mario.png").convert_alpha(). Use convert_alpha() for transparency. Free Mario-style sprites are available on OpenGameArt.org under Creative Commons licenses.

2. Add sound effects (15 minutes). Drop a WAV file in assets/, then: jump_sound = pygame.mixer.Sound("assets/jump.wav") and call jump_sound.play() inside the jump check. Add pygame.mixer.init() right after pygame.init().

3. Power-ups (Super Mushroom) (1-2 hours). Create a Powerup sprite class similar to Coin. On pickup, grow the player rect: self.image = pygame.Surface((30, 60)) and set a self.powered_up = True flag so the player survives one enemy hit before shrinking.

4. Multiple levels (2-3 hours). Move build_level() into a function that takes a level number, store level layouts as lists of tuples or read them from a text file (one character per tile), and trigger a level change when the player reaches a flag sprite at the far right.

5. Scrolling camera (3-4 hours). The harder upgrade. Instead of fixing the player to the screen, fix the camera to the player and offset every sprite’s draw position by the camera offset. This is what makes Mario feel like an endless world instead of a single screen.

6. Stomp-to-defeat enemies (30 minutes). On enemy collision, check if the player was falling AND the player’s bottom was above the enemy’s top last frame. If so, kill the enemy and bounce the player. Otherwise, take damage. This is the classic Mario mechanic.

Common pygame Errors and Fixes

Every pygame project hits the same handful of errors. Here’s how to fix them:

pygame.error: video system not initialized. You called a pygame display function before pygame.init(). Make sure pygame.init() is the first line of main(), before pygame.display.set_mode(). On macOS this also appears if you import pygame inside a non-main thread. keep all pygame calls on the main thread.

pygame.error: No available video device (Linux/WSL). Means there’s no display attached. If you’re on WSL2, install an X server (VcXsrv) and export DISPLAY=:0. On a headless server, you can’t run pygame at all. it needs a real or virtual display.

Sprite collision missing or false positives. Almost always a rect sizing issue. Use print(self.rect) inside update() to confirm the rect matches what you expect. A common bug: creating the Surface at one size and the rect at another. Always use self.image.get_rect(...) so they’re guaranteed to match.

Game runs too fast or too slow. You forgot clock.tick(FPS) inside the loop, so it runs as fast as your CPU allows. Add it back. If frame rate drops below 60, check whether you’re loading images inside the loop (load them once during __init__) and whether your sprite groups have hundreds of off-screen sprites being updated unnecessarily.

ModuleNotFoundError: No module named 'pygame'. You installed pygame but Python is using a different interpreter. Activate your virtualenv (.venv\Scripts\activate on Windows) and run pip install pygame again. Check which python (macOS/Linux) or where python (Windows) to confirm you’re using the venv interpreter.

Player getting stuck inside platforms. You’re moving both axes in one step. Always move horizontally first, resolve X collisions, then move vertically, then resolve Y collisions. The two-pass approach in the Player._collide method above prevents corner sticking.

Beyond Mario: What to Build Next

Once your Mario clone is running, the same pygame skills transfer to a huge range of capstone-friendly projects:

  • Snake. simpler than Mario (no physics), perfect for practicing the game loop pattern
  • Tetris. teaches grid-based logic, rotation matrices, and line-clearing algorithms
  • Pong / Breakout. pure collision physics, great if you want to deepen your math intuition
  • 2D RPG with tile maps. extend the platform/tile system from this tutorial into a top-down RPG with NPCs and dialog
  • Space invaders / scrolling shooter. flip the gravity off and you have a shoot-em-up
  • Card games (Solitaire, Tongits). pygame can handle card-based UIs too, great for a Filipino-themed capstone

When you outgrow pygame. usually when you want 3D, real shaders, or mobile builds. the natural next step is Unity (C#) for serious indie game dev, or Godot (free, open source, GDScript) if you want to stay in a Python-like language. For most BSIT capstones though, pygame is more than enough. It’s free, runs everywhere, and lets you focus on the game logic instead of fighting the framework.

Quick step-by-step summary (click to expand)
  1. Install Python and Pygame. Install Python 3.8 or later, then install Pygame with pip install pygame. Verify with python -m pygame.examples.aliens which should launch a test game.
  2. Download the source code and sprite assets. Download the complete zip from the article. It contains the Python source and the sprite images the game needs.
  3. Extract the files into a project folder. Extract the downloaded zip into a folder of your choice. Keep the sprite folder next to the Python file so relative paths work.
  4. Run the game. Open a terminal in the project folder and run python mario.py. Use the arrow keys to move and spacebar to jump.
  5. Customize levels and sprites. Edit the level layout in the source to add platforms or obstacles. Swap the character sprite by replacing the image file with your own PNG of the same size.

Frequently Asked Questions

How do I make a Mario game in Python?
To make a Mario game in Python, install pygame (pip install pygame), then build four components: a game loop running at 60 FPS, a Player class with horizontal movement and gravity-based jumping, Platform sprites the player can collide with, and Enemy sprites with patrol AI. The full working source code is in this article. about 220 lines total, runnable on Python 3.10 or newer.
What Python library is used to make Mario games?
pygame is the standard library for 2D games in Python and is what almost every Mario clone tutorial uses. It handles graphics, sound, keyboard input, sprite groups, and collision detection. Install it with pip install pygame. Alternatives include arcade (more modern, simpler API) and pyglet (lower-level), but pygame has the largest tutorial ecosystem. important when you’re learning.
Is Python good for making games?
Yes. for 2D games, learning projects, and BSIT capstones, Python with pygame is excellent. It’s free, runs on every OS, has a gentle learning curve, and the same skills transfer to data science and web dev. Python is NOT ideal for high-performance 3D games or mobile titles. for those, use Unity (C#) or Godot. But for a Mario clone, Snake, Tetris, or any 2D capstone project, Python is more than enough.
How long does it take to build a Mario game in Python?
If you copy the source code in this tutorial and tweak it, you can have a working Mario clone in 15-30 minutes. Building it from scratch while understanding every line takes 3-5 hours for someone comfortable with Python. Adding polish (sprite art, sound, multiple levels, scrolling camera) takes another 10-20 hours. Plenty of time for a one-week capstone sprint.
Can I run this Mario game without installing anything?
No. pygame is a Python library that needs Python installed. The good news is the install is small (under 30 MB total) and free. See our Python installation guide if you don’t have Python yet. After that, pip install pygame takes about 30 seconds.
Why is my pygame Mario game running too fast?
You’re missing clock.tick(FPS) inside the game loop. Without it, the loop runs as fast as your CPU allows. usually thousands of frames per second on a modern computer. Create a clock with clock = pygame.time.Clock() outside the loop, then call clock.tick(60) as the first line inside the loop. This caps the game at 60 FPS on every machine.
How do I add sprites and sound to my Mario game?
Replace pygame.Surface((w, h)) with pygame.image.load("assets/mario.png").convert_alpha() to use PNG sprites. For sound, call pygame.mixer.init() right after pygame.init(), then sound = pygame.mixer.Sound("assets/jump.wav") and sound.play() when the player jumps. Free Mario-style sprites and 8-bit sound effects are available on OpenGameArt.org under Creative Commons licenses.
Is this Mario game enough for a BSIT capstone project?
The base version is a good starting point for a capstone. it demonstrates OOP, game loops, physics, and collision. For a full capstone defense, extend it with at least 3 of: multiple levels (read from a text/JSON file), scrolling camera, sprite animations, sound effects, a high-score system that persists to a file, and a main menu screen. With those additions you’ll have a proper 500-800 line project that holds up under a panelist’s questions. See our best Python projects guide for more capstone-ready ideas.
Where can I download the full Mario Python source code?
The full source is in the article above. copy the 6 sections in order into a single mario.py file and it runs immediately. No external files needed. For sprite art and extended versions with sound and multiple levels, check our pygame projects collection for related downloadable projects.

Final Recommendation

A Mario game in Python is the rare capstone project that’s impressive to demo, simple to explain, and rich enough to learn from. The 220-line version above gives you a working platformer in one sitting. The customization roadmap (sprites → sound → levels → scrolling camera) gives you 20+ hours of upgrades when you want to make it capstone-defense-ready.

Whichever direction you take it, don’t get stuck trying to make it perfect on the first pass. Get the base game running today. then iterate. Every great game started as a colored rectangle that could jump on another colored rectangle.

Your next steps:

  1. Copy the source code above into mario.py and run it. see the colored Mario jump on platforms
  2. Pick one customization from the list (sprites or sound is easiest) and add it today
  3. Browse more capstone-ready ideas in our best Python projects with source code roundup
  4. Need the right editor? See our best Python IDE 2026 guide
  5. New to Python? Start with our free Python tutorial series
  6. Looking for more pygame projects? Browse our full pygame projects collection

Built this Mario game and added something cool? Drop a screenshot or your source code link in the comments. we feature the best student builds in our monthly capstone roundup.

I hope this article helps you build your Mario Game in Python successfully. If you have any questions or suggestions about the pygame source code, please feel free to leave a comment below or contact me at our contact page. You can also add me on Facebook at https://www.facebook.com/joken.villanueva for direct help with your Python projects or capstone defense.

Angel Jude Suarez

Full-Stack Developer at PIES IT Solution

Focuses on Python development, machine learning, and AI integration. Has built production AI systems including OpenAI Whisper integration for medical transcription and GPT-4o-powered diagnosis assistance. Strong background in pandas, scikit-learn, and TensorFlow.

Expertise: Python · PHP · Java · VB.NET · ASP.NET · Machine Learning · AI Integration · OpenCV · Django · CodeIgniter  · View all posts by Angel Jude Suarez →

9 thoughts on “Mario Game in Python 2026: Free Source Code + Pygame Tutorial [Copy and Paste]”

  1. There is a error in the Line 29 of the final code “https://itsourcecode.com/free-projects/python-projects/mario-game-in-python-with-source-code/”. Please go through the changes immediately.

Leave a Comment