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'sCamera(pan, zoom, shake) on the GPU;"screen"is for UI. Sprites default to world space;draw_rect,draw_circle,draw_line,draw_polygon,draw_textanddraw_imagetakespace=. - Measured text.
Scene.layout_text(text, width)returns wrapped lines and height;Scene.draw_paragraphdraws the same layout. Both acceptmax_lines, andScene.fit_textfits a single line with ellipsis. See the text layout cookbook. - Hex boards.
HexGridsupplies 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.
controlsmaps keys and chords to methods;bind_keydoes 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,Buttonwith keycap shortcuts,KeyHints,Panel,Row,Column,ProgressBar,Minimap, anchors, flow layout, wrapped text and aTheme. 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=Truekeeps HUD clicks out of the world. See focus and pointer handling. - Actions, tweens, timers, particle emitters, frame animation, transient
effects, audio with
master/music/sfxchannels and a silent driver, JSON save slots and persisted settings. - Multiplayer.
MatchHost/MatchClientfor LAN,OnlineClientand the sharedMatchMenu/MatchLobbyfor hosted rooms, andsaga2d.server, the authoritative room server any game registers with through aGameSpec. Follow the counter-room tutorial and the transport notes. - Packaging.
saga2d.packagingbuilds, verifies and installs standalone games with PyInstaller from a ten-linetools/package.py. Name a square 1024 px picture asGamePackage(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_scenefor 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)
| File | Size | Uploaded | |
|---|---|---|---|
| saga2d-0.3.14.tar.gz | 680.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|