Blend Modes

a multiply day/night tint and an additive flash.

▶ Run in browser

Tags: 2d

renderer.rect, renderer.polygon and renderer.texture take a blend= keyword that says how the new colour combines with what is already on screen:

  • "alpha" (the default) paints over what is beneath, weighted by the colour’s alpha.

  • "multiply" multiplies the screen by the colour. White leaves it unchanged, 50% grey halves it, and a blue-tinted grey darkens it and pushes it toward blue, which is a day/night cycle in one full-screen rect.

  • "add" adds the colour to the screen, brightening toward white: the usual glow, muzzle-flash and explosion idiom.

Ops composite in the order they are drawn, so an overlay goes after the scene it affects. Here a row of opaque squares is drawn with the default blend, a multiply rect over the whole view cycles between day and night, and an additive rect flashes and fades on top.

A Timer fires a flash every three seconds; press Space, click or tap to fire one yourself.

Source

 1# /// simvx
 2# web = { width = 960, height = 540 }
 3# screenshot_frame = 70
 4# ///
 5"""Blend Modes: a multiply day/night tint and an additive flash.
 6
 7`renderer.rect`, `renderer.polygon` and `renderer.texture` take a `blend=` keyword that says how
 8the new colour combines with what is already on screen:
 9
10- `"alpha"` (the default) paints over what is beneath, weighted by the colour's alpha.
11- `"multiply"` multiplies the screen by the colour. White leaves it unchanged, 50% grey halves
12  it, and a blue-tinted grey darkens it and pushes it toward blue, which is a day/night cycle
13  in one full-screen rect.
14- `"add"` adds the colour to the screen, brightening toward white: the usual glow,
15  muzzle-flash and explosion idiom.
16
17Ops composite in the order they are drawn, so an overlay goes after the scene it affects. Here a
18row of opaque squares is drawn with the default blend, a multiply rect over the whole view
19cycles between day and night, and an additive rect flashes and fades on top.
20
21A `Timer` fires a flash every three seconds; press Space, click or tap to fire one yourself.
22"""
23
24import math
25
26from simvx.core import Input, Key, Node2D, Pointer, Timer
27from simvx.graphics import App
28
29SQUARE_COLOURS = [
30    (0.90, 0.30, 0.25),
31    (0.95, 0.70, 0.20),
32    (0.30, 0.80, 0.40),
33    (0.25, 0.55, 0.95),
34    (0.65, 0.40, 0.90),
35]
36
37
38class BlendDemo(Node2D):
39    dynamic = True  # the tint and the flash change every frame
40    input_context = {"flash": [Key.SPACE, Pointer.PRIMARY]}
41
42    def on_ready(self):
43        self.time = 0.0
44        self.flash = 0.0  # additive flash strength, fading from 1 to 0
45        timer = self.add_child(Timer(duration=3.0, one_shot=False, autostart=True))
46        timer.timeout.connect(self.fire_flash)
47
48    def fire_flash(self):
49        self.flash = 1.0
50
51    def on_update(self, dt):
52        if Input.just_pressed("flash"):
53            self.fire_flash()
54        self.time += dt
55        self.flash = max(0.0, self.flash - dt * 1.8)
56
57    def on_draw(self, renderer):
58        width, height = self.viewport_size
59        renderer.rect((0, 0), (width, height), fill=(0.85, 0.88, 0.92))
60        gap = width / (len(SQUARE_COLOURS) + 1)
61        size = min(120, gap * 0.8, height * 0.4)
62        for i, colour in enumerate(SQUARE_COLOURS):
63            renderer.rect((gap * (i + 1) - size / 2, (height - size) / 2), (size, size), fill=colour)
64
65        # Night goes 0 (noon, white: no change) to 1 (midnight); red drops most, blue least.
66        night = 0.5 - 0.5 * math.cos(self.time * 0.6)
67        tint = (1.0 - 0.75 * night, 1.0 - 0.65 * night, 1.0 - 0.35 * night)
68        renderer.rect((0, 0), (width, height), fill=tint, blend="multiply")
69
70        if self.flash > 0:
71            f = self.flash
72            renderer.rect((0, 0), (width, height), fill=(0.9 * f, 0.85 * f, 0.6 * f), blend="add")
73
74        # Example help overlay: not part of the technique shown.
75        text_width = width - 40
76        renderer.text("BLEND MODES", rect=(20, 16, text_width, 40), fit_to_width=True, fill=(0.1, 0.1, 0.12), scale=3)
77        renderer.text(
78            "multiply = day/night tint   add = flash (Space, click or tap)",
79            rect=(20, 60, text_width, 30),
80            fit_to_width=True,
81            fill=(0.2, 0.2, 0.25),
82            scale=2,
83        )
84
85
86if __name__ == "__main__":
87    App(title="Blend Modes", width=960, height=540).run(BlendDemo())