diff --git a/components/micropython/usermodule/mp_sys_display.c b/components/micropython/usermodule/mp_sys_display.c
index e0c4cbaf831b81e873f4cf0bc8b072a23dc4397d..93f9e51f87847c3d93ab6b5ed3750a1765995957 100644
--- a/components/micropython/usermodule/mp_sys_display.c
+++ b/components/micropython/usermodule/mp_sys_display.c
@@ -65,22 +65,7 @@ STATIC mp_obj_t mp_set_palette(mp_obj_t pal_in) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_palette_obj, mp_set_palette);
 
 STATIC mp_obj_t mp_ctx(mp_obj_t mode_in) {
-    int mode = mp_obj_get_int(mode_in);
-    Ctx *ctx = NULL;
-    switch (mode) {
-        case 0:
-        case 16:
-            ctx = st3m_ctx(0);
-            if (ctx == NULL) return mp_const_none;
-            break;
-        case st3m_gfx_overlay:
-        case 8:
-        case 24:
-        case 32:
-            ctx = st3m_overlay_ctx();
-            break;
-    }
-    return mp_ctx_from_ctx(ctx);
+    return mp_ctx_from_ctx(st3m_ctx(mp_obj_get_int(mode_in)));
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_ctx_obj, mp_ctx);
 
diff --git a/components/st3m/st3m_gfx.c b/components/st3m/st3m_gfx.c
index 2eb31ff1787e021ceaea952b7a5ec1e2f8c82947..b1aa00808ddd08b9d8fb6e87c7067e38c35123e6 100644
--- a/components/st3m/st3m_gfx.c
+++ b/components/st3m/st3m_gfx.c
@@ -1,6 +1,7 @@
 #include "st3m_gfx.h"
 // Submit a filled ctx descriptor to the rasterization pipeline.
 
+#include <pthread.h>
 #include <string.h>
 
 #include "esp_log.h"
@@ -54,6 +55,7 @@ static Ctx *fb_ctx = NULL;
 
 // RGBA8 overlay ctx
 static Ctx *overlay_ctx = NULL;
+static pthread_mutex_t overlay_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 // corner pixel coordinates for overlay clip
 static int _st3m_overlay_y1 = 0;
@@ -187,6 +189,18 @@ uint8_t *st3m_gfx_fb(st3m_gfx_mode mode) {
     return st3m_overlay_fb;
 }
 
+Ctx *st3m_overlay_ctx(void) {
+    if (!overlay_ctx) {
+        Ctx *ctx = overlay_ctx = ctx_new_for_framebuffer(
+            st3m_overlay_fb, OVERLAY_WIDTH, OVERLAY_HEIGHT, OVERLAY_WIDTH * 4,
+            CTX_FORMAT_RGBA8);
+
+        ctx_translate(ctx, 120 - OVERLAY_X, 120 - OVERLAY_Y);
+        memset(st3m_overlay_fb, 0, sizeof(st3m_overlay_fb));
+    }
+    return overlay_ctx;
+}
+
 static void st3m_gfx_task(void *_arg) {
     (void)_arg;
 
@@ -212,7 +226,8 @@ static void st3m_gfx_task(void *_arg) {
                 break;
             case st3m_gfx_default:
                 ctx_render_ctx(user_copy_ctx, fb_ctx);
-                if (_st3m_overlay_y1 != _st3m_overlay_y0)
+                if (_st3m_overlay_y1 != _st3m_overlay_y0) {
+                    pthread_mutex_lock(&overlay_mutex);
                     st3m_ctx_merge_overlay(
                         fb,
                         st3m_overlay_fb + 4 * ((_st3m_overlay_y0 - OVERLAY_Y) *
@@ -222,13 +237,15 @@ static void st3m_gfx_task(void *_arg) {
                         _st3m_overlay_x0, _st3m_overlay_y0,
                         _st3m_overlay_x1 - _st3m_overlay_x0 + 1,
                         _st3m_overlay_y1 - _st3m_overlay_y0 + 1);
-                flow3r_bsp_display_send_fb(fb, 16);
-                if (_st3m_overlay_y1 != _st3m_overlay_y0)
+                    flow3r_bsp_display_send_fb(fb, 16);
                     st3m_ctx_unmerge_overlay(
                         fb, st3m_overlay_backup, _st3m_overlay_x0,
                         _st3m_overlay_y0,
                         _st3m_overlay_x1 - _st3m_overlay_x0 + 1,
                         _st3m_overlay_y1 - _st3m_overlay_y0 + 1);
+                    pthread_mutex_unlock(&overlay_mutex);
+                } else
+                    flow3r_bsp_display_send_fb(fb, 16);
                 break;
             case st3m_gfx_16bpp:
                 flow3r_bsp_display_send_fb(fb, 16);
@@ -532,7 +549,12 @@ static void st3m_gfx_drawctx_pipe_put(void) {
     xQueueSend(user_ctx_rastq, &descno, portMAX_DELAY);
 }
 
-void st3m_ctx_end_frame(Ctx *ctx) { st3m_gfx_drawctx_pipe_put(); }
+void st3m_ctx_end_frame(Ctx *ctx) {
+    if (ctx == overlay_ctx)
+        pthread_mutex_unlock(&overlay_mutex);
+    else
+        st3m_gfx_drawctx_pipe_put();
+}
 
 uint8_t st3m_gfx_drawctx_pipe_full(void) {
     return uxQueueMessagesWaiting(user_ctx_freeq) == 0;
@@ -558,12 +580,6 @@ void st3m_gfx_flush(void) {
     ESP_LOGW(TAG, "Pipeline flush/reset done.");
 }
 
-Ctx *st3m_ctx(TickType_t ticks_to_wait) {
-    Ctx *ctx = st3m_gfx_drawctx_free_get(ticks_to_wait);
-    if (!ctx) return NULL;
-    return ctx;
-}
-
 void st3m_overlay_clear(void) {
     Ctx *ctx = st3m_overlay_ctx();
     ctx_save(ctx);
@@ -574,16 +590,14 @@ void st3m_overlay_clear(void) {
     ctx_restore(ctx);
 }
 
-Ctx *st3m_overlay_ctx(void) {
-    if (!overlay_ctx) {
-        Ctx *ctx = overlay_ctx = ctx_new_for_framebuffer(
-            st3m_overlay_fb, OVERLAY_WIDTH, OVERLAY_HEIGHT, OVERLAY_WIDTH * 4,
-            CTX_FORMAT_RGBA8);
-
-        ctx_translate(ctx, 120 - OVERLAY_X, 120 - OVERLAY_Y);
-        memset(st3m_overlay_fb, 0, sizeof(st3m_overlay_fb));
+Ctx *st3m_ctx(st3m_gfx_mode mode) {
+    if (mode == st3m_gfx_overlay) {
+        pthread_mutex_lock(&overlay_mutex);
+        return st3m_overlay_ctx();
     }
-    return overlay_ctx;
+    Ctx *ctx = st3m_gfx_drawctx_free_get(2000);
+    if (!ctx) return NULL;
+    return ctx;
 }
 
 void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1) {
diff --git a/components/st3m/st3m_gfx.h b/components/st3m/st3m_gfx.h
index dd9ff51d29d1661cb07414f4116a06fce7e0b375..f2bf414bc8cb0756b8a70c87cdd4a6efb7a2cecf 100644
--- a/components/st3m/st3m_gfx.h
+++ b/components/st3m/st3m_gfx.h
@@ -36,8 +36,8 @@ void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1);
 // returns a running average of fps
 float st3m_gfx_fps(void);
 
-Ctx *st3m_overlay_ctx(void);              // XXX to be removed
-Ctx *st3m_ctx(TickType_t ticks_to_wait);  // XXX: will get mode as arg
+// returns a ctx for drawing at the specified mode/target
+Ctx *st3m_ctx(st3m_gfx_mode mode);
 
 void st3m_ctx_end_frame(Ctx *ctx);  // temporary, signature compatible
                                     // with ctx_end_frame()
diff --git a/python_payload/st3m/ui/elements/overlays.py b/python_payload/st3m/ui/elements/overlays.py
index f69791bcb08ae559edf4278165e263f0a49d8318..07082d448afb14b838df4753cd69cfdc06abbe4a 100644
--- a/python_payload/st3m/ui/elements/overlays.py
+++ b/python_payload/st3m/ui/elements/overlays.py
@@ -134,6 +134,7 @@ class Compositor(Responder):
                     overlay.draw(octx)
             self._frame_skip = 8
             sys_display.overlay_clip(_clip_x0, _clip_y0, _clip_x1, _clip_y1)
+            sys_display.update(octx)
         self._frame_skip -= 1
 
     def add_overlay(self, ov: Overlay) -> None:
diff --git a/recovery/main/rec_fatal.c b/recovery/main/rec_fatal.c
index a383ca1ba9469e807806e29c38e5ea39a596078a..cdfa21e897ffd7a896b323a158818d5c9c8cbf58 100644
--- a/recovery/main/rec_fatal.c
+++ b/recovery/main/rec_fatal.c
@@ -6,7 +6,7 @@
 
 void rec_fatal(const char *msg) {
     for (;;) {
-        Ctx *ctx = st3m_ctx(portMAX_DELAY);
+        Ctx *ctx = st3m_ctx(st3m_gfx_default);
 
         // Draw background.
         ctx_rgb(ctx, 0.29, 0.0, 0.0);
diff --git a/recovery/main/rec_gui.c b/recovery/main/rec_gui.c
index de33bcc05becc0cc55736536c2028aebcd9bc55a..0c572b8835b7cd703db4de8ad0f6ec15e9fdadba 100644
--- a/recovery/main/rec_gui.c
+++ b/recovery/main/rec_gui.c
@@ -38,7 +38,7 @@ static void _header_draw(Ctx *ctx) {
 }
 
 void rec_erasing_draw(void) {
-    Ctx *ctx = st3m_ctx(portMAX_DELAY);
+    Ctx *ctx = st3m_ctx(st3m_gfx_default);
     _header_draw(ctx);
 
     ctx_move_to(ctx, 0, 0);
@@ -50,7 +50,7 @@ void rec_erasing_draw(void) {
 }
 
 void rec_flashing_draw(int percent) {
-    Ctx *ctx = st3m_ctx(portMAX_DELAY);
+    Ctx *ctx = st3m_ctx(st3m_gfx_default);
     _header_draw(ctx);
 
     ctx_move_to(ctx, 0, 0);
@@ -68,7 +68,7 @@ void rec_flashing_draw(int percent) {
 }
 
 void rec_menu_draw(menu_t *menu) {
-    Ctx *ctx = st3m_ctx(portMAX_DELAY);
+    Ctx *ctx = st3m_ctx(st3m_gfx_default);
     _header_draw(ctx);
 
     int y = -20;