diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45e42b15fe316d499ca3d92f92172166f6a8de00..da4a17f9dd0c18d855c6a48407a89064bae89ceb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
+### Changed
+- `main.py` was moved into an app to allow easier reconfiguration of the
+  default app.  The new `main.py` points to the "old" one so behavior is not
+  changed.
 
 
 ## [v1.8] - 2019-08-27 11:38 - [HabaneroChilli]
diff --git a/preload/apps/analog_clock/__init__.py b/preload/apps/analog_clock/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c679a82ec20191acec37fa99519f1cb81e52fc71
--- /dev/null
+++ b/preload/apps/analog_clock/__init__.py
@@ -0,0 +1,331 @@
+# Adapted from https://github.com/muccc/flipdots/blob/master/scripts/clock.py
+import display
+from utime import sleep
+import utime
+import math
+import leds
+import buttons
+import ujson
+import os
+
+CONFIG_NAME = "clock.json"
+
+
+class Time:
+    def __init__(self, start=0):
+        self.time = start
+        self.wait_time = 0.95
+
+    def tick(self):
+        sleep(self.wait_time)
+        self.time += 1
+
+    @property
+    def second(self):
+        return self.time % 60
+
+    @property
+    def minute(self):
+        return (self.time / 60) % 60
+
+    @property
+    def hour(self):
+        return (self.time / 3600) % 24
+
+
+class Clock:
+    def __init__(
+        self,
+        sizex=80,
+        sizey=80,
+        radius=38,
+        offsetx=30,
+        hour_hand=True,
+        minute_hand=True,
+        second_hand=True,
+        console_out=False,
+        run_once=False,
+        update_interval=0,
+    ):
+        self.sizex = sizex
+        self.sizey = sizey
+        self.radius = radius
+        self.center = (int(self.sizex / 2), int(self.sizey / 2))
+        self.hour_hand = hour_hand
+        self.minute_hand = minute_hand
+        self.second_hand = second_hand
+        self.console_out = console_out
+        self.update_interval = (
+            update_interval if update_interval != 0 else (1 if self.second_hand else 30)
+        )
+        self.run_once = run_once
+        self.offsetx = offsetx
+        self.time = Time()
+        self.theme = 0
+        self.default_themes = [
+            {
+                "background": [0, 0, 0],
+                "center": [255, 255, 255],
+                "m1": [255, 255, 255],
+                "m5": [255, 255, 255],
+                "hour_hand": [255, 255, 255],
+                "minute_hand": [255, 255, 255],
+                "second_hand": [255, 255, 255],
+            },
+            {
+                "background": [130, 30, 70],
+                "center": [255, 255, 255],
+                "m1": [255, 255, 255],
+                "m5": [255, 255, 255],
+                "hour_hand": [255, 255, 255],
+                "minute_hand": [255, 255, 255],
+                "second_hand": [255, 255, 255],
+            },
+            {
+                "background": [0, 80, 0],
+                "center": [255, 255, 255],
+                "m1": [255, 255, 255],
+                "m5": [255, 255, 255],
+                "hour_hand": [255, 255, 255],
+                "minute_hand": [255, 255, 255],
+                "second_hand": [255, 255, 255],
+            },
+            {
+                "background": [0, 80, 80],
+                "center": [255, 255, 255],
+                "m1": [255, 255, 255],
+                "m5": [255, 255, 255],
+                "hour_hand": [255, 255, 255],
+                "minute_hand": [255, 255, 255],
+                "second_hand": [255, 255, 255],
+            },
+            {
+                "background": [255, 255, 255],
+                "center": [0, 0, 0],
+                "m1": [0, 0, 0],
+                "m5": [0, 0, 0],
+                "hour_hand": [0, 0, 0],
+                "minute_hand": [0, 0, 0],
+                "second_hand": [0, 0, 0],
+            },
+        ]
+        self.themes = self.default_themes
+
+        # check for config file
+        if CONFIG_NAME in os.listdir("."):
+            self.readConfig()
+        else:
+            self.writeConfig()
+
+        # load colors
+        self.setTheme(self.theme)
+
+    def readConfig(self):
+        with open(CONFIG_NAME, "r") as f:
+            try:
+                c = ujson.loads(f.read())
+                if (
+                    "themes" in c
+                    and len(c["themes"]) > 0
+                    and isinstance(c["themes"], list)
+                ):
+                    self.themes = c["themes"]
+                if "theme" and isinstance(c["theme"], int):
+                    self.theme = c["theme"]
+            except ValueError:
+                print("parsing %s failed" % CONFIG_NAME)
+
+    def writeConfig(self):
+        with open(CONFIG_NAME, "w") as f:
+            f.write(ujson.dumps({"theme": self.theme, "themes": self.themes}))
+
+    def setTheme(self, theme):
+        self.theme = theme % len(self.themes)
+        self.background_col = (
+            self.themes[self.theme]["background"]
+            if "background" in self.themes[self.theme]
+            else self.default_themes[0]["background"]
+        )
+        self.center_col = (
+            self.themes[self.theme]["center"]
+            if "center" in self.themes[self.theme]
+            else self.default_themes[0]["center"]
+        )
+        self.m1_col = (
+            self.themes[self.theme]["m1"]
+            if "m1" in self.themes[self.theme]
+            else self.default_themes[0]["m1"]
+        )
+        self.m5_col = (
+            self.themes[self.theme]["m5"]
+            if "m5" in self.themes[self.theme]
+            else self.default_themes[0]["m5"]
+        )
+        self.hour_hand_col = (
+            self.themes[self.theme]["hour_hand"]
+            if "hour_hand" in self.themes[self.theme]
+            else self.default_themes[0]["hour_hand"]
+        )
+        self.minute_hand_col = (
+            self.themes[self.theme]["minute_hand"]
+            if "minute_hand" in self.themes[self.theme]
+            else self.default_themes[0]["minute_hand"]
+        )
+        self.second_hand_col = (
+            self.themes[self.theme]["second_hand"]
+            if "second_hand" in self.themes[self.theme]
+            else self.default_themes[0]["second_hand"]
+        )
+
+    def loop(self):
+        colored = False
+        try:
+            with display.open() as disp:
+                button_pressed = False
+                while True:
+                    self.updateClock(disp)
+                    if self.run_once:
+                        break
+
+                    # check for button presses
+                    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
+                    if v == 0:
+                        button_pressed = False
+
+                    if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
+                        button_pressed = True
+                        self.setTheme(self.theme - 1)
+                        self.writeConfig()
+                    elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
+                        button_pressed = True
+                        self.setTheme(self.theme + 1)
+                        self.writeConfig()
+
+        except KeyboardInterrupt:
+            for i in range(11):
+                leds.set(i, (0, 0, 0))
+            return
+
+    def drawImage(self, image):
+        with display.open() as d:
+            d.clear()
+            for x in range(len(image)):
+                for y in range(len(image[x])):
+                    d.pixel(
+                        x + self.offsetx,
+                        y,
+                        col=(255, 255, 255) if image[x][y] else (0, 0, 0),
+                    )
+            d.update()
+
+    def updateClock(self, disp):
+        disp.clear(self.background_col)
+        localtime = utime.localtime()
+
+        disp.pixel(self.center[0] + self.offsetx, self.center[1], col=self.center_col)
+        hour_coords = self.circlePoint(
+            math.radians(
+                (((localtime[3] % 12) / 12.0) if localtime[3] else 0) * 360
+                + 270
+                + (localtime[4] / 2)
+            )
+        )
+        minute_coords = self.circlePoint(math.radians(localtime[4] * 6 + 270))
+        second_coords = self.circlePoint(math.radians(localtime[5] * 6 + 270))
+
+        for i in range(60):
+            degree = i * 6 + 90
+            radian = -math.radians(degree)
+            coords = self.circlePoint(radian)
+
+            if not i % 5:
+                self.addLine(disp, coords, self.center, 3, 1, col=self.m5_col)
+            else:
+                self.addLine(disp, coords, self.center, 1, col=self.m1_col)
+
+        if self.hour_hand:
+            self.addLine(
+                disp,
+                self.center,
+                hour_coords,
+                int(self.radius / 3),
+                1,
+                col=self.hour_hand_col,
+            )
+        if self.minute_hand:
+            self.addLine(
+                disp,
+                self.center,
+                minute_coords,
+                int(self.radius / 2),
+                col=self.minute_hand_col,
+            )
+        if self.second_hand:
+            self.addLine(
+                disp,
+                self.center,
+                second_coords,
+                self.radius - int(self.radius / 8.0),
+                col=self.second_hand_col,
+            )
+
+        if self.console_out:
+            for y in range(self.radius * 2):
+                line = ""
+                for x in range(self.radius * 2):
+                    line = line + (
+                        "."
+                        if image[(self.center[1] - self.radius) + y][
+                            (self.center[0] - self.radius) + x
+                        ]
+                        else " "
+                    )
+                print(line)
+
+        disp.update()
+
+    def circlePoint(self, t):
+        return (
+            int(round(self.radius * math.cos(t))) + self.center[0],
+            int(round(self.radius * math.sin(t))) + self.center[1],
+        )
+
+    def addLine(self, disp, source, aim, length, thickness=1, col=(255, 255, 255)):
+        vector = self.subVector(aim, source)
+        vector = self.normVector(vector)
+        destination = self.addVector(source, self.multiplyVector(vector, length))
+
+        disp.line(
+            round(source[0]) + self.offsetx,
+            round(source[1]),
+            round(destination[0]) + self.offsetx,
+            round(destination[1]),
+            col=col,
+            size=thickness,
+        )
+
+    def normVector(self, v):
+        length = math.sqrt(sum([i ** 2 for i in v]))
+        new_v = []
+        for i in range(len(v)):
+            new_v.append(v[i] / length)
+        return tuple(new_v)
+
+    def subVector(self, v1, v2):
+        res = []
+        for i in range(len(v1)):
+            res.append(v1[i] - v2[i])
+        return tuple(res)
+
+    def addVector(self, v1, v2):
+        res = []
+        for i in range(len(v1)):
+            res.append(v1[i] + v2[i])
+        return tuple(res)
+
+    def multiplyVector(self, v, multiplier):
+        return tuple([i * multiplier for i in v])
+
+
+clock = Clock()
+clock.loop()
diff --git a/preload/apps/analog_clock/metadata.json b/preload/apps/analog_clock/metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..3b3303d5ce09cf9c89f63fe2b2174b1958b0a118
--- /dev/null
+++ b/preload/apps/analog_clock/metadata.json
@@ -0,0 +1 @@
+{"name":"Analog Clock","description":"Analog Clock","category":"graphics","author":"markus","revision":-1}
diff --git a/preload/main.py b/preload/main.py
index c679a82ec20191acec37fa99519f1cb81e52fc71..7c8417e9ae1375779ef5a7c2fd4fdcca35cd36b7 100644
--- a/preload/main.py
+++ b/preload/main.py
@@ -1,331 +1,18 @@
-# Adapted from https://github.com/muccc/flipdots/blob/master/scripts/clock.py
-import display
-from utime import sleep
-import utime
-import math
-import leds
-import buttons
-import ujson
 import os
 
-CONFIG_NAME = "clock.json"
 
+def main():
+    # Try loading analog clock
+    default_app = "apps/analog_clock/__init__.py"
+    try:
+        with open(default_app, "r"):
+            pass
 
-class Time:
-    def __init__(self, start=0):
-        self.time = start
-        self.wait_time = 0.95
+        print("main.py: Loading " + default_app)
+        os.exec(default_app)
+    finally:
+        os.exit(1)
 
-    def tick(self):
-        sleep(self.wait_time)
-        self.time += 1
 
-    @property
-    def second(self):
-        return self.time % 60
-
-    @property
-    def minute(self):
-        return (self.time / 60) % 60
-
-    @property
-    def hour(self):
-        return (self.time / 3600) % 24
-
-
-class Clock:
-    def __init__(
-        self,
-        sizex=80,
-        sizey=80,
-        radius=38,
-        offsetx=30,
-        hour_hand=True,
-        minute_hand=True,
-        second_hand=True,
-        console_out=False,
-        run_once=False,
-        update_interval=0,
-    ):
-        self.sizex = sizex
-        self.sizey = sizey
-        self.radius = radius
-        self.center = (int(self.sizex / 2), int(self.sizey / 2))
-        self.hour_hand = hour_hand
-        self.minute_hand = minute_hand
-        self.second_hand = second_hand
-        self.console_out = console_out
-        self.update_interval = (
-            update_interval if update_interval != 0 else (1 if self.second_hand else 30)
-        )
-        self.run_once = run_once
-        self.offsetx = offsetx
-        self.time = Time()
-        self.theme = 0
-        self.default_themes = [
-            {
-                "background": [0, 0, 0],
-                "center": [255, 255, 255],
-                "m1": [255, 255, 255],
-                "m5": [255, 255, 255],
-                "hour_hand": [255, 255, 255],
-                "minute_hand": [255, 255, 255],
-                "second_hand": [255, 255, 255],
-            },
-            {
-                "background": [130, 30, 70],
-                "center": [255, 255, 255],
-                "m1": [255, 255, 255],
-                "m5": [255, 255, 255],
-                "hour_hand": [255, 255, 255],
-                "minute_hand": [255, 255, 255],
-                "second_hand": [255, 255, 255],
-            },
-            {
-                "background": [0, 80, 0],
-                "center": [255, 255, 255],
-                "m1": [255, 255, 255],
-                "m5": [255, 255, 255],
-                "hour_hand": [255, 255, 255],
-                "minute_hand": [255, 255, 255],
-                "second_hand": [255, 255, 255],
-            },
-            {
-                "background": [0, 80, 80],
-                "center": [255, 255, 255],
-                "m1": [255, 255, 255],
-                "m5": [255, 255, 255],
-                "hour_hand": [255, 255, 255],
-                "minute_hand": [255, 255, 255],
-                "second_hand": [255, 255, 255],
-            },
-            {
-                "background": [255, 255, 255],
-                "center": [0, 0, 0],
-                "m1": [0, 0, 0],
-                "m5": [0, 0, 0],
-                "hour_hand": [0, 0, 0],
-                "minute_hand": [0, 0, 0],
-                "second_hand": [0, 0, 0],
-            },
-        ]
-        self.themes = self.default_themes
-
-        # check for config file
-        if CONFIG_NAME in os.listdir("."):
-            self.readConfig()
-        else:
-            self.writeConfig()
-
-        # load colors
-        self.setTheme(self.theme)
-
-    def readConfig(self):
-        with open(CONFIG_NAME, "r") as f:
-            try:
-                c = ujson.loads(f.read())
-                if (
-                    "themes" in c
-                    and len(c["themes"]) > 0
-                    and isinstance(c["themes"], list)
-                ):
-                    self.themes = c["themes"]
-                if "theme" and isinstance(c["theme"], int):
-                    self.theme = c["theme"]
-            except ValueError:
-                print("parsing %s failed" % CONFIG_NAME)
-
-    def writeConfig(self):
-        with open(CONFIG_NAME, "w") as f:
-            f.write(ujson.dumps({"theme": self.theme, "themes": self.themes}))
-
-    def setTheme(self, theme):
-        self.theme = theme % len(self.themes)
-        self.background_col = (
-            self.themes[self.theme]["background"]
-            if "background" in self.themes[self.theme]
-            else self.default_themes[0]["background"]
-        )
-        self.center_col = (
-            self.themes[self.theme]["center"]
-            if "center" in self.themes[self.theme]
-            else self.default_themes[0]["center"]
-        )
-        self.m1_col = (
-            self.themes[self.theme]["m1"]
-            if "m1" in self.themes[self.theme]
-            else self.default_themes[0]["m1"]
-        )
-        self.m5_col = (
-            self.themes[self.theme]["m5"]
-            if "m5" in self.themes[self.theme]
-            else self.default_themes[0]["m5"]
-        )
-        self.hour_hand_col = (
-            self.themes[self.theme]["hour_hand"]
-            if "hour_hand" in self.themes[self.theme]
-            else self.default_themes[0]["hour_hand"]
-        )
-        self.minute_hand_col = (
-            self.themes[self.theme]["minute_hand"]
-            if "minute_hand" in self.themes[self.theme]
-            else self.default_themes[0]["minute_hand"]
-        )
-        self.second_hand_col = (
-            self.themes[self.theme]["second_hand"]
-            if "second_hand" in self.themes[self.theme]
-            else self.default_themes[0]["second_hand"]
-        )
-
-    def loop(self):
-        colored = False
-        try:
-            with display.open() as disp:
-                button_pressed = False
-                while True:
-                    self.updateClock(disp)
-                    if self.run_once:
-                        break
-
-                    # check for button presses
-                    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
-                    if v == 0:
-                        button_pressed = False
-
-                    if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
-                        button_pressed = True
-                        self.setTheme(self.theme - 1)
-                        self.writeConfig()
-                    elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
-                        button_pressed = True
-                        self.setTheme(self.theme + 1)
-                        self.writeConfig()
-
-        except KeyboardInterrupt:
-            for i in range(11):
-                leds.set(i, (0, 0, 0))
-            return
-
-    def drawImage(self, image):
-        with display.open() as d:
-            d.clear()
-            for x in range(len(image)):
-                for y in range(len(image[x])):
-                    d.pixel(
-                        x + self.offsetx,
-                        y,
-                        col=(255, 255, 255) if image[x][y] else (0, 0, 0),
-                    )
-            d.update()
-
-    def updateClock(self, disp):
-        disp.clear(self.background_col)
-        localtime = utime.localtime()
-
-        disp.pixel(self.center[0] + self.offsetx, self.center[1], col=self.center_col)
-        hour_coords = self.circlePoint(
-            math.radians(
-                (((localtime[3] % 12) / 12.0) if localtime[3] else 0) * 360
-                + 270
-                + (localtime[4] / 2)
-            )
-        )
-        minute_coords = self.circlePoint(math.radians(localtime[4] * 6 + 270))
-        second_coords = self.circlePoint(math.radians(localtime[5] * 6 + 270))
-
-        for i in range(60):
-            degree = i * 6 + 90
-            radian = -math.radians(degree)
-            coords = self.circlePoint(radian)
-
-            if not i % 5:
-                self.addLine(disp, coords, self.center, 3, 1, col=self.m5_col)
-            else:
-                self.addLine(disp, coords, self.center, 1, col=self.m1_col)
-
-        if self.hour_hand:
-            self.addLine(
-                disp,
-                self.center,
-                hour_coords,
-                int(self.radius / 3),
-                1,
-                col=self.hour_hand_col,
-            )
-        if self.minute_hand:
-            self.addLine(
-                disp,
-                self.center,
-                minute_coords,
-                int(self.radius / 2),
-                col=self.minute_hand_col,
-            )
-        if self.second_hand:
-            self.addLine(
-                disp,
-                self.center,
-                second_coords,
-                self.radius - int(self.radius / 8.0),
-                col=self.second_hand_col,
-            )
-
-        if self.console_out:
-            for y in range(self.radius * 2):
-                line = ""
-                for x in range(self.radius * 2):
-                    line = line + (
-                        "."
-                        if image[(self.center[1] - self.radius) + y][
-                            (self.center[0] - self.radius) + x
-                        ]
-                        else " "
-                    )
-                print(line)
-
-        disp.update()
-
-    def circlePoint(self, t):
-        return (
-            int(round(self.radius * math.cos(t))) + self.center[0],
-            int(round(self.radius * math.sin(t))) + self.center[1],
-        )
-
-    def addLine(self, disp, source, aim, length, thickness=1, col=(255, 255, 255)):
-        vector = self.subVector(aim, source)
-        vector = self.normVector(vector)
-        destination = self.addVector(source, self.multiplyVector(vector, length))
-
-        disp.line(
-            round(source[0]) + self.offsetx,
-            round(source[1]),
-            round(destination[0]) + self.offsetx,
-            round(destination[1]),
-            col=col,
-            size=thickness,
-        )
-
-    def normVector(self, v):
-        length = math.sqrt(sum([i ** 2 for i in v]))
-        new_v = []
-        for i in range(len(v)):
-            new_v.append(v[i] / length)
-        return tuple(new_v)
-
-    def subVector(self, v1, v2):
-        res = []
-        for i in range(len(v1)):
-            res.append(v1[i] - v2[i])
-        return tuple(res)
-
-    def addVector(self, v1, v2):
-        res = []
-        for i in range(len(v1)):
-            res.append(v1[i] + v2[i])
-        return tuple(res)
-
-    def multiplyVector(self, v, multiplier):
-        return tuple([i * multiplier for i in v])
-
-
-clock = Clock()
-clock.loop()
+if __name__ == "__main__":
+    main()