diff --git a/sim/fakes/ctx.py b/sim/fakes/ctx.py
index f5606dbd73b1d1a86b944df25e47fc69cd97cc76..d9f809ac4cc4daa8e3ee73b6af3957d274b6c032 100644
--- a/sim/fakes/ctx.py
+++ b/sim/fakes/ctx.py
@@ -61,6 +61,17 @@ class Wasm:
         args = [float(a) for a in args]
         return self._i.exports.ctx_apply_transform(ctx, *args)
 
+    def ctx_text_width(self, ctx, text):
+        s = text.encode('utf-8')
+        slen = len(s) + 1
+        p = self.malloc(slen)
+        mem = self._i.exports.memory.uint8_view(p)
+        mem[0:slen-1] = s
+        mem[slen-1] = 0
+        res = self._i.exports.ctx_text_width(ctx, p)
+        self.free(p)
+        return res
+
 _wasm = Wasm()
 
 class Ctx:
@@ -84,6 +95,7 @@ class Ctx:
 
         self.text_align = 'start'
         self.text_baseline = 'alphabetic'
+        self.font_size = 10.0
 
     def _get_fb(self):
         return _wasm._i.exports.memory.uint8_view(self._fb)
@@ -92,7 +104,7 @@ class Ctx:
         _wasm.ctx_parse(self._ctx, text)
 
     def move_to(self, x, y):
-        self._emit(f"moveTo {x} {x}")
+        self._emit(f"moveTo {int(x)} {int(y)}")
         return self
 
     def rgb(self, r, g, b):
@@ -102,17 +114,18 @@ class Ctx:
             r /= 255.0
             g /= 255.0
             b /= 255.0
-        self._emit(f"rgb {r} {g} {b}")
+        self._emit(f"rgb {r:.3f} {g:.3f} {b:.3f}")
         return self
 
     def text(self, s):
         self._emit(f"textAlign {self.text_align}")
         self._emit(f"textBaseline {self.text_baseline}")
+        self._emit(f"fontSize {self.font_size}")
         self._emit(f"text \"{s}\"")
         return self
 
     def round_rectangle(self, x, y, width, height, radius):
-        self._emit(f"roundRectangle {x} {y} {width} {height} {radius}")
+        self._emit(f"roundRectangle {int(x)} {int(y)} {int(width)} {int(height)} {radius}")
         return self
 
     def rectangle(self, x, y, width, height):
@@ -124,5 +137,9 @@ class Ctx:
         return self
 
     def arc(self, x, y , radius, arc_from, arc_to, direction):
-        self._emit(f"arc {x} {y} {radius} {arc_from} {arc_to} {direction}")
-        return self
\ No newline at end of file
+        self._emit(f"arc {int(x)} {int(y)} {int(radius)} {arc_from:.4f} {arc_to:.4f} {1 if direction else 0}")
+        return self
+
+    def text_width(self, text):
+        self._emit(f"fontSize {self.font_size}")
+        return _wasm.ctx_text_width(self._ctx, text)
\ No newline at end of file
diff --git a/sim/fakes/gc.py b/sim/fakes/gc.py
new file mode 100644
index 0000000000000000000000000000000000000000..965169876cba59627d4e54efac1d2cdeb9cde2c8
--- /dev/null
+++ b/sim/fakes/gc.py
@@ -0,0 +1,2 @@
+def mem_free():
+    return 1337
diff --git a/sim/fakes/hardware.py b/sim/fakes/hardware.py
index 3afd56558c96b8cbdd0c9d13b5870fb570d3bace..9221eb499f12e6221259ac44fac23faa2e45326d 100644
--- a/sim/fakes/hardware.py
+++ b/sim/fakes/hardware.py
@@ -347,15 +347,14 @@ def update_leds():
 def set_global_volume_dB(a):
     pass
 
-
-def get_button(a):
+def get_button_state(left):
     _sim.process_events()
     _sim.render_gui_lazy()
 
     state = _sim.buttons.state()
-    if a == 1:
+    if left == 1:
         sub = state[:3]
-    elif a == 0:
+    elif left == 0:
         sub = state[3:6]
     else:
         return 0
@@ -369,6 +368,28 @@ def get_button(a):
     return 0
 
 
+menu_button_left = 0
+
+def menu_button_get():
+    return get_button_state(menu_button_left)
+
+def application_button_get():
+    return get_button_state(1 - menu_button_left)
+
+def left_button_get():
+    return get_button_state(1)
+
+def right_button_get():
+    return get_button_state(0)
+
+def menu_button_set_left(_broken):
+    global menu_button_left
+    menu_button_left = 1
+
+def menu_button_get_left():
+    return menu_button_left
+
+
 def get_captouch(a):
     _sim.process_events()
     _sim.render_gui_lazy()
diff --git a/sim/fakes/time.py b/sim/fakes/time.py
index 82a7ceeae8de6c8eac1d357f6333212a1476f0bf..62c6c33efa7a0579ae7bfaa6a9b11baf2d1cb477 100644
--- a/sim/fakes/time.py
+++ b/sim/fakes/time.py
@@ -1,8 +1,11 @@
+import importlib
+_time = importlib.import_module('_time')
 
 def sleep_ms(ms):
-    import _time
     _time.sleep(ms * 0.001)
 
 def ticks_ms():
-    import _time
     return int(_time.time() * 1000)
+
+def ticks_diff(a, b):
+    return a - b
\ No newline at end of file
diff --git a/sim/run.py b/sim/run.py
index 28a5d14da97a0e59da18b2f8ea11275ea7c84b3e..978df24502fd3bfb5afba56f062bd62c220bcfb0 100644
--- a/sim/run.py
+++ b/sim/run.py
@@ -8,37 +8,46 @@ import sys
 
 projectpath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 
-class Hook:
-    """
-    Hook implements a importlib.abc.Finder which overwrites resolution order to
-    more closely match the resolution order on the badge's Micropython
-    environment.
-    """
+import random
+import pygame
+import cmath
+import wasmer
+import wasmer_compiler_cranelift
+
+
+class UnderscoreFinder(importlib.abc.MetaPathFinder):
+    def __init__(self, builtin):
+        self.builtin = builtin
+
     def find_spec(self, fullname, path, target=None):
-        # Attempt to load from python_payload, then python_modules then
-        # sim/fakes. Afterwards, the normal import resolution order kicks in.
-        paths = [
-            os.path.join(projectpath, 'python_payload', fullname+'.py'),
-            os.path.join(projectpath, 'python_payload', fullname),
-            os.path.join(projectpath, 'python_modules', fullname+'.py'),
-            os.path.join(projectpath, 'python_modules', fullname),
-            os.path.join(projectpath, 'sim', 'fakes', fullname+'.py'),
-        ]
-        for p in paths:
-            if os.path.exists(p):
-                root = os.path.split(p)[:-1]
-                return PathFinder.find_spec(fullname, root)
-        # As we provide our own micropython-compatible time library, allow
-        # resolving the original CPython time through _time
         if fullname == '_time':
-            return BuiltinImporter.find_spec('time')
-        return None
+            return self.builtin.find_spec('time', path, target)
+        if fullname in ['random']:
+            print(fullname, path, target)
+            return self.builtin.find_spec(fullname, path, target)
 
-sys.meta_path.insert(0, Hook())
-sys.path_importer_cache.clear()
+
+#sys.meta_path.insert(0, Hook())
+
+sys.path = [
+    os.path.join(projectpath, 'python_payload'),
+    os.path.join(projectpath, 'sim', 'fakes'),
+]
+
+builtin = BuiltinImporter()
+pathfinder = PathFinder()
+underscore = UnderscoreFinder(builtin)
+sys.meta_path = [pathfinder, underscore]
 
 # Clean up whatever might have already been imported as `time`.
 import time
+print('aaaa', time)
 importlib.reload(time)
+print('bbbb', time)
+
+sys.path_importer_cache.clear()
+importlib.invalidate_caches()
+
+sys.modules['time'] = time
 
-import main
+import main
\ No newline at end of file
diff --git a/sim/wasm/build.sh b/sim/wasm/build.sh
index 4981d3a244d316a8de628f5185055010e5a41d80..72ccec89d02c5fa39cfcc7e707ff02af0f00e083 100755
--- a/sim/wasm/build.sh
+++ b/sim/wasm/build.sh
@@ -17,7 +17,7 @@ fi
 emcc ctx.c \
     -I ../../usermodule/uctx/uctx/ \
     -I ../../usermodule/uctx/fonts/ \
-    -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_parse,_ctx_apply_transform,_malloc,_free \
+    -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_parse,_ctx_apply_transform,_ctx_text_width,_malloc,_free \
     --no-entry -flto -O3 \
     -o ctx.wasm
 
diff --git a/sim/wasm/ctx.wasm b/sim/wasm/ctx.wasm
index 6e5300ff9f43b2f99051d670f5be14adbf4e08a5..75f892015ae30b6bdc3cc2189dad129e4c4b5d4f 100755
Binary files a/sim/wasm/ctx.wasm and b/sim/wasm/ctx.wasm differ