From ebdb1f045703b43dba8ec5c5a6ede9334d089656 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 15 Jul 2026 18:34:17 +0200 Subject: [PATCH] Cycle rain streams through the sprite pool for a sense of change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each stream periodically swaps to a different one of the pooled streak sprites on a staggered timer, so the columns keep changing rather than sitting statically tiled — the rain reads as alive in the corner of the eye. The pool stays at 5: each sprite is ~15 KB of internal RAM and more starves esp-hosted's internal-RAM task stacks into a boot loop. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01LKPbR6DY2JygHbyLjxm7Uu --- firmware/main/face.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/firmware/main/face.c b/firmware/main/face.c index cd741df..c1e63e6 100644 --- a/firmware/main/face.c +++ b/firmware/main/face.c @@ -32,7 +32,9 @@ LV_FONT_DECLARE(font_rage_64); #define SPRITE_GLYPHS 12 /* streak length in a sprite */ #define SPRITE_W 24 /* sprite width (glyph ~22px) */ #define SPRITE_H (SPRITE_GLYPHS * CELL_H) -#define SPRITE_POOL 5 /* distinct pre-rendered streaks */ +#define SPRITE_POOL 5 /* distinct pre-rendered streaks. Capped at 5: + * each ~15 KB in internal RAM, and more starves + * esp-hosted's internal-RAM task stacks (boot loop). */ #define RAIN_COLS (SCREEN / RAIN_CELL) #define TICK_MS 33 @@ -89,6 +91,7 @@ static const char *GLYPHS[] = { typedef struct { lv_obj_t *img; /* lv_image showing one of the pool sprites */ int sprite; /* which pool sprite (index into sprite_dsc) */ + int flip_in; /* ticks until this stream swaps to another pool sprite */ int x, y, speed; bool alive; } stream_t; @@ -174,6 +177,7 @@ static void stream_spawn(stream_t *s, int speed) s->speed = speed + esp_random() % 4; s->y = -SPRITE_H; s->sprite = esp_random() % SPRITE_POOL; + s->flip_in = 12 + esp_random() % 28; if (s->img == NULL) { s->img = lv_image_create(F.rain_layer); } @@ -218,6 +222,11 @@ static void rain_tick(void) alive++; s->y += s->speed * 2; /* cheap blit of a pre-rendered sprite */ lv_obj_set_pos(s->img, s->x, s->y); + if (--s->flip_in <= 0) { /* swap to a different streak sprite: the "flip" */ + s->sprite = (s->sprite + 1 + esp_random() % (SPRITE_POOL - 1)) % SPRITE_POOL; + lv_image_set_src(s->img, &sprite_dsc[s->sprite]); + s->flip_in = 12 + esp_random() % 28; + } if (s->y > SCREEN) { if (alive > target) { stream_hide(s);