Splash Screen

the branded boot splash and a reusable loading screen.

▶ Run in browser

Tags: ui splash loading branding

Every windowed SimVX game boots with the engine’s branded splash: a fade in from black, the SIMVX wordmark over a faint centre-lit grid, a segmented gradient progress bar, then a fade back to black once the first frame is ready (a one-second minimum stops sub-second flicker on fast hardware). This demo runs with that default, then lets you re-trigger the same SplashScreen control as an in-game loading screen driven by a real 0..1 progress source: the pattern for heavy scene transitions.

What it demonstrates

  • The default boot splash (App(splash=None)): automatic on windowed runs; configure via App(splash=..., splash_min_time=..., unbranded=...).

  • Reusing SplashScreen for scene transitions: bind progress to any () -> float or an object with a .progress attribute (an asset BatchHandle works as-is).

  • skippable=True: once loading completes, any key or click skips the hold.

  • The finished signal driving teardown (splash.destroy).

Controls: L / Click / Tap - Simulate loading a level behind a SplashScreen Escape - Quit

Source

  1"""Splash Screen: the branded boot splash and a reusable loading screen.
  2
  3Every windowed SimVX game boots with the engine's branded splash: a fade in
  4from black, the SIMVX wordmark over a faint centre-lit grid, a segmented
  5gradient progress bar, then a fade back to black once the first frame is ready
  6(a one-second minimum stops sub-second flicker on fast hardware). This demo
  7runs with that default, then lets you re-trigger the same `SplashScreen`
  8control as an in-game loading screen driven by a real 0..1 progress source:
  9the pattern for heavy scene transitions.
 10
 11# /// simvx
 12# tags = ["ui", "splash", "loading", "branding"]
 13# web = { root = "SplashDemo", width = 800, height = 600, responsive = true }
 14# ///
 15
 16## What it demonstrates
 17
 18- The default boot splash (`App(splash=None)`): automatic on windowed runs;
 19  configure via `App(splash=..., splash_min_time=..., unbranded=...)`.
 20- Reusing `SplashScreen` for scene transitions: bind `progress` to any
 21  `() -> float` or an object with a `.progress` attribute (an asset
 22  `BatchHandle` works as-is).
 23- `skippable=True`: once loading completes, any key or click skips the hold.
 24- The `finished` signal driving teardown (`splash.destroy`).
 25
 26Controls:
 27  L / Click / Tap - Simulate loading a level behind a SplashScreen
 28  Escape          - Quit
 29"""
 30
 31import math
 32import sys
 33
 34from simvx.core import AnchorPreset, Colour, Control, Input, Key, Label, MouseButton, SplashScreen
 35from simvx.graphics import App
 36
 37WIDTH, HEIGHT = 800, 600
 38LOAD_TIME = 2.5  # seconds the simulated level load takes
 39
 40
 41class SplashDemo(Control):
 42    input_actions = {"load": [Key.L, MouseButton.LEFT], "quit": [Key.ESCAPE]}
 43
 44    def on_ready(self):
 45        self.set_anchor_preset(AnchorPreset.FULL_RECT)  # top-level Controls anchor, never absolute-size
 46        self._t = 0.0
 47        self._level = 1
 48        self._splash: SplashScreen | None = None
 49        self._load_started_at = 0.0
 50        self._hint = self.add_child(
 51            Label("Press L (or click) to load the next level behind a SplashScreen", font_size=18)
 52        )
 53        self._hint.position = (24, 24)
 54
 55    # ------------------------------------------------------------- loading
 56
 57    def _begin_load(self):
 58        if self._splash is not None:
 59            return
 60        self._load_started_at = self.tree.now
 61        # A real game passes an asset BatchHandle here; this demo fakes the
 62        # same 0..1 contract off scene time.
 63        self._splash = SplashScreen(
 64            progress=self._load_progress,
 65            min_display_time=1.0,
 66            skippable=True,
 67        )
 68        self._splash.set_progress(0.0, f"loading level {self._level + 1}")
 69        self._splash.finished.connect(self._end_load)
 70        self.add_child(self._splash)  # last child: draws over the scene
 71
 72    def _load_progress(self) -> float:
 73        return min(1.0, (self.tree.now - self._load_started_at) / LOAD_TIME)
 74
 75    def _end_load(self):
 76        self._level += 1
 77        self._hint.text = f"Level {self._level} loaded. Press L to load another."
 78        splash, self._splash = self._splash, None
 79        splash.destroy()
 80
 81    # ------------------------------------------------------------- frame
 82
 83    def on_update(self, dt: float):
 84        self._t += dt
 85        if Input.is_action_just_pressed("load"):
 86            self._begin_load()
 87        if Input.is_action_just_pressed("quit"):
 88            self.app.quit()
 89        self.queue_redraw()  # animated background below
 90
 91    def on_draw(self, renderer):
 92        w, h = self.tree.screen_size
 93        # A live background per level hue, so the loading screen visibly covers
 94        # a running scene and the reveal fades back onto it.
 95        hue = (self._level * 0.13) % 1.0
 96        base = Colour.hex("#173042") if self._level % 2 else Colour.hex("#2b1a38")
 97        renderer.draw_rect((0, 0), (w, h), colour=base, filled=True)
 98        cx, cy = w / 2, h / 2
 99        for i in range(8):
100            a = self._t * 0.8 + i * math.tau / 8
101            x = cx + math.cos(a) * (120 + 40 * math.sin(self._t + i))
102            y = cy + math.sin(a) * (90 + 30 * math.cos(self._t + i))
103            radius = 14 + 6 * math.sin(self._t * 2 + i)
104            renderer.draw_circle((x, y), radius, colour=(0.2 + hue * 0.5, 0.8, 0.7, 0.85), filled=True)
105
106
107def main():
108    if "--test" in sys.argv:
109        App(title="SimVX Splash Screen", width=WIDTH, height=HEIGHT, visible=False).run_headless(
110            SplashDemo(), frames=30
111        )
112        return
113    App(title="SimVX Splash Screen", width=WIDTH, height=HEIGHT).run(SplashDemo())
114
115
116if __name__ == "__main__":
117    main()