diff --git a/python_payload/application.py b/python_payload/application.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7bd5e7425c38b68a47d11df41786dc46f154647
--- /dev/null
+++ b/python_payload/application.py
@@ -0,0 +1,149 @@
+import ui
+import event
+import menu
+
+STATE_OFF = 0
+STATE_INIT = 10
+STATE_BACKGROUND = 200
+STATE_FOREGROUND = 300
+STATE_ERROR = 500
+
+
+class Application():
+    def __init__(self,title="badge23 app", author="someone@earth"):
+        self.title = title
+        self.author = author
+        self.state = STATE_OFF
+        self.has_background = False
+        self.has_foreground = True
+        self._events_background = []
+        self._events_foreground = []
+        self.ui = ui.Viewport()
+        self.engine = event.the_engine
+
+        self.add_event(event.Event(
+                name="exit",
+            action=self.exit,
+            condition=lambda e: e["type"]=="button" and e.get("from")==2 and e["change"]
+        ))
+
+        self.ui.add(ui.Icon(label=self.title))
+        
+    def __repr__(self):
+        return "App "+self.title
+    def init(self):
+        print("INIT")
+        self.state = STATE_INIT
+        print("before on_init")
+        self.on_init()
+        print("after on_init")
+        if self.has_background:
+            if self._events_background:
+                self._set_events(self._events_background,True)
+            engine.register_service_loop(self.main_always,True)
+
+
+    def run(self):
+        print ("RUN from",self.state)
+        if self.state == STATE_OFF:
+            print("go init")
+            self.init()
+        
+        if self.has_foreground:
+            self._to_foreground()
+        elif self.has_background:
+            self._to_background()
+            print ("App {} has no foreground, running in background".format(self.title))
+        else:
+            print("App has neither foreground nor background, not doing anything")
+
+        #start the eventloop if it is not already running
+        event.the_engine.eventloop()
+
+        
+    def exit(self,data={}):
+        print("EXIT")
+        self.on_exit()
+        if self.state == STATE_FOREGROUND:
+            self._to_background()
+        
+    
+    def kill(self):
+        #disable all events
+        engine.register_service_loop(self.main_always,False)
+        engine.register_main_loop(self.main_forground,False)
+        self._set_events(self._events_background,False)
+        self._set_events(self._events_forground,False)
+        self.state = STATE_OFF
+
+    def add_event(self,event,is_background=False):
+        if not is_background:
+            self._events_foreground.append(event)
+        else:
+            self._events_background.append(event)
+
+    def is_foreground(self):
+        return self.state == STATE_FOREGROUND
+
+    def _to_foreground(self):
+        print ("to foreground", self)
+        if not self.has_foreground:
+            #TODO log
+            return
+        
+        if self._events_background:
+            self._set_events(self_events_background,False)
+        self.state = STATE_FOREGROUND
+        
+        if self._events_foreground:
+            self._set_events(self._events_foreground,True)
+        #TODO should this really happen here??
+        menu.menu_disable()
+        if self.ui:
+            self.ui.draw()
+        self.on_foreground()
+        
+        self.engine.register_main_loop(self.main_foreground,True)
+        
+          
+    def _to_background(self):
+        self.state = STATE_BACKGROUND
+        if self._events_foreground:
+            print("baz")
+            self._set_events(self._events_foreground,False)
+
+        self.engine.register_main_loop(self.main_foreground,False)
+        if self.has_background:
+            self.state = STATE_BACKGROUND
+        self.on_background()
+        
+    def _set_events(self,events,enabled=True):
+        for e in events:
+            e.set_enabled(enabled)
+
+    def on_init(self):
+        print("nothing to init")
+        pass
+
+    def on_foreground(self):
+        pass
+
+    def on_background(self):
+        pass
+    
+    def on_exit(self):
+        pass
+
+    def on_kill(self): 
+        pass
+
+    def main_foreground(self):
+        #print("nothing")
+        pass
+    
+    def main_always(self):
+        pass
+
+
+    def main(self):
+        self.main_foreground()
diff --git a/python_payload/cap_touch_demo.py b/python_payload/cap_touch_demo.py
index 312b162c92e906a3ac55c1b5ec556818166707d7..6b64786a1127988e31e8a8655a16aab108b720ad 100644
--- a/python_payload/cap_touch_demo.py
+++ b/python_payload/cap_touch_demo.py
@@ -26,3 +26,8 @@ def run():
 def foreground():
     pass
 
+
+from application import Application
+
+app = Application("cap touch")
+app.main_foreground = run
diff --git a/python_payload/demo_menu.py b/python_payload/demo_menu.py
index 522c758f31ffad47382a7724f33c354f5e113643..25c024a719f2422056e1b7d7d374e63c38f641d6 100644
--- a/python_payload/demo_menu.py
+++ b/python_payload/demo_menu.py
@@ -2,6 +2,7 @@ import menu
 import event
 import hardware
 import control
+import application
 
 import demo_worms,demo_sparabo,cap_touch_demo, melodic_demo, harmonic_demo
 import menu_settings,menu_tinysynth
@@ -9,67 +10,14 @@ import menu_settings,menu_tinysynth
 import time
 
 hardware.captouch_autocalib()
+hardware.set_global_volume_dB(0)
 
-def start_worms(action):
-    menu.menu_stack.append(menu.active_menu)
-    menu.active_menu=None
-    demo_worms.run()
 
-def start_sparabo(action):
-    menu.menu_stack.append(menu.active_menu)
-    menu.active_menu=None
-    demo_sparabo.run()
-
-def start_captouch(action):
-    armed = False
-    while True:
-        cap_touch_demo.run()
-        time.sleep_ms(10)
-        if hardware.get_button(1) == 0:
-            armed = True
-        if armed and hardware.get_button(1) == 2:
-            break
-
-def start_melodic(action):
-    armed = False
-    melodic_demo.init()
-    hardware.set_global_volume_dB(20)
-    hardware.display_fill(0)
-    while True:
-        melodic_demo.run()
-        time.sleep_ms(10)
-        if hardware.get_button(1) == 0:
-            armed = True
-        if armed and hardware.get_button(1) == 2:
-            break
-
-def start_harmonic(action):
-    armed = False
-    harmonic_demo.init()
-    hardware.set_global_volume_dB(20)
-    hardware.display_fill(0)
-    while True:
-        harmonic_demo.run()
-        time.sleep_ms(10)
-        if hardware.get_button(1) == 0:
-            armed = True
-        if armed and hardware.get_button(1) == 2:
-            break
 menu_demo = menu.Menu("demo")
-item_worms = menu.MenuItem("worms")
-item_worms.action = start_worms
-menu_demo.add(item_worms)
-
-item_abo = menu.MenuItem("abo")
-item_abo.action = start_sparabo
-menu_demo.add(item_abo)
 
-item_cap = menu.MenuItem("captouch")
-item_cap.action = start_captouch
-menu_demo.add(item_cap)
+for app_module in [demo_worms,demo_sparabo,cap_touch_demo,melodic_demo,harmonic_demo]:
+    menu_demo.add(menu.MenuItemApp(app_module.app))
 
-menu_demo.add(menu.MenuItem("melodic", action=start_melodic))
-menu_demo.add(menu.MenuItem("harmonic", action=start_harmonic))
 
 testmenu = menu.Menu("test")
 
diff --git a/python_payload/demo_sparabo.py b/python_payload/demo_sparabo.py
index 4bbf4f1e76b93c902e971d677dd27eeba752b492..36ead3a4aabb26459618b6e9b0919fb77e3d113c 100644
--- a/python_payload/demo_sparabo.py
+++ b/python_payload/demo_sparabo.py
@@ -1,66 +1,58 @@
+#python
+import math
+
+#badge23
 import event
 import hardware
 from synth import tinysynth
-import math
+import application
+import ui
 
-def xy_from_polar(r,deg):
-	#rad = deg/180*math.pi
-	
-	return( (
-		r * math.sin(deg), #x
-		r * math.cos(deg)  #y
-	)  )
 
-ctx = hardware.get_ctx()
 popcorn = [9,7,9,5,0,5,-3,999]
-synth = tinysynth(440,1)
-
-sequencer = None
-handler = None
 
 def on_step(data):
-	note = popcorn[data["step"]]
-	if note != 999:
-		synth.tone(note)
-		synth.start()
-	
-	
-	ctx.rgb(1,1,0).rectangle(-120,-120,240,240).fill()
-	(x,y) = xy_from_polar(90,-2*math.pi/8*data["step"]+math.pi)
-	size=180
-	ctx.rgb(0.8,0.8,0)
-	ctx.round_rectangle(
-			x-size/2,
-			y-size/2,
-			size,size,size//2
-	).fill()
-	ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"]))
-	hardware.display_update()
-	
-def handle_input(data={}):
-    print("removed")
-    
+    ctx = app.ui.ctx
+    synth = app.synth
+    note = popcorn[data["step"]]
+    if note != 999:
+        synth.tone(note)
+        synth.start()
     
-    sequencer.remove()
-    ev.remove()
+    if not app.is_foreground(): return
 
-    #TODO this is a bad hack!
-    event.the_engine.events_timed=[]
-    
-
-def init():
     ctx.text_align = ctx.CENTER
     ctx.text_baseline = ctx.MIDDLE
 
-    synth = tinysynth(440,1)
-    synth.decay(25)
-    global sequencer
-    sequencer = event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
-    global ev
-    ev=event.Event(name="sparabo",action=handle_input, 
-        condition=lambda e:  e["type"] =="button" and e["change"] and e["value"] == 2)
+    ctx.rgb(1,1,0).rectangle(-120,-120,240,240).fill()
+    (x,y) = ui.xy_from_polar(90,-2*math.pi/8*data["step"]+math.pi)
+    size=180
+    ctx.rgb(0.8,0.8,0)
+    ctx.round_rectangle(
+            x-size/2,
+            y-size/2,
+            size,size,size//2
+    ).fill()
+    ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"]))
+
+class AppSparabo(application.Application):
+    def on_init(self):
+        hardware.set_global_volume_dB(0)
+        
+        self.synth = tinysynth(440,1)
+        self.synth.decay(25)
+
+        print ("here")
+        self.sequencer = event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
+        self.sequencer.start()
+        if self.sequencer.repeat_event:
+            self.add_event(self.sequencer.repeat_event)
+    
+    def on_foreground(self):
+        self.sequencer.start()
 
-def run():
-    init();
-    print("run")
-    event.the_engine.eventloop()
+    def on_exit(self):
+        self.sequencer.stop()
+
+    
+app = AppSparabo("sequencer")
\ No newline at end of file
diff --git a/python_payload/demo_worms.py b/python_payload/demo_worms.py
index ece958f380e652f236719ecef4089ec88b5b93ad..f20540cfc94bbf02ce0c7526582acc770a09d3e0 100644
--- a/python_payload/demo_worms.py
+++ b/python_payload/demo_worms.py
@@ -1,63 +1,115 @@
-import hardware
+#python
 import random
 import time
 import math
+
+#badge23
 import event
+import application
+import ui
 
-def xy_from_polar(r,deg):
-    #rad = deg/180*math.pi
+# Subclass Application
+class AppWorms(application.Application):
 
-    return (
-        r * math.sin(deg), #x
-        r * math.cos(deg)  #y
-    )
+    def on_init(self):
+        print("on init")
 
-def randrgb():
-    return ((random.random(),random.random(),random.random()))
+        # TODO(q3k): factor out frame limiter
+        self.last_render = None
+        self.target_fps = 30
+        self.target_delta = 1000 / self.target_fps
+        self.frame_slack = None
+        self.last_report = None
 
+        self.worms = []
+        for i in range(0):
+            worms.append(Worm())
+    
+    def on_foreground(self):
+        print("on foreground")
+        ctx = app.ui.ctx
+
+        #center the text horizontally and vertically
+        ctx.text_align = ctx.CENTER
+        ctx.text_baseline = ctx.MIDDLE
+
+        #ctx.rgb() expects individual values for the channels, so unpack a list/tuple with *
+        #operations on ctx can be chained
+        #create a blue background
+        ctx.rgb(*ui.BLUE).rectangle(-ui.WIDTH/2,-ui.HEIGHT/2,ui.WIDTH,ui.HEIGHT).fill()
+
+        #Write some text
+        ctx.move_to(0,0).rgb(*ui.WHITE).text("touch me :)")
+
+    def main_foreground(self):
+        now = time.ticks_ms()
+
+        if self.last_render is not None:
+            delta = now - self.last_render
+            if self.frame_slack is None:
+                self.frame_slack = self.target_delta - delta
+            if delta < self.target_delta:
+                return
+
+            if self.last_report is None or (now - self.last_report) > 1000:
+                fps = 1000/delta
+                print(f'fps: {fps:.3}, frame budget slack: {self.frame_slack:.3}ms')
+                self.last_report = now
+
+        # Simulation is currently locked to FPS.
+
+        for w in self.worms:
+            w.draw()
+            w.move()
+        
+        self.last_render = now
 
-WIDTH = 240
-HEIGHT = 240
+app = AppWorms("worms")
 
-#Define a few RGB (0.0 to 1.0) colors
-BLACK = (0,0,0)
-RED = (1,0,0)
-GREEN = (0,1,0)
-BLUE = (0,0,1)
-WHITE = (1,1,1)
-GREY = (0.5,0.5,0.5)
+def handle_input(data):
+    worms = app.worms
+    worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi ))
+    if len(worms)>10:
+        worms.pop(0)
 
-# The global context (representing the whole screen)
-ctx = None
+app.add_event(event.Event(
+    name="worms_control",
+    action=handle_input, 
+    condition=lambda data: data.get("type","")=="captouch" and data.get("value")==1 and data["change"],
+    )
+)
 
-worms = []
+app.add_event(event.Event(
+    name="worms_exit",
+    action=app.exit,
+    condition=lambda e: e["type"]=="button" and e.get("from")==2 and e["change"]
+))
 
 class Worm():
     def __init__(self,direction=None):
-        self.color = randrgb()
-        
+        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) = xy_from_polar(100, self.direction)
+        (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.rgb(*self.color)
-        ctx.round_rectangle(
+        app.ui.ctx.rgb(*self.color)
+        app.ui.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])
         
@@ -74,7 +126,7 @@ class Worm():
         
         self.direction += (random.random()-0.5)*math.pi/4
         
-        (dx,dy) = xy_from_polar(self.speed,self.direction)
+        (dx,dy) = ui.xy_from_polar(self.speed,self.direction)
         self.x+=dx
         self.y+=dy
         
@@ -86,84 +138,11 @@ class Worm():
             self.direction=-math.atan2(dy,dx)
             self.mutate()
         self._lastdist = dist
-          
-
-def handle_input(data):
-    worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi ))
-    if len(worms)>10:
-        worms.pop(0)
 
-
-def init(data={}):    
-    # Get the global context (representing the whole screen)
-    ctx = hardware.get_ctx()
-
-# TODO(q3k): factor out frame limiter
-last_render = None
-target_fps = 30
-target_delta = 1000 / target_fps
-frame_slack = None
-last_report = None
-def run():
-    global last_render
-    global last_report
-    global frame_slack
-    now = time.ticks_ms()
-
-    if last_render is not None:
-        delta = now - last_render
-        if frame_slack is None:
-            frame_slack = target_delta - delta
-        if delta < target_delta:
-            return
-
-        if last_report is None or (now - last_report) > 1000:
-            fps = 1000/delta
-            print(f'fps: {fps:.3}, frame budget slack: {frame_slack:.3}ms')
-            last_report = now
-
-    # Simulation is currently locked to FPS.
-    global worms
-    for w in worms:
-        w.draw()
-        w.move()
-    hardware.display_update()
-    last_render = now
-
-
-def foreground():
-    ctx.text_align = ctx.CENTER
-    ctx.text_baseline = ctx.MIDDLE
-
-    #ctx.rgb() expects individual values for the channels, so unpack a list/tuple with *
-    #operations on ctx can be chained
-    #create a blue background
-    ctx.rgb(*BLUE).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
-
-    #Write some text
-    ctx.move_to(0,0).rgb(*WHITE).text("touch me :)")
-    hardware.display_update()
-    global worms
-    worms = []
-    for i in range(0):
-        worms.append(Worm())
-    
-    event.Event(name="worms_control",action=handle_input, 
-        condition=lambda data: data.get("type","")=="captouch" and data.get("value")==1 and data["change"])
-
-def loop(data={}):
-    for w in worms:
-        w.draw()
-        w.move()
-    
-    hardware.display_update()
-
-def run(data={}):
-    init()
-    event.the_engine.userloop = loop
-    event.the_engine.eventloop()    
+# To run standalone:
+#app.run()
+#app.engine.eventloop()
 
 #Known problems:
 #ctx.rotate(math.pi) turns the display black until powercycled
-#ctx.clip() causes crashes
-
+#ctx.clip() causes crashes
\ No newline at end of file
diff --git a/python_payload/event.py b/python_payload/event.py
index 8c1d2e66a2c9bec2718163768b94da6c5c73a80b..599c8c80d849f862a924e3c2049185b77449ca33 100644
--- a/python_payload/event.py
+++ b/python_payload/event.py
@@ -37,12 +37,23 @@ class Engine():
         self.remove_timed(group_id)
 
     def remove_timed(self,group_id):
+        #print("before:",len(self.events_timed))
         self.events_timed = [event for event in self.events_timed if event.group_id!=group_id]
         self._sort_timed()
+        #print("after",len(self.events_timed))
     
     def remove_input(self,group_id):
         self.events_input = [event for event in self.events_input if event.group_id!=group_id]
 
+    def register_main_loop(self,loop,enable=True):
+        if enable:
+            #print ("new userloop",loop)
+            #loop()
+            self.userloop = loop
+        elif self.userloop == loop:
+            #print ("removed userloop")
+            self.userloop = None
+
     def _sort_timed(self):
         self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline)
 
@@ -102,7 +113,7 @@ class Engine():
                  entry["change"] = False
 
             #find and trigger the events q
-            triggered_events = list(filter(lambda e: e.condition(entry),self.events_input))
+            triggered_events = list(filter(lambda e: e.enabled and e.condition(entry),self.events_input))
             #print (triggered_events)
             #map(lambda e: e.trigger(d), triggered_events)
             for e in triggered_events:
@@ -111,7 +122,8 @@ class Engine():
         self.last_input_state=input_state        
         
     def _handle_userloop(self):
-        if self.userloop:
+        if not self.userloop is None:
+            #print("userloop",self.userloop)
             self.userloop()
         
     def _eventloop_single(self):
@@ -134,17 +146,21 @@ class Engine():
             time.sleep(0.005)
         
 class Event():
-    def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None):
+    def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None,enabled=False):
         #print (action)
         self.name = name
         self.eventtype = None
         self.data = data
         self.action = action
         self.condition = condition
+        self.enabled = enabled
         if not condition:
             self.condition = lambda x: True
         self.group_id=group_id
-        the_engine.add(self)
+        
+        if enabled:
+            self.set_enabled()
+            
             
         
     def trigger(self,triggerdata={}):
@@ -153,11 +169,22 @@ class Event():
             triggerdata.update(self.data)
             self.action(triggerdata)
     
+    def set_enabled(self,enabled=True):
+        self.enabled=enabled
+        if enabled:
+            the_engine.add(self)
+        else:
+            self.remove()
+
     def remove(self):
-        if self in the_engine.events_input:
+        print ("remove",self)
+        while self in the_engine.events_input:
+            print ("from input")
             the_engine.events_input.remove(self)
-        if self in the_engine.events_timed:
+        while self in the_engine.events_timed:
+            print("from timed")
             the_engine.events_timed.remove(self)
+            the_engine._sort_timed()
 
 class EventTimed(Event):
     def __init__(self,ms,name="timer", *args, **kwargs):
@@ -172,23 +199,48 @@ class EventTimed(Event):
         return ("event on tick {} ({})".format(self.deadline,self.name))
 
 
+#hack, make this oo
+def on_restart(data):
+    print("loop sequence")
+    obj = data["object"]
+    if obj.is_running:
+        obj.start()
+
+
+
 class Sequence():
     def __init__(self,bpm=60,loop=True,steps=16,action=None):
         self.group_id = random.randint(0,100000000)
         self.bpm = bpm
+        self.steps = steps
+        self.repeat_event = None
+        self.loop = loop
+        self.events = []
+        self.is_running = False
+        
         if not action:
             self.action = lambda data: print("step {}".format(data.get("step")))
         else:
             self.action = action
-        stepsize_ms = int(60*1000/bpm)
-        for i in range(steps):
-            EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}, group_id=self.group_id)
-        
-        if loop:
-            EventTimed(stepsize_ms*steps,name="loop", group_id=self.group_id, action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action))
-
-    def remove(self):
-        the_engine.remove_timed(self.group_id)
+    
+    def start(self):
+        if self.is_running: self.stop()
+        stepsize_ms = int(60*1000/self.bpm)
+        for i in range(self.steps):
+            print("adding sequence event ", i)
+            self.events.append(EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}, group_id=self.group_id,enabled=True))
+        if self.loop:
+            self.repeat_event=EventTimed(stepsize_ms*self.steps,name="loop", group_id=self.group_id, enabled=True, action=on_restart, data={"object":self})
+        self.is_running=True
+    
+    def stop(self):
+        #for e in self.events: e.remove()
+        print("sequence stop")
+        the_engine.remove_timed(group_id=self.group_id)
+        self.events = []
+        if self.repeat_event: self.repeat_event.remove()
+        self.is_running=False
+    
         
 global the_engine
 the_engine = Engine()
\ No newline at end of file
diff --git a/python_payload/harmonic_demo.py b/python_payload/harmonic_demo.py
index 3207a09cd26f33fccfa514f2a6e899ae07bb52d1..95e73355e5525b444c7987b9b1c44546460c4a7b 100644
--- a/python_payload/harmonic_demo.py
+++ b/python_payload/harmonic_demo.py
@@ -58,3 +58,19 @@ def foreground():
     tmp = chord_index
     chord_index = -1
     set_chord(tmp)
+
+from application import Application
+class HarmonicApp(Application):
+    def on_init(self):
+        init()
+        #foreground()
+    
+    def on_foreground(self):
+        #foreground()
+        pass
+    
+    def main_foreground(self):
+        print ("hererer")
+        run()
+
+app=HarmonicApp("harmonic")
\ No newline at end of file
diff --git a/python_payload/melodic_demo.py b/python_payload/melodic_demo.py
index 8ffee2979dd158cbfcf233259d63686ca92316bb..06878ead5db0d7a18e69c16e2f81d44075a382e1 100644
--- a/python_payload/melodic_demo.py
+++ b/python_payload/melodic_demo.py
@@ -65,3 +65,17 @@ def init():
 
 def foreground():
     adjust_playing_field_to_octave()
+
+from application import Application
+
+class MelodicApp(Application):
+    def on_init(self):
+        init()
+    
+    def on_foreground(self):
+        foreground()
+    
+    def main_foreground(self):
+        run()
+
+app=MelodicApp("melodic")
\ No newline at end of file
diff --git a/python_payload/menu.py b/python_payload/menu.py
index e4ec935f5610b0fa0ba480b15e379e4e11e5966c..885bb9f5de86dc9489bc9fac13716da16f336780 100644
--- a/python_payload/menu.py
+++ b/python_payload/menu.py
@@ -100,6 +100,15 @@ class MenuItem():
         if self.action:
             self.action(data)
 
+class MenuItemApp(MenuItem):
+    def __init__(self,app):
+        super().__init__(name=app.title)
+        self.target = app
+    
+    def enter(self,data={}):
+        if self.target:
+            self.target.run()
+
 class MenuItemSubmenu(MenuItem):
     def __init__(self,submenu):
         super().__init__(name=submenu.name)
@@ -168,37 +177,45 @@ def on_release(d):
     
 def on_enter(d):
     if active_menu is None:
+        
+        #TODO this should not bee needed...
         event.the_engine.userloop=None
         menu_back()
         return
-    else:
+
+    if active_menu:
         active_menu.get_hovered_item().enter()
+        render()
+    else:
+        return
+
     
 event.Event(name="menu rotation button",group_id="menu",
-    condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1 ,
-    action=on_scroll
+    condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1,
+    action=on_scroll, enabled=True
 )
 
 event.Event(name="menu rotation captouch",group_id="menu",
     condition=lambda e: e["type"] =="captouch" and not e["change"] and abs(e["value"])==1 and e["index"]==2,
-    action=on_scroll_captouch
+    action=on_scroll_captouch, enabled=False
 )
 
 event.Event(name="menu rotation button release",group_id="menu",
     condition=lambda e: e["type"] =="button" and e["change"] and e["value"] ==0,
-    action=on_release
+    action=on_release, enabled=True
 )
 
 event.Event(name="menu button enter",group_id="menu",
-    condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2,
-    action=on_enter
+    condition=lambda e: e["type"] =="button" and e["change"] and e["from"] == 2,
+    action=on_enter, enabled=True
 )
 
 def render():
     print (active_menu)
     if active_menu is None:
         return
-    hardware.get_ctx().rectangle(-120,-120,240,240).rgb(0,0,0).fill()
+    
+    ui.the_ctx.rectangle(-120,-120,240,240).rgb(0,0,0).fill()
     active_menu.draw()
     #hardware.display_update()
 
@@ -206,9 +223,18 @@ def set_active_menu(menu):
     global active_menu
     active_menu = menu
 
+def menu_disable():
+    global active_menu
+    if active_menu:
+        menu_stack.append(active_menu)
+        active_menu=None
+
 def menu_back():
     if not menu_stack:
         return
 
     previous = menu_stack.pop()
-    set_active_menu(previous)
\ No newline at end of file
+
+    set_active_menu(previous)
+    render()
+    
\ No newline at end of file
diff --git a/python_payload/ui.py b/python_payload/ui.py
index c47d5baae43856a24e5c38b6498bc78d3e2567cc..3f8a79df7491c802ffe753ee88d7b8b74a72550a 100644
--- a/python_payload/ui.py
+++ b/python_payload/ui.py
@@ -4,30 +4,78 @@ import math
 import time
 from math import sin,cos,pi
 
-
+import gc
+
+WIDTH = 240
+HEIGHT = 240
+
+#Define a few RGB (0.0 to 1.0) colors
+BLACK = (0,0,0)
+RED = (1,0,0)
+GREEN = (0,1,0)
+BLUE = (0,0,1)
+WHITE = (1,1,1)
+GREY = (0.5,0.5,0.5)
 GO_GREEN = (63/255,255/255,33/53)
 PUSH_RED = (251/255,72/255,196/255)
 
+the_ctx = hardware.get_ctx()
+
+# Utility functions
+def xy_from_polar(r,deg):
+    #rad = deg/180*math.pi
+
+    return (
+        r * math.sin(deg), #x
+        r * math.cos(deg)  #y
+    )
+
+#def ctx_circle(self, x,y, radius, arc_from = -math.pi, arc_to = math.pi):
+#    return self.arc(x,y,radius,arc_from,arc_to,True)
+
+#the_ctx.circle = ctx_circle
+
+def randrgb():
+    return ((random.random(),random.random(),random.random()))
 
 class UIElement():
     def __init__(self,origin=(0,0)):
         self.children = []
         self.origin = origin
-        self.ctx = hardware.get_ctx()
+        self.ctx = the_ctx
     
     def draw(self, offset=(0,0)):
         pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
 
-        self._draw(pos)
+        
         for child in self.children:
             child.draw(pos)
 
+        self._draw(pos)
+
     def _draw(self,pos):
         pass
 
     def add(self, child):
         self.children.append(child)
 
+class Viewport(UIElement):
+    def _draw(self,pos):
+        self.ctx.rgb(0.3,0.3,0.3).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
+
+class Circle(UIElement):
+    def __init__(self,radius,color=PUSH_RED,arc_from=-math.pi, arc_to=math.pi, *args, **kwargs):
+        self.radius = radius
+        self.color = color
+        self.arc_from = arc_from
+        self.arc_to = arc_to
+        super().__init__()
+    
+    def _draw(self,pos):
+        (x,y)=pos
+        self.ctx.move_to(x,y).rgb(*self.color).arc(x,y,self.radius,self.arc_from,self.arc_to,True).fill()
+
+
 class Text(UIElement):
     def __init__(self,s="foo"):
         self.s = s
@@ -40,9 +88,7 @@ class Text(UIElement):
 
 class Icon(UIElement):
     def __init__(self,label="?", size=60):
-        self.bg_r = random.random()
-        self.bg_g = random.random()
-        self.bg_b = random.random()
+        self.bg = (random.random(),random.random(),random.random())
         self.fg = 0
         self.label=label
         self.size=size
@@ -50,8 +96,8 @@ class Icon(UIElement):
         super().__init__()
 
     def _draw(self,pos):
-        x = int(pos[0])
-        y = int(pos[1])
+        #print("ui.Icon._draw()")
+        (x,y) = pos
 
         self.ctx.text_align = self.ctx.CENTER
         self.ctx.text_baseline = self.ctx.MIDDLE
@@ -63,15 +109,23 @@ class Icon(UIElement):
                 y-hs/2,
                 hs,hs,hs//2
             ).fill()
-        self.ctx.move_to(x,y).rgb(self.bg_r,self.bg_g,self.bg_b).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
-        #self.ctx.move_to(x,y-self.size/2).rgb(self.bg_r,self.bg_g,self.bg_b).
-        #.round_rectangle(
-        #    x-self.size/2,
-        #    y-self.size/2,
-        #    self.size,self.size,self.size//2
-        #).fill()
+        
+        self.ctx.move_to(x,y).rgb(*self.bg).arc(x,y,self.size/2,-math.pi,math.pi,True).fill()
+        #self.ctx.move_to(x,y).rgb(self.bg_r,self.bg_g,self.bg_b).circle(x,y,self.size/2).fill()
+        
         self.ctx.rgb(1,1,1).move_to(x,y).text(self.label)
 
+class IconFlower(Icon):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.petal_size=size/2
+        self.petal_count=5
+    
+    def _draw(self,pos):
+        (x,y)=pos
+
+
+
 class IconValue(Icon):
     def __init__(self, value=0, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -89,6 +143,7 @@ class IconValue(Icon):
 
 
 
+
 class GroupStackedVertical(UIElement):
     pass
 
@@ -104,10 +159,12 @@ class GroupRing(UIElement):
         pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
         self._draw(pos)
         for index in range(len(self.children)):
+            #print("child",index)
             child = self.children[index]
             angle = 2*math.pi/len(self.children)*index+self.angle_offset
             x = math.sin(angle)*self.r+pos[0]
             y = -math.cos(angle)*self.r+pos[1]
+            #print("pos",(x,y))
             child.draw(offset=(x,y))
 
     def _draw(self,pos):