diff --git a/firmware/.gitignore b/firmware/.gitignore
index 8af1ba9..3396a99 100644
--- a/firmware/.gitignore
+++ b/firmware/.gitignore
@@ -5,3 +5,5 @@ sdkconfig.old
 *.swp
 /main/font/*.c
 /main/img/*.c
+/assets/moon.png
+/assets/star.png
diff --git a/firmware/assets/moon_orig.png b/firmware/assets/moon_orig.png
new file mode 100644
index 0000000..8bbb246
Binary files /dev/null and b/firmware/assets/moon_orig.png differ
diff --git a/firmware/assets/star_orig.png b/firmware/assets/star_orig.png
new file mode 100644
index 0000000..0c6705d
Binary files /dev/null and b/firmware/assets/star_orig.png differ
diff --git a/firmware/default.nix b/firmware/default.nix
index 950df5e..c87ec3d 100644
--- a/firmware/default.nix
+++ b/firmware/default.nix
@@ -2,43 +2,6 @@
   perSystem = { libMy, pkgs, ... }:
   let
     genImgsPy = pkgs.python3.withPackages (ps: with ps; [ pillow ]);
-    genImgs = pkgs.writeScriptBin "gen-imgs" ''
-      #!${genImgsPy}/bin/python
-      import itertools
-      import os
-
-      from PIL import Image
-
-      imgs = ['bg.png']
-      for fname in imgs:
-        with Image.open(os.path.join('assets/', fname)) as img:
-          w = img.width
-          h = img.height
-
-          data = [(r >> 3) << 11 | (g >> 2) << 5 | (b >> 3) for r, g, b in img.getdata()]
-
-        basename = os.path.splitext(fname)[0]
-        with open(os.path.join('main/img/', basename + '.c'), 'w') as f:
-          f.write(
-            '#include <inttypes.h>\n\n'
-            '#include "lvgl.h"\n\n'
-            'static const uint16_t _data[] = {\n')
-
-          for group in itertools.batched(data, 20):
-            f.write('  ' + ' '.join(map(lambda i: f'{i:#x},', group)) + '\n')
-
-          f.write('};\n\n')
-
-          f.write(
-            f'const lv_image_dsc_t ui_img_{basename} = {{\n'
-             '  .header.magic = LV_IMAGE_HEADER_MAGIC,\n'
-             '  .header.cf = LV_COLOR_FORMAT_RGB565,\n'
-            f'  .header.w = {w},\n'
-            f'  .header.h = {h},\n'
-             '  .data_size = sizeof(_data) * 2,\n'
-             '  .data = (const uint8_t *)_data,\n'
-             '};\n')
-    '';
   in
   {
     devenv.shells.firmware = libMy.withRootdir {
@@ -46,7 +9,7 @@
         esp-idf-esp32s3
         picocom
         lv_font_conv
-        genImgs
+        imagemagick
       ];
 
       env = {
@@ -63,6 +26,12 @@
               --format lvgl --lv-include lvgl.h --lv-font-name lv_font_tungsten_"$s" -o main/font/tungsten_"$s".c
           done
         '';
+        gen-imgs.exec = ''
+          magick assets/moon_orig.png -resize 180x180 assets/moon.png
+          magick assets/star_orig.png -resize 180x180 assets/star.png
+
+          ${genImgsPy}/bin/python gen-imgs.py
+        '';
       };
     };
   };
diff --git a/firmware/gen-imgs.py b/firmware/gen-imgs.py
new file mode 100755
index 0000000..27d7bc0
--- /dev/null
+++ b/firmware/gen-imgs.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+import itertools
+import os
+import struct
+
+from PIL import Image
+
+def rgb2h(r, g, b):
+  return (r >> 3) << 11 | (g >> 2) << 5 | (b >> 3)
+
+def bytes_line(bs):
+  return '  ' + ' '.join(map(lambda i: f'{i:#x},', bs)) + '\n'
+
+imgs = ['bg.png', 'moon.png', 'star.png']
+for fname in imgs:
+  alpha = []
+  with Image.open(os.path.join('assets/', fname)) as img:
+    w = img.width
+    h = img.height
+
+    if img.has_transparency_data:
+      has_alpha = True
+      data = [rgb2h(r, g, b) for r, g, b, _ in img.getdata()]
+      alpha = [a for _, _, _, a in img.getdata()]
+    else:
+      data = [rgb2h(r, g, b) for r, g, b in img.getdata()]
+
+  basename = os.path.splitext(fname)[0]
+  with open(os.path.join('main/img/', basename + '.c'), 'w') as f:
+    f.write(
+      '#include <inttypes.h>\n\n'
+      '#include "lvgl.h"\n\n'
+      'static const uint8_t _data[] = {\n')
+
+    for group in itertools.batched(data, 10):
+      bs = bytearray()
+      for c in group:
+        bs += struct.pack('<H', c)
+
+      f.write(bytes_line(bs))
+    for group in itertools.batched(alpha, 20):
+      f.write(bytes_line(group))
+
+    f.write('};\n\n')
+
+    f.write(
+      f'const lv_image_dsc_t ui_img_{basename} = {{\n'
+       '  .header.magic = LV_IMAGE_HEADER_MAGIC,\n'
+      f'  .header.cf = LV_COLOR_FORMAT_RGB565{"A8" if alpha else ""},\n'
+      f'  .header.w = {w},\n'
+      f'  .header.h = {h},\n'
+       '  .data_size = sizeof(_data),\n'
+       '  .data = _data,\n'
+       '};\n')
diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt
index 388d9e4..d318757 100644
--- a/firmware/main/CMakeLists.txt
+++ b/firmware/main/CMakeLists.txt
@@ -1,3 +1,3 @@
 idf_component_register(
-  SRCS "valconomy.c" "ui.c" "lcd.c" "usb.c" "font/tungsten_40.c" "font/tungsten_180.c" "img/bg.c"
+  SRCS "valconomy.c" "ui.c" "lcd.c" "usb.c" "font/tungsten_40.c" "font/tungsten_180.c" "img/bg.c" "img/moon.c" "img/star.c"
   INCLUDE_DIRS ".")
diff --git a/firmware/main/ui.c b/firmware/main/ui.c
index 9fa50cd..98013a8 100644
--- a/firmware/main/ui.c
+++ b/firmware/main/ui.c
@@ -14,6 +14,23 @@ static lv_obj_t *o_container = NULL;
 static lv_anim_timeline_t *at_active = NULL;
 static lv_obj_t *o_active = NULL;
 
+static const void* imgfont_get_path(
+    const lv_font_t *font, uint32_t unicode, uint32_t unicode_next, int32_t *offset_y, void *user_data) {
+  LV_UNUSED(font);
+  LV_UNUSED(unicode_next);
+  LV_UNUSED(offset_y);
+  LV_UNUSED(user_data);
+
+  switch (unicode) {
+  case 0x1F319:
+    return &ui_img_moon;
+  case 0x2B50:
+    return &ui_img_star;
+  default:
+    return NULL;
+  }
+}
+
 static void b_cfg_cb(lv_event_t *e) {
   lv_obj_t *box = lv_msgbox_create(NULL);
   lv_msgbox_add_title(box, "Hello");
@@ -56,7 +73,7 @@ void val_ui_none() {
   lv_obj_t *l_main = lv_label_create(o_active);
   lv_obj_add_style(l_main, &s_hero, 0);
   lv_obj_center(l_main);
-  lv_label_set_text_static(l_main, "HELLO MS!");
+  lv_label_set_text_static(l_main, "HELLO \U0001F319\u2B50!");
 
   lv_obj_t *l_subtitle = lv_label_create(o_active);
   lv_obj_add_style(l_subtitle, &s_subtitle, 0);
@@ -108,7 +125,7 @@ void val_ui_none() {
   lv_anim_timeline_add(at_active, 0, &a_sub);
   lv_anim_timeline_add(at_active, 750, &a_hello_out);
   lv_anim_timeline_add(at_active, 1000, &a_val);
-  lv_anim_timeline_start(at_active);
+  // lv_anim_timeline_start(at_active);
 }
 
 void val_lvgl_ui(lv_display_t *disp) {
@@ -125,8 +142,12 @@ void val_lvgl_ui(lv_display_t *disp) {
     font_normal);
   // lv_sysmon_hide_performance(disp);
 
+  lv_font_t *f_hero_emoji = lv_imgfont_create(180, imgfont_get_path, NULL);
+  assert(f_hero_emoji);
+  f_hero_emoji->fallback = &lv_font_tungsten_180;
+
   lv_style_init(&s_hero);
-  lv_style_set_text_font(&s_hero, &lv_font_tungsten_180);
+  lv_style_set_text_font(&s_hero, f_hero_emoji);
   lv_style_set_text_color(&s_hero, color_text_hero);
 
   lv_style_init(&s_subtitle);
diff --git a/firmware/main/ui.h b/firmware/main/ui.h
index 5df141e..9d21da3 100644
--- a/firmware/main/ui.h
+++ b/firmware/main/ui.h
@@ -6,5 +6,7 @@ LV_FONT_DECLARE(lv_font_tungsten_40)
 LV_FONT_DECLARE(lv_font_tungsten_180)
 
 LV_IMAGE_DECLARE(ui_img_bg);
+LV_IMAGE_DECLARE(ui_img_moon);
+LV_IMAGE_DECLARE(ui_img_star);
 
 void val_lvgl_ui(lv_display_t *disp);
diff --git a/firmware/sdkconfig.defaults b/firmware/sdkconfig.defaults
index c00aceb..db1704e 100644
--- a/firmware/sdkconfig.defaults
+++ b/firmware/sdkconfig.defaults
@@ -21,9 +21,11 @@ CONFIG_ESP_WIFI_DPP_SUPPORT=y
 CONFIG_FREERTOS_HZ=1000
 CONFIG_TINYUSB_DEBUG_LEVEL=0
 CONFIG_TINYUSB_HID_COUNT=1
+CONFIG_LV_DEF_REFR_PERIOD=24
 CONFIG_LV_USE_LOG=y
 CONFIG_LV_LOG_PRINTF=y
 CONFIG_LV_FONT_MONTSERRAT_24=y
 CONFIG_LV_USE_SYSMON=y
 CONFIG_LV_USE_PERF_MONITOR=y
+CONFIG_LV_USE_IMGFONT=y
 CONFIG_IDF_EXPERIMENTAL_FEATURES=y