diff --git a/sim/fakes/ctx.py b/sim/fakes/ctx.py
index 71ce397b7c43bf72d851731aa8bfb9d21755fc9b..f2ed18b8e3fa4a01a35366b22888ee369cccc52f 100644
--- a/sim/fakes/ctx.py
+++ b/sim/fakes/ctx.py
@@ -103,9 +103,39 @@ class Wasm:
     def ctx_render_ctx(self, ctx, dctx):
         return self._i.exports.ctx_render_ctx(ctx, dctx)
 
+    def stbi_load_from_memory(self, buf):
+        p = self.malloc(len(buf))
+        mem = self._i.exports.memory.uint8_view(p)
+        mem[0 : len(buf)] = buf
+        wh = self.malloc(4 * 3)
+        res = self._i.exports.stbi_load_from_memory(p, len(buf), wh, wh + 4, wh + 8, 4)
+        whmem = self._i.exports.memory.uint32_view(wh // 4)
+        r = (res, whmem[0], whmem[1], whmem[2])
+        self.free(p)
+        self.free(wh)
+
+        res, w, h, c = r
+        b = self._i.exports.memory.uint8_view(res)
+        if c == 3:
+            return r
+        for j in range(h):
+            for i in range(w):
+                b[i * 4 + j * w * 4 + 0] = int(
+                    b[i * 4 + j * w * 4 + 0] * b[i * 4 + j * w * 4 + 3] / 255
+                )
+                b[i * 4 + j * w * 4 + 1] = int(
+                    b[i * 4 + j * w * 4 + 1] * b[i * 4 + j * w * 4 + 3] / 255
+                )
+                b[i * 4 + j * w * 4 + 2] = int(
+                    b[i * 4 + j * w * 4 + 2] * b[i * 4 + j * w * 4 + 3] / 255
+                )
+        return r
+
 
 _wasm = Wasm()
 
+_img_cache = {}
+
 
 class Context:
     """
@@ -135,7 +165,7 @@ class Context:
 
     @image_smoothing.setter
     def image_smoothing(self, v):
-        self._emit(f"imageSmoothing 0")
+        self._emit(f"imageSmoothing {v}")
 
     @property
     def text_align(self):
@@ -279,17 +309,15 @@ class Context:
         )
         return self
 
-    def image(self, path, x, y, width, height):
-        # TODO: replace with base64 encoded, decoded version of image
-        self._emit(f"save")
-        self._emit(f"rectangle {x} {y} {width} {height}")
-        self._emit(f"rgba 0.5 0.5 0.5 0.5")
-        self._emit(f"fill")
-        self._emit(f"rectangle {x} {y} {width} {height}")
-        self._emit(f"gray 1.0")
-        self._emit(f"lineWidth 1")
-        self._emit(f"stroke")
-        self._emit(f"restore")
+    def image(self, path, x, y, w, h):
+        if not path in _img_cache:
+            buf = open(path, "rb").read()
+            _img_cache[path] = _wasm.stbi_load_from_memory(buf)
+        img, width, height, components = _img_cache[path]
+        _wasm.ctx_define_texture(
+            self._ctx, path, width, height, width * components, RGBA8, img, 0
+        )
+        _wasm.ctx_draw_texture(self._ctx, path, x, y, w, h)
         return self
 
     def rectangle(self, x, y, width, height):
diff --git a/sim/wasm/build.sh b/sim/wasm/build.sh
index b66dbc532b845b40095811493468df3389c6906e..b4cac9a183db9b84fe8e39158d4d45eb816d9480 100755
--- a/sim/wasm/build.sh
+++ b/sim/wasm/build.sh
@@ -18,7 +18,7 @@ emcc ctx.c \
     -I ../../components/ctx/ \
     -I ../../components/ctx/fonts/ \
     -D SIMULATOR \
-    -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_new_drawlist,_ctx_parse,_ctx_apply_transform,_ctx_text_width,_ctx_render_ctx,_ctx_define_texture,_ctx_draw_texture,_ctx_destroy,_malloc,_free \
+    -s EXPORTED_FUNCTIONS=_ctx_new_for_framebuffer,_ctx_new_drawlist,_ctx_parse,_ctx_apply_transform,_ctx_text_width,_ctx_render_ctx,_ctx_define_texture,_ctx_draw_texture,_ctx_destroy,_stbi_load_from_memory,_malloc,_free \
     --no-entry -flto -O3 \
     -o ctx.wasm
 
diff --git a/sim/wasm/ctx.wasm b/sim/wasm/ctx.wasm
index a7bbe5dd123872f32db40f6074af8c82b8ae5b11..68a2afc1994c401992ff5359484f2fc88b0e66ee 100755
Binary files a/sim/wasm/ctx.wasm and b/sim/wasm/ctx.wasm differ