PAGAN — Pixel Art Grammar And Notation
PAGAN is a translator between AI and pixel art. It defines a small text notation for pixel art — palette-keyed grids written in YAML — that an AI agent can write fluently, and a Python tool that reads the same notation and turns it into PNGs, sprite sheets, and game-engine-ready assets.
frames:
idle_0: |
.....000000.....
....05566650....
...0556776650...
Status: early development, but the full pipeline works: palettes, 2D sprites and tilesets, 3D voxel models rendered from 8 headings or exported as real meshes (glTF) for 3D games, previews, lint, import, and builds into Godot 4. See CHANGELOG.md and the issues.
Docs: install, a tutorial, and references generated from the code:
every notation field, pagan.toml settings and
lint rules. The full notation with examples is the skill's
docs topics (pagan docs).
Install
pip install pagan-gen # the package is pagan-gen on PyPI; the command and the import are `pagan`
pagan --version
Install (development)
Requires Python 3.11+.
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pagan --version
pytest
Quick start
pagan init my-art # creates pagan.toml, STYLE.md, palettes/, sprites/, tiles/
cd my-art
pagan new palette dusk # scaffolds palettes/dusk.pal.yaml; then set default_palette = "dusk" in pagan.toml
pagan palette show # renders swatches to previews/palettes/*.png
pagan new sprite potion --size 16x16 --description "red health potion" # 2D: edit the grid
pagan new model ship --size 96x96 --directions 8 # 3D: shapes + materials
pagan lint # format errors + style warnings
pagan preview potion # upscaled sheet, frames and GIFs in previews/potion/
pagan gallery # every asset side by side
pagan build # sheets + manifest (+ Godot SpriteFrames) into output_dir
Every command accepts --json for machine-readable output and exits non-zero on errors.
See examples/demo/ for an example project (palettes, a potion, a slime, meadow tiles and an
8-heading 3D ship) and examples/godot/ for the Godot project it builds into: open it in Godot 4.7 and
press Play.
Using the output in a game
pagan build writes PNG sheets and manifest.json (frame rects, animations, pivots, tile coordinates)
for any engine. With export = ["png", "godot"] it also writes Godot 4 resources:
-
Sprites and 3D models:
<name>.tresSpriteFrames. Godot resources can't hold the pivot, so apply it on the node, using the asset'sgodot_offsetfrom the manifest:$AnimatedSprite2D.sprite_frames = load("res://assets/generated/sprites/ship.tres") $AnimatedSprite2D.centered = true $AnimatedSprite2D.offset = Vector2(0.0, -12.0) # manifest: assets.ship.godot_offset $AnimatedSprite2D.play("sail_ne") # 3D models: one animation per heading
-
Tilesets:
tiles/<name>.tresis a TileSet with every tile in atlas source 0; the manifest'stilesgives each tile name's atlas coordinates. Terrains, collision and other tile data are set up in Godot.
Isometric tile games: the dimetric camera
For a 2:1 isometric TileMap (e.g. Godot's TileMapLayer, 64x32 cells), set the exact pixel dimetric camera:
[camera]
projection = "dimetric" # default "orthographic" (any elevation, `scale` px per voxel)
tile = [64, 32] # pixels per grid cell: the floor diamond
cell = 16 # voxels per grid cell side
z_step = 0 # pixels per voxel of height; 0 = tile height / cell (2 here)
A cell x cell floor then renders to the hand-drawn 64x32 diamond (rows 2, 6, ... 62, 62, ... 6, 2) and
tiles at the grid pitch with no gaps or overlap; walls and furniture share that scale, and a 1-voxel move is
always whole pixels (2 across and 1 down along x/y, z_step up). Models use heading: 45 with directions: 4
(frames ne, nw, sw, se); other headings can't land on whole pixels and are an error. Meshes are unaffected.
3D games: meshes
For a free 3D camera, add "mesh" to export: every 3D model also builds models/<name>.glb (binary glTF:
one node per part, animations, part metadata) plus a palette ramp texture, and with "godot" a
models/<name>.tscn with PAGAN's palette-locked ramp shader applied. Pick the project's texel density with
pagan config set mesh.voxel_size 0.0625 (metres per voxel). Models face +Z, +Y is up, the model's origin
is the mesh origin; rig: in a model sets part pivots, parents and metadata (hit zones, detachable parts).
var soldier: Node3D = load("res://assets/generated/models/soldier.tscn").instantiate() # manifest: mesh.godot_scene
add_child(soldier)
soldier.look_at(target, Vector3.UP, true) # true: the model's front (+Z) faces the target
soldier.get_node("AnimationPlayer").play("walk") # looping animations loop
var helmet: Node3D = soldier.get_node("body/torso/head/helmet") # manifest: mesh.parts.helmet.node
if helmet.get_meta("extras").get("detachable", false): # rig meta -> node metadata "extras"
helmet.reparent(get_tree().current_scene) # knock it off: keeps its place in the world
Light the scene with a DirectionalLight3D (shadows on) and use the Linear tonemapper: the ramp shader then
keeps every lit pixel a palette colour (Forward+ and Compatibility renderers; Mobile can be a few levels off in
dark colours). Big repeating surfaces (ground, walls) are tileset textures:
export_textures writes repeatable PNGs and triplanar Godot materials. Review models with
pagan preview <model> --orbit (all headings, from below too), --closeup <part> and --engine.
examples/godot/scenes/mesh_test.tscn shows the demo meshes on turntables under a moving sun.
Using PAGAN with Claude
PAGAN ships a Claude skill that teaches an agent the whole workflow: setting up a game's art folder, asking the right questions, drawing/modelling, looking at previews, and changing the look over time.
pagan skill install # copies it to <repo>/.claude/skills/pagan/
Then just ask: "I'm making a naval exploration game; make the player's ship, three masts, 8 headings."
Using PAGAN from any agent (MCP)
pagan mcp runs PAGAN as an MCP server, so any MCP client (Claude
Desktop, Claude Code, Cursor, your own agent) can make, lint, preview and build art for one project. Tools
return the same JSON as the CLI's --json, and previews come back as images the model can look at.
claude mcp add pagan -- uvx pagan-gen mcp --project /path/to/art # no install needed (uv); or `pagan mcp` after pip install
Other clients take the same command in their config:
{ "mcpServers": { "pagan": { "command": "uvx", "args": ["pagan-gen", "mcp", "--project", "/path/to/art"] } } }
PAGAN is listed in the MCP Registry as io.github.lukasgolino/pagan.
Add --allow-godot to let the build tool verify output in a headless Godot. Then ask: "make a 16x16
health potion", "give the zombie an attack animation", "make the whole game a bit darker".
License
Apache 2.0 — see LICENSE.
Metadata
Release files for pagan-gen 0.22.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pagan_gen-0.22.1.tar.gz | 346.5 kB | With provenance | Details for pagan_gen-0.22.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pagan_gen-0.22.1-py3-none-any.whl | Python 3 | none | any | Details for pagan_gen-0.22.1-py3-none-any.whl |
Total release size: 625.3 kB
Release files / pagan_gen-0.22.1.tar.gz
| Download URL | pagan_gen-0.22.1.tar.gz |
|---|---|
| Size | 346.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4b4fe4590a9925742f8890decfef135062d36e23144a00ad2f153a34a3960055
|
|
BLAKE2b-256 checksum How to use checksums |
00e8af388fb423123d95016883b4fdf13e58fd8fb6c0e00f5578ca1e8a787395
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logRelease files / pagan_gen-0.22.1-py3-none-any.whl
| Download URL | pagan_gen-0.22.1-py3-none-any.whl |
|---|---|
| Size | 278.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3d0dfe41130606297d75d64230d601f692e590f1f8ff22bd485bf531a2b863bf
|
|
BLAKE2b-256 checksum How to use checksums |
6d04d040570f7459bf6c1183eca8bf801c7899a92a4189cbd45570d5ab053ca5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logSecurity for pagan-gen 0.22.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for pagan_gen-0.22.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.