Skip to main content

Saga2D

A small Python framework for 2D games where the developer writes game logic, not engine plumbing. It renders sprites and simple shapes on the GPU through pyglet, lays out a small UI toolkit, runs a scene stack, hosts online matches and packages standalone builds. Game code never touches the backend.

Three games prove it, each in its own repository: Tribes (turn-based, Polytopia-style), Warband (real-time strategy) and Shardbound (an Eador-inspired campaign). Procedural assets — sound synthesis and a software low-poly renderer — live in sagaforge; the hosted server and website in saga-online.

Install

Requires Python 3.12 or newer. Pin the engine in a game so engine development cannot change that game's build:

uv add 'saga2d==0.3.0'
# or: python -m pip install 'saga2d==0.3.0'

Commit the game's pyproject.toml and lockfile. Upgrade the pin deliberately, then run that game's tests before committing the upgrade. See the release guide for compatibility policy, publishing and testing a local engine checkout.

Develop the engine

uv sync --extra dev
uv run pytest -q          # headless suite on the mock backend

Set SAGA2D_SILENT=1 to keep any pyglet-backed script off the speakers (SAGA2D_HEADLESS=1 implies it and also hides windows).

A complete game

This example needs no image or sound files:

from saga2d import Anchor, Button, Column, Game, Label, Scene


class World(Scene):
    background_color = (18, 20, 30, 255)
    controls = {"escape": "close"}

    def on_enter(self):
        self.gold = 0
        self.ui.add(Column(
            Label(lambda: f"Gold: {self.gold}", text_style="heading"),
            Button("Collect a coin", shortcut="Space", on_click=self.collect, width=220),
            Label("Esc closes the window.", text_style="caption"),
            spacing=16, anchor=Anchor.TOP_LEFT, margin=24,
        ))

    def draw(self):
        self.draw_circle(360, 220, 32, (230, 190, 90, 255))

    def collect(self):
        self.gold += 1

    def close(self):
        self.game.quit()


Game("My Game", resolution=(640, 400)).run(World())

What you get

  • Two coordinate spaces. "world" is transformed by the scene's Camera (pan, zoom, shake) on the GPU; "screen" is for UI. Sprites default to world space; draw_rect, draw_circle, draw_line, draw_polygon, draw_text and draw_image take space=.
  • Measured text. Scene.layout_text(text, width) returns wrapped lines and height; Scene.draw_paragraph draws the same layout. Both accept max_lines, and Scene.fit_text fits a single line with ellipsis. See the text layout cookbook.
  • Hex boards. HexGrid supplies centers, corners, picking, neighbours, weighted movement ranges and shortest paths; the game supplies terrain costs. See the cookbook.
  • Sprites with a logical size. Sprite("name", size=(64, 64)) draws any texture at that size, so procedural textures rendered at the display's pixel density stay crisp on HiDPI screens.
  • Declarative input. controls maps keys and chords to methods; bind_key does the same at runtime; mouse events carry world coordinates and modifier keys.
  • A scene stack with transparent overlays, deferred push/pop, and per-scene ownership of sprites, timers and particle emitters.
  • UI: reactive Label, Button with keycap shortcuts, KeyHints, Panel, Row, Column, ProgressBar, Minimap, anchors, flow layout, wrapped text and a Theme. Text is measured by the backend, so layout fits. See wrapped labels, measuring a tree and button shortcuts. Opt-in keyboard focus, custom activation and pointer capture work across the same tree; blocks_pointer=True keeps HUD clicks out of the world. See focus and pointer handling.
  • Actions, tweens, timers, particle emitters, frame animation, transient effects, audio with master/music/sfx channels and a silent driver, JSON save slots and persisted settings.
  • Multiplayer. MatchHost/MatchClient for LAN, OnlineClient and the shared MatchMenu/MatchLobby for hosted rooms, and saga2d.server, the authoritative room server any game registers with through a GameSpec. Follow the counter-room tutorial and the transport notes.
  • Packaging. saga2d.packaging builds, verifies and installs standalone games with PyInstaller from a ten-line tools/package.py. Name a square 1024 px picture as GamePackage(icon=...) and the Windows executable, its installer and the Mac bundle carry it in each platform's shape; without one they carry the engine's mark.
  • Testing. A mock backend that records every draw call, render_scene for offscreen screenshots you can look at, a CPU budget for long checks, paced native frames, and a counter test game for the server.

See DESIGN.md for the architecture and the reasoning behind the cuts. The September 2026 engine review prioritizes game extractions and proposed improvements beyond the current genres. The engine backlog tracks actionable tasks, priorities and completion criteria, including rendering performance and UI clipping.

Release files for saga2d 0.3.14

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for saga2d 0.3.14
File Size Uploaded
saga2d-0.3.14.tar.gz 680.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for saga2d 0.3.14
File Interpreter ABI Platform
saga2d-0.3.14-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / saga2d-0.3.14.tar.gz

Download URL saga2d-0.3.14.tar.gz
Size 680.8 kB
Tags Source
SHA-256 checksum
How to use checksums
655d4837918a2a769c6e0abb383a35be7b823eac573a9c679a5962e48dabedea
BLAKE2b-256 checksum
How to use checksums
0d3baeb3b8b19c8000e30fff7cdf4b10fa98f3f8af1d38e1dfe9c6a42365d1de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.10

Release files / saga2d-0.3.14-py3-none-any.whl

Download URL saga2d-0.3.14-py3-none-any.whl
Size 525.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cf5ba375802a6374df1ea580228ab2f2d879c2c87b187f0dfeb49f670efd040f
BLAKE2b-256 checksum
How to use checksums
d1f0085920d86c80fdf7bea4e83b8511bf1daf8300d1bf604d3e3c7a6e4a49df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.10

Release history Release notifications | RSS feed

This release

0.3.14 This release

2 release files

0.3.13

2 release files

0.3.12

2 release files

0.3.11

2 release files

0.3.10

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page