diff --git a/python_payload/apps/demo_worms4.py b/python_payload/apps/demo_worms4.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfba5f02059185145b6597c19d0c8dc4aaf491d7
--- /dev/null
+++ b/python_payload/apps/demo_worms4.py
@@ -0,0 +1,139 @@
+# python imports
+import random
+import time
+import math
+
+# flow3r imports
+from st3m import event, ui
+from st4m import application
+from st4m import Ctx, InputState
+
+
+tau = 2 * math.pi
+
+
+# Subclass Application
+class AppWorms(application.Application):
+    def __init__(self, name):
+        super().__init__(name)
+
+        # HACK: we work against double buffering by keeping note of how many
+        # times on_draw got called.
+        #
+        # We know there's two buffers, so if we render the same state twice in a
+        # row we'll be effectively able to keep a persistent framebuffer, like
+        # with the old API.
+        #
+        # When bufn is in [0, 1] we render the background image.
+        # When bufn is in [2, ...) we render the worms.
+        # When bufn is > 3, we enable updating the worm state.
+        #
+        # TODO(q3k): allow apps to request single-buffered graphics for
+        # persistent framebuffers.
+        self.bufn = 0
+
+        self.worms = []
+        for i in range(0):
+            self.worms.append(Worm())
+
+        self.just_shown = True
+
+    def on_enter(self):
+        # print("on foreground")
+        self.just_shown = True
+
+    def draw(self, ctx):
+        if self.bufn == 0 or self.bufn == 1:
+            ctx.rgb(*ui.BLUE).rectangle(
+                -ui.WIDTH / 2, -ui.HEIGHT / 2, ui.WIDTH, ui.HEIGHT
+            ).fill()
+            ctx.text_align = ctx.CENTER
+            ctx.text_baseline = ctx.MIDDLE
+            ctx.move_to(0, 0).rgb(*ui.WHITE).text("touch me :)")
+            self.bufn += 1
+
+            return
+
+        for w in self.worms:
+            w.draw(ctx)
+        self.bufn += 1
+
+    def think(self, ins: InputState, delta_ms: int) -> None:
+        super().think(ins, delta_ms)
+
+        # Simulation is currently locked to FPS.
+        if self.bufn > 3:
+            for w in self.worms:
+                w.move()
+            self.bufn = 2
+        for index, petal in enumerate(self.input.captouch.petals):
+            if petal.pressed or petal.repeated:
+                self.worms.append(Worm(tau * index / 10 + math.pi))
+
+    def handle_input(self, data):
+        worms = self.worms
+        worms.append(Worm(data.get("index", 0) * 2 * math.pi / 10 + math.pi))
+        if len(worms) > 10:
+            worms.pop(0)
+
+
+class Worm:
+    def __init__(self, direction=None):
+        self.color = ui.randrgb()
+
+        if direction:
+            self.direction = direction
+        else:
+            self.direction = random.random() * math.pi * 2
+
+        self.size = 50
+        self.speed = self.size / 5
+        (x, y) = ui.xy_from_polar(100, self.direction)
+        self.x = x
+        self.y = y
+        # (self.dx,self.dy) = xy_from_polar(1,self.direction)
+        self._lastdist = 0.0
+
+    def draw(self, ctx):
+        ctx.rgb(*self.color)
+        ctx.round_rectangle(
+            self.x - self.size / 2,
+            self.y - self.size / 2,
+            self.size,
+            self.size,
+            self.size // 2,
+        ).fill()
+
+    def mutate(self):
+        self.color = [
+            max(0, min(1, x + ((random.random() - 0.5) * 0.3))) for x in self.color
+        ]
+
+    def move(self):
+        dist = math.sqrt(self.x**2 + self.y**2)
+        target_size = (130 - dist) / 3
+
+        if self.size > target_size:
+            self.size -= 1
+
+        if self.size < target_size:
+            self.size += 1
+
+        self.speed = self.size / 5
+
+        self.direction += (random.random() - 0.5) * math.pi / 4
+
+        (dx, dy) = ui.xy_from_polar(self.speed, self.direction)
+        self.x += dx
+        self.y += dy
+
+        if dist > 120 - self.size / 2 and dist > self._lastdist:
+            polar_position = math.atan2(self.y, self.x)
+            dx = dx * -abs(math.cos(polar_position))
+            dy = dy * -abs(math.sin(polar_position))
+            self.direction = -math.atan2(dy, dx)
+            self.mutate()
+        self._lastdist = dist
+
+
+app = AppWorms("worms")
diff --git a/python_payload/main.py b/python_payload/main.py
index fcd8a927efe1d2258fa74074aeeede13e2563209..51a3ec2b667a2120c499ed4d317dee6130fa290e 100644
--- a/python_payload/main.py
+++ b/python_payload/main.py
@@ -46,11 +46,14 @@ menu_music = SimpleMenu(
     vm,
 )
 
+from apps.demo_worms4 import app as worms
+
+worms._view_manager = vm
 menu_apps = SimpleMenu(
     [
         MenuItemBack(),
         MenuItemNoop("captouch"),
-        MenuItemNoop("worms"),
+        MenuItemForeground("worms", worms),
     ],
     vm,
 )
@@ -58,7 +61,7 @@ menu_apps = SimpleMenu(
 
 menu_main = FlowerMenu(
     [
-        MenuItemForeground("MUsic", menu_music),
+        MenuItemForeground("Music", menu_music),
         MenuItemForeground("Apps", menu_apps),
         MenuItemNoop("Settings"),
     ],
diff --git a/python_payload/st4m/application.py b/python_payload/st4m/application.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcb44fcadfb13036113c545a558718e5684c972b
--- /dev/null
+++ b/python_payload/st4m/application.py
@@ -0,0 +1,15 @@
+from st4m.ui.view import ViewWithInputState, ViewTransitionSwipeRight
+
+
+class Application(ViewWithInputState):
+    def __init__(self, name: str = __name__):
+        self._name = name
+        self._view_manager = None
+        super().__init__()
+
+    def think(self, ins: InputState, delta_ms: int) -> None:
+        super().think(ins, delta_ms)
+
+        if self.input.left_shoulder.middle.pressed:
+            if self._view_manager is not None:
+                self._view_manager.pop(ViewTransitionSwipeRight())
diff --git a/python_payload/st4m/ui/elements/visuals.py b/python_payload/st4m/ui/elements/visuals.py
index 244ae3036fdef667b65cd4cd0e5e04a2ec847b6d..43236a4411a94076ba4b53100bdcb2f009f13cef 100644
--- a/python_payload/st4m/ui/elements/visuals.py
+++ b/python_payload/st4m/ui/elements/visuals.py
@@ -84,7 +84,6 @@ class GroupRing(Responder):
             ctx.rotate(-angle).translate(0, self.r).rotate(math.pi)
             item.draw(ctx)
             ctx.restore()
-        # ctx.restore()
 
 
 class FlowerIcon(Responder):
@@ -103,7 +102,7 @@ class FlowerIcon(Responder):
         self.highlighted = False
         self.rotation_time = 0.0
 
-        self.petal_count = random.randint(2, 3)
+        self.petal_count = random.randint(3, 5)
         self.petal_color = (random.random(), random.random(), random.random())
         self.phi_offset = random.random()
         self.size_offset = random.randint(0, 20)
@@ -127,11 +126,13 @@ class FlowerIcon(Responder):
         ctx.text_baseline = ctx.MIDDLE
         ctx.font_size = self.size / 3
         ctx.line_width = 5
+        ctx.font = ctx.get_font_name(6)
         if self.rotation_time:
             phi_rotate = tau * ((self.ts % self.rotation_time) / self.rotation_time)
         else:
             phi_rotate = 0
         for i in range(self.petal_count):
+            # continue
             # ctx.save()
 
             phi = (tau / self.petal_count * i + self.phi_offset + phi_rotate) % tau
@@ -158,21 +159,20 @@ class FlowerIcon(Responder):
 
         # label
         # y += self.size / 3
+
         w = max(self.size, ctx.text_width(self.label) + 10)
         h = self.size / 3 + 8
-        if False and self.highlighted:
-            ctx.rgb(*BLACK).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*GO_GREEN).move_to(x, y).text(self.label)
+        # ctx.save()
+        ctx.translate(0, self.size / 3)
+
+        if self.highlighted:
+            bg = BLACK
+            fg = GO_GREEN
         else:
-            ctx.save()
-            ctx.translate(0, self.size / 3)
-            ctx.rgb(*PUSH_RED).round_rectangle(
-                x - w / 2, y - h / 2, w, h, w // 2
-            ).fill()
+            bg = PUSH_RED
+            fg = BLACK
 
-            ctx.rgb(*BLACK).move_to(x, y).text(self.label)
-            ctx.restore()
+        ctx.rgb(*bg).round_rectangle(x - w / 2, y - h / 2, w, h, w // 2).fill()
+        ctx.rgb(*fg).move_to(x, y).text(self.label)
 
         ctx.restore()