# ursina: a Python game engine built on Panda3D with a one line import

> Ursina wraps Panda3D in a Python API small enough that a moving box is nine lines, and ships a 3D level editor, prefabs and asset converters in the same package. It is aimed at teaching and prototyping rather than at a shipped commercial game.

**pokepetter/ursina** — A 3D and 2D game engine for Python

- Repository: https://github.com/pokepetter/ursina
- Website: https://pokepetter.github.io/ursina/
- Stars: 2,592 · Forks: 356
- Language: Python
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/pokepetter-ursina

## Nine lines to a window with a box in it

The README's example is the pitch. One import, one application object, one entity, one call to run:

```python
from ursina import *

app = Ursina()
ground = Entity(
    model = 'cube',
    color = color.magenta,
    z = -.1,
    y = -3,
    origin = (0, .5),
    scale = (50, 1, 10),
    collider = 'box',
    )

app.run()
```

Every one of those keyword arguments carries weight. `model = 'cube'` looks a model up by name in the built-in asset set. `origin = (0, .5)` sets the pivot, so the object sits on the ground rather than through it. `collider = 'box'` attaches a collision volume, which is what turns a decorative cube into something a character can stand on. The single star import is doing a lot of work, pulling in `Entity`, `Ursina`, the colour module and the input state all at once.

`app.run()` opens the window and starts the loop. That is the entire lifecycle: construct, add entities, run. The next example in the README adds the one thing that turns a scene into a game, an `update` function the engine calls automatically, plus `held_keys` for input:

```python
def update():
    player.x += held_keys['d'] * .1
    player.x -= held_keys['a'] * .1
```

There is no fixed timestep to manage, no scene manager to wire up, and no loop to write. The engine calls you.

## Installing from PyPI, from GitHub, or editable

The README gives three install paths, and the difference is whether you want a released version, the development head, or an editable checkout.

```bash
pip install ursina
pip install ursina[extras]
```

The first is the plain install. The extras form pulls in the optional packages listed separately, which the README calls imageio, numpy, psd-tools, blender, pyperclip and pillow, though the exact membership has shifted between versions. The README also warns that on some systems you need `pip3` instead of `pip` to reach Python 3 rather than Python 2, and that you can target a specific version with `python3.xx -m pip install ursina`.

The development and editable routes are for working on the engine rather than using it:

```bash
pip install git+https://github.com/pokepetter/ursina.git
git clone https://github.com/pokepetter/ursina.git
cd ursina
pip install --editable .
```

The README recommends the clone plus `--editable` route if you want to edit the source, and notes that git has to be installed for it. An editable install is the right choice when you intend to add a prefab or change a shader, because your edits take effect without reinstalling.

The runtime dependencies are listed in the README as Python 3.10 or newer, panda3d, pillow for texture manipulation, psd-tools for converting .psd files, blender for converting .blend files, and pyperclip for copy and paste. `pyproject.toml` gives the machine-readable version of that list: `panda3d>=1.10.15`, `panda3d-gltf>=1.3.0`, `pillow>=12.0.0`, `pyperclip>=1.11.0` and `screeninfo>=0.8.1`, with `requires-python = ">=3.12"`.

That last line is a genuine conflict, and you should notice it. The README says Python 3.10 or newer; the packaging metadata requires 3.12 or newer. The README also names psd-tools and blender as dependencies, while `pyproject.toml` puts psd-tools aside and lists an optional group named `extra` containing imageio, numpy, psutil, thumbhash-python and tripy. The safe reading is that the packaging file is authoritative and the README's dependency prose is out of date.

## Assets ship with the package, which is the design decision

The README's project structure listing describes `ursina/` as the actual module and then names its subdirectories in plain language: `audio` for built-in audio clips, `editor` for the 3D level editor, `fonts` for built-in fonts, `models` for .blend files and procedural classes such as Cylinder, Quad and Terrain, `models_compressed` for .blend files converted to .ursinamesh, and `prefabs` for higher level classes like Draggable, Slider and Sprite.

Then there is a list of base modules: `__init__.py`, `application.py`, `audio.py`, and a comment saying the rest cover Entity, input_handler, Text and window. `pyproject.toml` confirms the packaging intent with `include-package-data = false` and a table of package-data entries that ship `ursina.audio`, `ursina.fonts`, `ursina.models.procedural`, `ursina.models_compressed`, `ursina.prefabs.particle_systems`, `ursina.scripts`, `ursina.textures`, `ursina.textures.items` and `ursina.shaders.screenspace_shaders`.

So the engine installs its own art. That is what makes `model = 'cube'` resolve to something on a machine that has never opened a 3D tool, and it is the single biggest difference from a general-purpose engine, where a blank scene is genuinely blank.

The `models_compressed` directory has a separate name because `.blend` files are large and slow to load, so they are pre-converted to a `.ursinamesh` format and shipped alongside. That conversion step is a decision someone made about load time, and it also tells you what happens when you bring your own asset: you will be looking for the same treatment, and the README's list of blender as a dependency exists to convert .blend files.

## A level editor in the box, and prefabs as the reuse unit

Two features separate ursina from a thin wrapper. The first is `ursina/editor`, a 3D level editor that ships inside the package. For a Python-first audience this is the feature that removes the usual excuse, which is that you cannot lay out a level without leaving your editor to write scene files by hand. Having an in-package editor means the loop stays inside one language.

The second is `prefabs`, which the README describes as higher level classes like Draggable, Slider and Sprite. A prefab in this engine is a Python class that builds itself, so reuse happens at the level of code rather than at the level of a scene file you copy around. `Draggable` in particular is the kind of behaviour people write by hand in every project: an entity that follows the mouse. Here it arrives as a class.

What the engine does not include is more consequential. There is no animation system described, no physics beyond the collider shapes, no audio mixing, no scene transitions, no save format beyond the editor, and no asset pipeline for importing a real game. Those are exactly the pieces a commercial project would have to build, and the absence tells you the audience. The README's own examples are a Minecraft clone and a platformer game, both built in the documentation pages, and the `samples/` directory plus the samples website hold small games. That is a teaching and prototyping library that happens to be able to ship a small game.

The documentation is generated from source comments. `docs/cheat_sheet.html` is described as auto-generated by a `documentation_generator.py`, and `tutorial_generator.py` turns .py files into .txt files that become .html, extracting comments as descriptions and the following code as blocks. So the prose in the tutorials and the code cannot drift apart.

## Versioning, Python floor, and what the repository does not say

GitHub reports the repository as Python, MIT licensed, not archived, last pushed 2026-09-19, on the `master` branch, with topics for 3d game development, game development and game engine, and the homepage at pokepetter.github.io/ursina. There are no GitHub releases at all, which is the most significant fact here.

No releases means no changelog and no tags to pin, so `pip install ursina` gives you whatever PyPI currently holds, and the only version marker in the repository is `version = "8.2.0"` in `pyproject.toml`. A library with a major version of 8 and no published release history is worth thinking about before you depend on it: you cannot read what changed, and you cannot easily return to a known state.

The Python floor is the second thing to check, given the 3.10 versus 3.12 conflict above. If you are on Python 3.11 you will find the README inviting you and the packaging metadata refusing you, which is a confusing failure rather than a clean one.

The repository tree is small and legible: `ursina/`, `samples/`, `tests/`, `docs/`, `pyproject.toml`, `CONTRIBUTING.md`, `FUNDING.yml`, `index.html` and `project_structure.txt`. There is a `tests/` directory but nothing in the README describes how tests are run, and the development dependency group in `pyproject.toml` names ruff and sswg, the latter being the static site generator the docs pipeline uses.

The README closes the way many personal open source projects close, with issues for bugs and pull requests for fixes, a Discord, a Twitter account, a Patreon and GitHub Sponsors. That funding route is a fair signal about what support you get, and it is also a reason to read the code rather than expect a support contract.

## Against Pygame, Godot through a bridge, and Panda3D directly

The comparison with plain Panda3D is the most direct, because ursina is a layer on top of it. Panda3D gives you a full 3D engine with a large task system, a scene graph and asset tools; ursina gives you a Pythonic API, a prefab library, a level editor and pre-converted models. What you lose by using ursina is access to the layers underneath when you need them, and what you gain is not having to learn Panda3D's own conventions first.

Against Pygame the difference is dimension and abstraction. Pygame is 2D, immediately readable, and has an enormous tutorial ecosystem. Ursina is 3D with a scene graph, so the concepts you have to hold in your head are more numerous, but the payoff is a game with actual depth.

Against Godot, the honest position is that they are not competing for the same work. Godot is a full editor and engine with a real asset pipeline, animation, physics and an export chain for shipping. Ursina is a library you drive from a Python file, with no editor beyond its built-in level editor and no shipping pipeline. If the goal is a released game with art, audio and a build for a platform, Godot is the tool and ursina is not.

What ursina does have is a specificity: it is the fastest route from a Python beginner to a 3D scene that moves. That is a real use, and its documentation is built around exactly that path.

## Conclusion

ursina is a good fit for learning 3D game programming, for prototypes, and for anyone who wants a 3D window without writing a render loop. It is a poor fit for a commercial title that needs a proven asset pipeline, because it ships almost none. GitHub reports the last push on 2026-09-19 and the packaging file names version 8.2.0, so pin a release before you build on it. Start from `samples/platformer.py`, which shows the parts an empty scene never does.

## FAQ

### What is Ursina and what is it built on?

Ursina is an easy to use game engine and framework for Python, wrapping Panda3D. `pyproject.toml` names version 8.2.0 and requires `panda3d>=1.10.15`, `panda3d-gltf>=1.3.0`, `pillow>=12.0.0`, `pyperclip>=1.11.0` and `screeninfo>=0.8.1`. It is MIT licensed and the last push was 2026-09-19.

### How do I install Ursina for Python?

Use `pip install ursina` for the released version, or `pip install ursina[extras]` to pull the optional dependencies. The README suggests `pip3` on systems where `pip` still resolves to Python 2, and `python3.xx -m pip install ursina` to target a specific version. Be aware that `pyproject.toml` requires Python 3.12 or newer even though the README says 3.10 or newer.

### Do I need Blender or Photoshop to make assets for Ursina?

Not to get started. Built-in models, textures, fonts and audio ship inside the package, which is why `model = 'cube'` resolves on a fresh install, and `.blend` files are pre-converted to a `.ursinamesh` format in `models_compressed` for faster loading. The README does list blender and psd-tools as dependencies for converting `.blend` and `.psd` files, so they matter once you bring your own art.

## Sources

- [Issues](https://github.com/pokepetter/ursina/issues)
- [License: MIT](https://github.com/pokepetter/ursina/blob/master/LICENSE)
- [pokepetter/ursina on GitHub](https://github.com/pokepetter/ursina)
- [Project website](https://pokepetter.github.io/ursina/)
- [README](https://github.com/pokepetter/ursina/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pokepetter-ursina
