preview (coming soon) — Python 3.14+ · Vulkan 1.2+

SIMVX

A full-featured game engine written in Python.
Vulkan rendering, a visual editor, and an integrated IDE.

The editor and IDE are built using the engine itself — everything you see runs through one Python codebase.

Open Editorbeta Browse Examples Ask the AI →
Showcase
See the engine in action

Every clip below, from pixel-art 2D to PBR-lit 3D to the visual editor, runs on the same Vulkan/WebGPU renderer, all driven from Python. Code and live demos in Examples.

bouncing_ball.py — complete app
from simvx.core import Node2D, Property, Vec2
from simvx.graphics import App

class Ball(Node2D):
    speed    = Property(280.0)        # editor-visible
    radius   = Property(28.0)
    velocity = Vec2(1, 0.7)

    def on_process(self, dt):
        self.position += self.velocity * self.speed * dt
        if not self.radius < self.position.x < 800 - self.radius:
            self.velocity.x *= -1
        if not self.radius < self.position.y < 600 - self.radius:
            self.velocity.y *= -1

    def on_draw(self, r):
        r.draw_circle(self.position, self.radius,
                       colour=(0, 1, 0.6, 1))

App("Hello SimVX", 800, 600).run(
    Ball(position=Vec2(400, 300)))
Engine
Built in Python, for Python developers

No custom scripting language. No framework lock-in. Write your project in the same language that powers the engine, with full access to PyPI and your existing toolchain.

🐍

Pure Python

No custom scripting language. Write game logic in pure Python with full access to PyPI, pdb, static analysis, and your existing tools.

Vulkan & WebGPU

GPU-driven forward renderer with multi-draw indirect, PBR materials, SSAO, IBL, shadows, and post-processing — Vulkan on desktop, WebGPU in the browser, same scene code.

🌳

Scene Tree, 2D + 3D

Node hierarchy with signals, groups, and coroutines. Sprites, tilemaps, PBR meshes, skeletal animation, particles, physics, and pathfinding — same API in both dimensions.

🛠

Editor & IDE

Viewport, inspector, gizmos, play mode — plus an integrated code editor with LSP diagnostics, DAP debugging, and a terminal. Both built on the engine's own UI widgets.

📤

Web Export

Ship your game as a single standalone HTML file. Runs on Pyodide with WebGPU under the hood — the same scene code as the desktop Vulkan build, from sprites and UI to PBR, shadows, post-processing, and reflection probes.

🌐

Cross-Platform

Linux, macOS (MoltenVK), and Windows on Vulkan; browsers via WebGPU. Choose your windowing backend: GLFW3, SDL3, or PySide6 — same engine on all of them.

Testing & Automation
Built for fast iteration

Every part of the engine is scriptable. Render headlessly, replay input deterministically, walk the scene tree, and assert anywhere — same loop for CI, regression tests, and AI-assisted debugging. Or let an LLM agent loose to play-test your game, find bugs, and autonomously iterate.

Write game
Replay script
Headless render
Inspect frame + scene
Assert or feed to LLM

Session Replay CI

Record and replay both game and editor sessions deterministically. DemoRunner scripts define step-by-step sequences with Click, TypeText, PressKey, Wait, and Assert actions. Run them headlessly in CI to catch regressions without a display server.

Mocked Input System TESTING

InputSimulator injects key presses, mouse clicks, and scroll events directly into the engine's input pipeline. Test gameplay logic, UI flows, and editor workflows without touching a keyboard. Same API as platform input — your game can't tell the difference.

Direct Node Access API

Full programmatic access to the scene tree during tests. Query nodes by path, name, type, or group. Inspect properties, call methods, emit signals. SceneRunner advances frames and lets you assert on game state between each one.

Headless Rendering AI

App.run_headless() renders full Vulkan frames without a window and returns RGBA numpy arrays. Combined with scene state inspection, an LLM can play your game, screenshot the result, identify what went wrong, patch the code, and re-run — closing the loop autonomously.

Web Export Smoketest WEB

Validate a standalone HTML export the same way. A headless Chromium + WebGPU runner loads the page, waits for the first frame, advances N more, and screenshots the canvas — capturing browser-console tracebacks so a Pyodide error surfaces instead of a silently blank page. Catches web-only breakage a desktop run never sees.

# Replay an editor session in CI
from simvx.core.scripted_demo import (
    DemoRunner, Click, TypeText,
    PressKey, Wait, Assert
)

steps = [
    Click(400, 300),
    TypeText("player_name"),
    PressKey(Key.ENTER),
    Wait(1.0),
    Assert(
        lambda tree: tree.find("Player"),
        "Player node should exist"
    ),
]

# Run headlessly — no window, no GPU display
DemoRunner.run_headless(
    scene, steps,
    speed=50.0,
    max_frames=20000
)
# Simulate gameplay with mocked input
from simvx.core.testing import InputSimulator

sim = InputSimulator()

def on_frame(idx):
    if idx == 0:
        sim.tap_key("enter")   # Start
    if idx == 30:
        sim.press_key("left")  # Move
    if idx == 50:
        sim.release_key("left")
        sim.tap_key("space")   # Fire!

# Works identically to real input
frames = app.run_headless(
    game, frames=100,
    on_frame=on_frame,
    capture_frames=[99]
)
# Query the scene tree in tests
from simvx.core.testing import (
    SceneRunner, InputSimulator, scene_diff
)

runner = SceneRunner()
runner.load(MyGame())

# Advance 10 frames
runner.advance_frames(10)

# Direct node access
player = runner.find("Player")
assert player.position == Vec2(100, 200)

# Simulate input, advance, assert
sim = InputSimulator()
sim.tap_key("space")
runner.advance_frames(1)
assert player.velocity.y < 0  # Jumped!

# Compare scene state snapshots
before = runner.snapshot()
runner.advance_frames(60)
diff = scene_diff(before, runner.snapshot())
assert "score" in diff.changed
# Inspect state + capture frames for LLM debugging
from simvx.graphics import App
from PIL import Image

app = App("My Game", 800, 600)

# Render 100 frames, capture the last one
frames = app.run_headless(
    MyScene(),
    frames=100,
    capture_frames=[99]
)

# frames[0] is a numpy array (H, W, 4) uint8 RGBA
img = Image.fromarray(frames[0][:, :, :3])
img.save("debug_frame.png")

# Feed to vision model for analysis
# "What's wrong with this frame?"
# "The player sprite is clipping through
#  the wall at position (320, 180)"
#
# Capture frames for visual regression
# testing or LLM-assisted debugging.
Get Started
Clone and run
$ git clone https://fezzik.dev/fezzik/simvx.git && cd simvx && uv sync click to copy

Requires Python 3.14+ · Vulkan 1.2+ · GLFW3 · glslc

Source Code Documentation
License
Open source. Free to ship. Yours to own.

SimVX is AGPL-3.0. Build in the open for free, ship a closed-source commercial game with a small SimVX credit, or license it to go fully unbranded.

🔓

AGPL-3.0

The full engine, free and open. Use it for anything — personal projects, jams, education, research, or commercial work — as long as you release your own source under the AGPL.

Attribution Exception

Keep your source closed and ship commercially for free. Just display a SimVX splash on startup, or a small in-game watermark. No revenue limits, no fees.

💼

Commercial License

Remove the splash and the AGPL obligations entirely. Closed source, fully unbranded, on your terms. Get in touch for studio and per-title licensing.

License Details Commercial Enquiry