porta
porta is two things:
- A DSL for concisely defining a relational floorplan; and
- A software package for deterministically converting a floorplan into a clean SVG map.
room hall "Great Hall" 40x20 root
room parlour "Parlour" 20x20 left-of hall
room kitchen "Kitchen" 20x20 right-of hall
room study "Study" ?x20 down-of parlour
The porta DSL
porta floorplans are defined in .porta files adhering to a strict specification.
Each room is defined in a single line relative to one or more anchor rooms,
forming a dependency graph all the way back to the initial root room. This model
creates a 1-to-1 correspondence between the floorplan and the final SVG map it
produces, allowing porta to rapidly and deterministically generate one from
the other.
Files are read line by line: a # starts a comment that runs to the end of the
line (a # inside a quoted name is literal), and blank lines are ignored.
Any statement can be continued onto the next physical line by ending it with a backslash, separated from the last token by whitespace:
room pn "" 40x10 root
room pw "" 10x20 down-of pn
room pe "" 10x20 down-of pn align=end
room ps "" 40x10 down-of pw \
down-of pe
block passages "Passages" glyph=pn \
pn pw \
pe ps
Continued lines parse as one statement, and errors still point at the physical line of the offending token. A backslash anywhere else — mid-line, glued to a token, or followed by a comment — is an error, as is a blank (or comment-only) line inside a continued statement.
For more on the porta DSL specification, see the following documentation:
- Rooms — the
roomstatement, room positioning, auto-dimensions (?), disconnected components, and the layout resolution procedure. - Windows — interior and exterior windows, widths and offsets.
- Doors — default doors & how to modify them, and
the
doorstatement for explicitly adding non-default doors. - Blocks — the
blockstatement for joining multiplerooms into a single non-rectangular space. - Dividers — the
dividerstatement for drawing a dividing line between two members of a block. - Links — the
linkstatement for joining disconnected components into one continuous plan. - Stairs — the
stairsstatement for flights leading up or down off a floor, or between levels within one.
The porta tool
Once a valid floorplan has been written, porta draw converts it into an
SVG map in a two-step process. First, the dependency graph is traversed
and the relations in the floorplan are converted into a coordinate system.
Second, that coordinate system is used to deterministically generate an
SVG file.
porta draw plan.porta -o plan.svg
The solved coordinate system can also be viewed and debugged directly as an ASCII grid:
porta draw plan.porta --debug-ascii
A plan that can't be solved (due to overlaps, missing anchors, gaps between a room and its anchor, etc) will fail and raise an error.
Installation
porta requires Python 3.14 or newer and is published on PyPI:
pip install porta
Alternatively, install it from a checkout of this repo. Add the [dev] extra
to pull in the test and lint tools as well:
pip install -e . # editable install
pip install -e ".[dev]" # ... plus pytest, ruff, and mypy
For development with uv, set up the environment from the repository root:
uv sync --extra dev
Why porta?
porta is built to be:
- Text-based — a plan is plain text: editable in any editor, diffable, and version-controlled alongside your campaign notes. No WYSIWYG, no binary files.
- Concise — a terse syntax with strong defaults; a whole plan is a handful of short lines.
- Deterministic — no procedural generation and no randomness; the same plan always renders the same map.
- Relational — rooms are placed against other rooms, never drawn by hand or given raw coordinates.
- Spatial — the output is a flush, to-scale floor plan in tabletop conventions, not a flowchart or connectivity graph.
- Fast — placement is a single pass over a dependency graph, with no constraint solver to run.
- AI-friendly — terse, text-only, and predictable, so a language model can read and write plans reliably (this one falls out of the rest).
No existing tool hits all of these:
- Procedural and AI generators (Watabou, Inkarnate) aren't deterministic or controllable — they invent a layout instead of rendering yours.
- GUI map editors (Dungeondraft, Dungeon Scrawl) aren't text: mouse-driven, binary, and kept apart from your notes.
- Diagram tools (Mermaid, Graphviz, D2) give connectivity, not a spatial, to-scale map.
- Hand-written SVG is text, but neither concise nor relational — verbose and easy to get wrong.
Metadata
Release files for porta 1.7.0
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 | |
|---|---|---|---|---|
| porta-1.7.0.tar.gz | 141.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| porta-1.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 184.4 kB
Release files / porta-1.7.0.tar.gz
| Download URL | porta-1.7.0.tar.gz |
|---|---|
| Size | 141.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ae8fb184c951fa79e138d2f074c4cb86aeca0e77ea63bb4d564d848054ffbb84
|
|
BLAKE2b-256 checksum How to use checksums |
6583a3fa326715325a43b8557aa194822c0c9005ae89ae4a9815b25e1bdd2ebd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / porta-1.7.0-py3-none-any.whl
| Download URL | porta-1.7.0-py3-none-any.whl |
|---|---|
| Size | 42.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df274d89a7f87129bcb4c3bcee7b4f9fb75215eecddf629eacfff5e78f8432bf
|
|
BLAKE2b-256 checksum How to use checksums |
e79231b71105054d3882a00a2d85a340ecd22b4b7238bf778a4f0f207d163f88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for porta 1.7.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.