App

App opens a window, builds the scene tree around the node you pass to run(), and runs the game loop: input, fixed-timestep physics, update, draw.

from simvx.core import Camera3D, Material, Mesh, MeshInstance3D, Node
from simvx.graphics import App


class MyGame(Node):
    def on_ready(self):
        self.add_child(Camera3D(position=(0, 5, 10), look_at=(0, 0, 0)))
        self.add_child(MeshInstance3D(mesh=Mesh.box(), material=Material(colour=(1, 0, 0, 1))))


if __name__ == "__main__":
    App(title="My Game").run(MyGame())

run() returns when the window closes. From inside the tree, self.app.quit() ends it; never call sys.exit(), which leaves the audio thread running.

Configuration

Argument

Default

Meaning

title

"SimVX"

Window title

width, height

1280, 720

Initial window size; lay out from node.viewport_size, not from these

physics_fps

60

Fixed tick rate for on_fixed_update, independent of the display rate

target_fps

None

Display frame cap; None is uncapped (vsync-limited). Writable later as app.target_fps

vsync

True

Vertical sync

visible

True

False hides the window, for tests

bg_colour

None

Clear colour: an RGBA tuple, "transparent", or None for the theme background

backend

None

Windowing backend, see below

After run() has opened the window, app.window_title, app.window_size and app.active_backend report the live state; assign app.window_size to resize.

Running headless

simvx run game.py --headless --frames 60 runs a program unmodified, with no window: its own App(...).run(root) renders 60 frames offscreen at the size it asked for, then returns, and an exception exits non-zero. --screenshot last.png saves the last frame. Without --frames it runs 60 frames. The mechanism underneath is App.force_headless(frames, screenshot=...), a block inside which every App.run(root) runs that way.

Code that builds its own scene calls app.run_headless(root, frames=N) instead, which can also return captured frames as arrays. Visual testing covers it.

Windowing backend

App(backend="sdl3")   # touch, pens, app lifecycle
App(backend="glfw")   # the default install; fullscreen toggle
App(backend="qt")     # a Vulkan surface inside a Qt application

With no backend, App tries SDL3, then GLFW, then Qt, and uses the first that imports. GLFW always installs with SimVX; SDL3 and Qt are the sdl3 and qt extras of simvx-graphics. Windowing backends has the feature matrix.

API reference

See simvx.graphics.app for the complete API.