Installation

The full builds are served from the project’s own index. Either spelling works:

pip install --pre simvx --extra-index-url https://pypi.simvx.com/
uv pip install --pre simvx --extra-index-url https://pypi.simvx.com/

# ...and with the example library beside it
pip install --pre "simvx[examples]" --extra-index-url https://pypi.simvx.com/

The simvx name on public PyPI is a placeholder that reserves the name; it installs no code, and its simvx-preflight script checks the environment and points here. Installing from source (below) remains fully supported.

Requirements

System Requirements

  • Python 3.14+

  • Vulkan 1.2+ capable GPU and drivers

  • glslc shader compiler, optional (from the Vulkan SDK): only needed to recompile shaders; the compiler otherwise ships via uv sync

  • One windowing library: GLFW3, SDL3, or PySide6 (any one works: see Windowing Backends)

  • uv: workspace packages resolve only through uv run

Install uv if you don’t have it:

curl -LsSf https://astral.sh/uv/install.sh | sh

Per-Platform Setup

Arch / Manjaro

sudo pacman -S vulkan-devel glfw shaderc

Ubuntu / Debian

sudo apt install libvulkan-dev libglfw3-dev glslc

macOS

Install the Vulkan SDK (includes MoltenVK and glslc), then:

brew install glfw

Clone & Install

git clone https://git.simvx.com/simvx/simvx.git
cd simvx

# Install all workspace packages (editable) into the project venv
uv sync

# Optional: pull in dev tools (pytest, ruff, mypy, black) and/or doc build deps
uv sync --group dev --group docs

To install only a subset of packages editable (e.g. when working on a single package), use the explicit form instead:

uv pip install -e packages/core -e packages/graphics

The two dependency groups defined in the workspace pyproject.toml:

Group

Adds

When you need it

dev

pytest, pytest-cov, mypy, ruff, black

running the test suite or pre-commit checks

docs

sphinx, myst-parser, sphinx-autodoc2, sphinx-design, sphinx-sitemap, furo

building the documentation locally

Both are optional: you can use SimVX without either.

The example library

The examples are a distribution of their own, simvx-examples, and they install as an extra of the engine:

pip install --pre "simvx[examples]" --extra-index-url https://pypi.simvx.com/

The index is not optional here either: the simvx name on public PyPI declares no extras, so a bare pip install simvx[examples] warns that the extra does not exist and installs nothing. pip install simvx-examples on its own is not an equivalent spelling either. That distribution deliberately declares no dependencies, so alone it gives you neither the engine the examples import nor the simvx command, which belongs to simvx-core.

With it installed, simvx examples browses, runs and copies the library:

simvx examples list                       # every installed example, grouped by tier
simvx examples list --tier tutorials
simvx examples list physics               # search id, title, description and tags
simvx examples path features_2d_light     # print the file, for your own tooling
simvx examples run tutorials_pong
simvx examples run tutorials_pong -- --test
simvx examples fork tutorials_pong --to ~/games/my-pong

fork is the one that matters. It copies the example, and everything beside it, into a directory you own, with the LICENSE alongside. Editing the library inside site-packages is not the way: it is version-managed, replaced on upgrade, and invisible to your own version control.

SIMVX_EXAMPLES_ROOT overrides where the library is read from. That is how a Linux distribution packages the tree somewhere its own policy prefers, and it is also how you point the commands at a checkout:

SIMVX_EXAMPLES_ROOT=~/src/simvx/examples simvx examples list

From a checkout none of this is needed: the tree is already there, and each example’s docstring carries the direct command.

uv run python examples/tutorials/pong/main.py

Inside the repository, uv sync --extra examples is what puts the distribution itself into the project venv the first time. There is no editable install of it, and a plain re-sync will not notice that you edited it: after changing anything under examples/_package/, rebuild and reinstall it explicitly.

uv sync --extra examples --reinstall-package simvx-examples

The fourth tier in the repository, examples/ports/, is not part of the distribution and cannot be: each port re-implements a third-party game and is licensed individually against the original. Read those in a checkout, or on the gallery.

Native audio extension

Audio playback uses a small C extension (ma_engine) compiled at install time by a PEP 517 build hook (packages/core/build_audio_ext_hook.py). Three install modes control how the build is treated:

Env var

Behaviour at install

Behaviour at runtime

(default)

Tries to compile. On failure (no C compiler, etc.) the install succeeds and prints a stderr WARNING.

If the .so is missing, falls back to the legacy mixer with a one-time WARNING: higher latency and no 3D positional audio.

SIMVX_SKIP_AUDIO_BUILD=1

Skips the compile step entirely. Quiet.

Same legacy fallback with WARNING.

SIMVX_REQUIRE_AUDIO_NATIVE=1

Fails the install if the compile fails. For CI / production pipelines.

Native required: runtime can’t fall back if the file was never built.

# Default (best-effort native build)
uv pip install -e packages/core

# Skip the build, accept the legacy mixer
SIMVX_SKIP_AUDIO_BUILD=1 uv pip install -e packages/core

# Hard require: fail loud on missing compiler
SIMVX_REQUIRE_AUDIO_NATIVE=1 uv pip install -e packages/core

Two runtime env vars control the fallback policy:

Env var

Effect

(default)

Native → legacy (with WARNING) → null (with WARNING).

SIMVX_ALLOW_LEGACY_AUDIO=0

Native or AudioBackendUnavailable. No silent degradation.

What the legacy mixer costs

The fallback is not simply a slower version of the same thing:

Native (ma_engine)

Legacy mixer

Latency

20 ms

100 ms

Mixing runs in

C, on miniaudio’s own thread

Python and NumPy, on the audio thread under the GIL

3D positional audio

listener position, direction and Doppler applied

no spatializer: the calls are accepted and discarded

Bus routing, gain, effects

yes

yes

The latency follows from where the mixing happens. The legacy path drives miniaudio.PlaybackDevice from the miniaudio distribution, which wraps the same C library as the native path but exposes only its device and decoder layer, never its engine. Mixing therefore lands back in Python on the audio thread, where a heavy frame on the main thread can stall it, so the buffer has to be large enough to survive that.

The spatializer is the part worth noticing: a game built on 3D positional audio does not sound late on this path, it sounds wrong.

Getting the native extension

From a source checkout, build it in place:

uv run --with setuptools simvx build-audio

Installed from an index, reinstall from the sdist with a C compiler present. The wheels carry the CFFI glue but not the vendored miniaudio source, so an in-place rebuild has nothing to compile:

pip install --force-reinstall --no-binary simvx-core simvx-core

Verify Installation

Check the Vulkan driver:

vulkaninfo --summary

Import-check the packages:

from simvx.core import Node, SceneTree, Vec3, Property
from simvx.graphics import App

Run the core test suite:

uv run --package simvx-core pytest

Next