First Scene

Open a window and add a node.

▶ Run in browser

Tags: beginner tutorial

First Scene

Your first SimVX scene in under thirty lines. By the end you will have opened a window, drawn a pulsing circle, and seen the three lifecycle hooks every node has access to.

Step 1: Subclass Node2D

Every visible thing in SimVX is a node. Node2D is the 2D-aware variant: it knows about position, rotation, and lets you override on_draw() to issue immediate-mode draw commands.

from simvx.core import Node2D

class FirstScene(Node2D):
    pass

Step 2: Hook into the frame loop

Three hooks fire automatically once the node is in the scene tree:

  • on_ready(): called once after the node enters the tree. Initialise state here.

  • on_update(dt): called every frame with the seconds since the last frame.

  • on_draw(renderer): issue draw commands here. The 2D renderer is retained: it re-runs on_draw only when the node changes, so a node whose drawing animates every frame (like our pulsing circle) sets dynamic = True to opt into per-frame redraws.

class FirstScene(Node2D):
    dynamic = True  # the circle pulses every frame, so redraw it every frame

def on_ready(self):
    self.t = 0.0

def on_update(self, dt: float):
    self.t += dt

def on_draw(self, renderer):
    renderer.draw_circle((400, 300), 80, colour=(0.4, 0.8, 1.0, 1.0), filled=True)

Step 3: Launch it

App creates a Vulkan-backed window and runs your scene tree.

from simvx.graphics import App

App(title="First Scene", width=800, height=600).run(FirstScene())

Run it

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

A window opens with a pulsing blue circle and a welcome message.

What’s next

  • Nodes and Signals – the next tutorial: wire several nodes together so they react to each other.

  • Input and Movement – drive a node from the keyboard with input actions.

  • Bouncing BallsProperty descriptors and many children at once.

Source

 1"""First Scene: Open a window and add a node.
 2
 3A two-minute tour: subclass `Node2D`, override `on_draw()` to paint a circle,
 4launch with `App`. The companion README explains the lifecycle hooks called
 5along the way.
 6
 7# /// simvx
 8# tags = ["beginner", "tutorial"]
 9# web = { root = "FirstScene", width = 800, height = 600, responsive = true }
10# ///
11"""
12
13import math
14
15from simvx.core import Node2D
16from simvx.graphics import App
17
18WIDTH, HEIGHT = 800, 600
19
20
21class FirstScene(Node2D):
22    # The circle pulses every frame -> opt this node into per-frame redraw so the
23    # retained 2D renderer re-runs on_draw each frame instead of freezing the geometry.
24    dynamic = True
25
26    def on_ready(self):
27        self.t = 0.0
28
29    def on_update(self, dt: float):
30        self.t += dt
31
32    def on_draw(self, renderer):
33        cx, cy = WIDTH / 2, HEIGHT / 2
34        radius = 80 + 20 * math.sin(self.t * 2)  # gentle pulse, 60..100 px
35        renderer.draw_circle((cx, cy), radius, colour=(0.4, 0.8, 1.0, 1.0), filled=True)
36        renderer.draw_text("Welcome to SimVX", (cx - 110, cy + 140), scale=2, colour=(1.0, 1.0, 1.0))
37
38
39if __name__ == "__main__":
40    App(title="First Scene", width=WIDTH, height=HEIGHT).run(FirstScene())