Back to portfolio

cathedral-painter

A desktop image editor in Python that keeps the interpreter off the hot path.

Work in progress — not yet released 2026

The cathedral-painter editing canvas with a layers panel.
The image viewer in its current state. The tile compositor, ICC color pipeline, and plugin system are built; painting tools are in progress.

Context

Cathedral Painter is a desktop image editor written in Python, with a plugin system and a planned Steam release. Editors in this class are almost always C++ projects, and for good reason. The bet here was that Python could hold up under a real paint workload if the architecture is strict about what the interpreter is allowed to touch.

The hard part

Interactive painting from an interpreted language. Drag a brush across a large canvas and the result has to appear in the same frame; touch those pixels in Python and the UI freezes. So the core rule of the whole codebase is that Python never handles pixels on the hot path. It decides what needs to happen. Native code and the GPU do the work.

Color is the second problem, and it's less forgiving than speed. Wrong color math looks fine right up until a designer opens the file somewhere else. The editor runs an ICC color pipeline so what you paint is what exports.

Approach

The canvas is split into tiles, and an edit only recomposites the tiles it touched, so a brush stroke costs what the stroke covered no matter how big the canvas is. A Skia GPU surface, driven through OpenGL inside the PySide6 UI, draws the result at interactive speed.

Pixel work goes to the libraries built for it: libvips, NumPy, and Pillow handle the processing. Python's actual job is orchestration and UI, which it is good at.

Plugins are plain Python. Extending the editor means writing the same language the editor is written in, not learning a native SDK.

Tradeoffs

Python was the right call. The plugin system alone justifies it: anyone who can write a little Python can write a working filter, and that's the difference between an editor with an ecosystem and an editor with a feature list.

The cost shows up at the edges. Shipping means compiling the whole application with Nuitka rather than asking users to install an interpreter, and compiling a large Qt app to native code is its own project. GPU rendering needs a CPU fallback for machines where OpenGL misbehaves. Every new feature gets checked for whether it sneaks Python onto the hot path, because one careless loop undoes the whole premise.

Where it stands

14.7k lines of Python so far: the tile compositor, the ICC color pipeline, the plugin system, and the PySide6 UI around all of it. The Steamworks SDK is already integrated for the planned Steam release — a product on a store has to install cleanly and start fast on hardware I've never seen.

Built with

Python PySide6 Skia OpenGL libvips NumPy