Blend Modes¶
Multiply day/night overlay + additive flash via Draw2D.
▶ Run in browserTags: 2d
Demonstrates the blend= keyword on the Draw2D colour ops:
"multiply"(dst * src): a full-screen tint that darkens and colour-shifts the scene like a day/night cycle. A 50% grey multiply halves the scene; a blue-tinted multiply pushes it toward night."add"(dst + src): an additive flash burst that brightens toward white, the standard glow / muzzle-flash / explosion idiom."alpha"(default): the normal over-composited scene underneath.
The scene is a row of opaque sprites (coloured rects) painted with the default alpha blend; the multiply overlay and additive flash are drawn on top in submission order.
Controls:
SPACE triggers an additive flash
ESC quits
Source¶
1"""Blend Modes: Multiply day/night overlay + additive flash via Draw2D.
2
3Demonstrates the ``blend=`` keyword on the Draw2D colour ops:
4
5* ``"multiply"`` (dst * src): a full-screen tint that darkens and colour-shifts
6 the scene like a day/night cycle. A 50% grey multiply halves the scene; a
7 blue-tinted multiply pushes it toward night.
8* ``"add"`` (dst + src): an additive flash burst that brightens toward white,
9 the standard glow / muzzle-flash / explosion idiom.
10* ``"alpha"`` (default): the normal over-composited scene underneath.
11
12The scene is a row of opaque sprites (coloured rects) painted with the default
13alpha blend; the multiply overlay and additive flash are drawn on top in
14submission order.
15
16# /// simvx
17# web = { width = 960, height = 540, root = "BlendDemo" }
18# screenshot_frame = 70
19# ///
20
21Controls:
22 - SPACE triggers an additive flash
23 - ESC quits
24"""
25
26import math
27
28from simvx.core import Input, InputMap, Key, Node2D
29from simvx.graphics import App
30
31WIDTH, HEIGHT = 960, 540
32
33# A palette of opaque scene sprites (drawn with default alpha blend).
34_SPRITES = [
35 (0.90, 0.30, 0.25),
36 (0.95, 0.70, 0.20),
37 (0.30, 0.80, 0.40),
38 (0.25, 0.55, 0.95),
39 (0.65, 0.40, 0.90),
40]
41
42
43class BlendDemo(Node2D):
44 """Multiply day/night tint + additive flash over an alpha-blended scene."""
45
46 def __init__(self, **kwargs):
47 super().__init__(name="BlendDemo", **kwargs)
48 self._t = 0.0
49 self._flash = 0.0 # decaying additive-flash strength in [0, 1]
50
51 def on_ready(self):
52 InputMap.add_action("flash", [Key.SPACE])
53 InputMap.add_action("quit", [Key.ESCAPE])
54
55 def on_update(self, dt: float):
56 self._t += dt
57 # Auto-pulse a flash every ~3s so the demo animates without input, plus
58 # the SPACE trigger for interactive use.
59 if Input.is_action_just_pressed("flash") or math.fmod(self._t, 3.0) < dt:
60 self._flash = 1.0
61 self._flash = max(0.0, self._flash - dt * 1.8)
62 if Input.is_action_just_pressed("quit"):
63 self.app.quit()
64
65 def _night_factor(self) -> float:
66 """0 at noon (no darkening) -> 1 at midnight (heavy blue multiply)."""
67 return 0.5 - 0.5 * math.cos(self._t * 0.6)
68
69 def on_draw(self, renderer):
70 # 1) Light background + a row of opaque sprites (default alpha blend).
71 renderer.draw_rect((0, 0), (WIDTH, HEIGHT), filled=True, colour=(0.85, 0.88, 0.92))
72 n = len(_SPRITES)
73 gap = WIDTH / (n + 1)
74 size = 120
75 for i, col in enumerate(_SPRITES):
76 cx = gap * (i + 1)
77 renderer.draw_rect(
78 (cx - size / 2, HEIGHT * 0.5 - size / 2), (size, size),
79 filled=True, colour=col,
80 )
81
82 # 2) Day/night MULTIPLY overlay. A blue-grey tint that deepens toward
83 # "midnight": multiply darkens and colour-shifts everything beneath.
84 night = self._night_factor()
85 tint = (
86 1.0 - 0.75 * night, # red drops most
87 1.0 - 0.65 * night,
88 1.0 - 0.35 * night, # blue survives -> cool night tone
89 )
90 renderer.draw_rect((0, 0), (WIDTH, HEIGHT), filled=True, colour=tint, blend="multiply")
91
92 # 3) Additive FLASH burst, centred, brightening toward white.
93 if self._flash > 0.001:
94 f = self._flash
95 renderer.draw_rect(
96 (0, 0), (WIDTH, HEIGHT),
97 filled=True, colour=(0.9 * f, 0.85 * f, 0.6 * f), blend="add",
98 )
99
100 # HUD (default alpha text).
101 renderer.draw_text("BLEND MODES", (20, 16), scale=3, colour=(0.1, 0.1, 0.12))
102 renderer.draw_text(
103 "multiply = day/night tint add = flash (SPACE) ESC = quit",
104 (20, 60), scale=2, colour=(0.2, 0.2, 0.25),
105 )
106
107
108if __name__ == "__main__":
109 App(title="Blend Modes", width=WIDTH, height=HEIGHT).run(BlendDemo())