diff --git a/python_payload/apps/fil3s/browser.py b/python_payload/apps/fil3s/browser.py
index 1d03defa42b5f9ac68af349c538e821aec37cc4c..04648d16fcb3a175a4a39d21b766b7d11692e0c6 100644
--- a/python_payload/apps/fil3s/browser.py
+++ b/python_payload/apps/fil3s/browser.py
@@ -20,6 +20,7 @@ class Browser(ActionView):
     up_enabled: bool = False
     prev_enabled: bool = False
     next_enabled: bool = False
+    delete_enabled: bool = True
     current_pos = 0
     current_entry: tuple[str, str]
 
@@ -31,6 +32,10 @@ class Browser(ActionView):
     ) -> None:
         super().__init__()
 
+        self._delete_held_for = 0.0
+        self._delete_hold_time = 1.5
+        self._delete_require_release = False
+
         self.path = path
         self.navigate = navigate
         self.update_path = update_path
@@ -51,6 +56,8 @@ class Browser(ActionView):
             if self.current_pos < len(self.dir_entries) - 1:
                 self.current_pos += 1
                 self._update_position()
+        elif index == 0:
+            self._delete()
 
     def draw(self, ctx: Context) -> None:
         utils.fill_screen(ctx, theme.BACKGROUND)
@@ -72,15 +79,24 @@ class Browser(ActionView):
     def think(self, ins: InputState, delta_ms: int) -> None:
         super().think(ins, delta_ms)
 
-        for i in range(0, 5):
+        # Handle delete petal being held down
+        if ins.captouch.petals[0].pressed:
+            if not self._delete_require_release:
+                self._delete_held_for += delta_ms / 1000
+                if self._delete_held_for > self._delete_hold_time:
+                    self._delete_held_for = self._delete_hold_time
+                    self._delete_require_release = True
+                    self._on_action(0)
+        else:
+            self._delete_held_for = 0.0
+            self._delete_require_release = False
+        self.actions[0].progress = self._delete_held_for / self._delete_hold_time
+
+        for i in range(1, 5):
             if self.input.captouch.petals[i * 2].whole.pressed:
                 self._on_action(i)
                 return
 
-    def _is_dir(self, path: str) -> bool:
-        st_mode = uos.stat(path)[0]  # Can fail with OSError
-        return stat.S_ISDIR(st_mode)
-
     def _get_dir_entry(
         self, names: list[str]
     ) -> Generator[tuple[str, str], None, None]:
@@ -88,7 +104,7 @@ class Browser(ActionView):
             try:
                 if self.path + name == "/flash/sys/st3m":
                     yield (name, "\ue545")
-                elif self._is_dir(self.path + name):
+                elif utils.is_dir(self.path + name):
                     yield (name, "\ue2c7")
                 else:
                     yield (name, "\ue873")
@@ -121,7 +137,7 @@ class Browser(ActionView):
         old_path = self.path
         new_path = self.path + name
         try:
-            if self._is_dir(new_path):
+            if utils.is_dir(new_path):
                 self._change_path(new_path + "/")
             else:
                 self.update_path(self.path + name)
@@ -131,6 +147,24 @@ class Browser(ActionView):
             print(f"Failed to open {new_path}: {e}")
             self._change_path(old_path)
 
+    def _delete(self) -> None:
+        name = self.dir_entries[self.current_pos][0]
+        path = self.path + name
+
+        try:
+            if utils.is_dir(path):
+                utils.rmdirs(path)
+            else:
+                os.remove(path)
+                print(f"deleted file: {path}")
+
+            # refresh dir listing
+            self.current_pos = max(self.current_pos - 1, 0)
+            self._scan_path()
+        except Exception as e:
+            # TODO: Create error view
+            print(f"Failed to delete {path}: {e}")
+
     def _up(self) -> None:
         if not self.up_enabled or len(self.path) <= 1:
             return
@@ -144,7 +178,8 @@ class Browser(ActionView):
 
     def _update_actions(self) -> None:
         self.actions = [
-            Action(icon="\ue3e3", label="Menu", enabled=False),
+            # TODO: swap for a better icon
+            Action(icon="\ue3e3", label="Delete", enabled=self.delete_enabled),
             Action(icon="\ue409", label="Next", enabled=self.next_enabled),
             Action(icon="\ue876", label="Select"),
             Action(icon="\ue5c4", label="Back", enabled=self.up_enabled),
@@ -154,9 +189,18 @@ class Browser(ActionView):
     def _update_position(self) -> None:
         try:
             self.current_entry = self.dir_entries[self.current_pos]
-        except:
+        except Exception:
             self.current_entry = ("\ue002", "No files")
 
         self.prev_enabled = self.current_pos > 0
         self.next_enabled = self.current_pos < len(self.dir_entries) - 1
+        # disallow deleting st3m folder
+        name = self.dir_entries[self.current_pos][0]
+        self.delete_enabled = self.path + name not in [
+            "/flash",
+            "/flash/sys",
+            "/flash/sys/st3m",
+            "/sd",
+        ]
+
         self._update_actions()
diff --git a/python_payload/apps/fil3s/common/action_view.py b/python_payload/apps/fil3s/common/action_view.py
index 63cb097afa82b357e990040109eed9a5a52d4ec0..ea29742bd2ea8a9f1e809f4de4be55d1be27be0f 100644
--- a/python_payload/apps/fil3s/common/action_view.py
+++ b/python_payload/apps/fil3s/common/action_view.py
@@ -13,11 +13,15 @@ class Action:
     icon: str
     label: str
     enabled: bool
+    progress: float
 
-    def __init__(self, icon: str, label: str, enabled: bool = True) -> None:
+    def __init__(
+        self, icon: str, label: str, enabled: bool = True, progress: float = 0.0
+    ) -> None:
         self.icon = icon
         self.label = label
         self.enabled = enabled
+        self.progress = progress
 
 
 class ActionView(BaseView):
@@ -66,7 +70,12 @@ class ActionView(BaseView):
 
             if action.enabled:
                 utils.draw_circle(
-                    ctx, theme.PRIMARY, self.action_x[i], self.action_y[i], 18
+                    ctx,
+                    theme.PRIMARY,
+                    self.action_x[i],
+                    self.action_y[i],
+                    18,
+                    action.progress,
                 )
             else:
                 utils.draw_circle(
diff --git a/python_payload/apps/fil3s/common/utils.py b/python_payload/apps/fil3s/common/utils.py
index e77db4f2c01e575d42431b40ef59d3b3de301fa6..dd1e72b2f73500e2855b1f9b96cdbc145f3d56e1 100644
--- a/python_payload/apps/fil3s/common/utils.py
+++ b/python_payload/apps/fil3s/common/utils.py
@@ -1,5 +1,7 @@
-from math import pi
+from math import tau
 from ctx import Context
+import uos
+import stat
 
 
 def fill_screen(ctx: Context, color: tuple[float, float, float]) -> None:
@@ -13,9 +15,31 @@ def fill_screen(ctx: Context, color: tuple[float, float, float]) -> None:
 
 
 def draw_circle(
-    ctx: Context, color: tuple[float, float, float], x: int, y: int, radius: int
+    ctx: Context,
+    color: tuple[float, float, float],
+    x: int,
+    y: int,
+    radius: int,
+    progress: float = 0.0,
 ) -> None:
     ctx.move_to(x, y)
     ctx.rgb(*color)
-    ctx.arc(x, y, radius, -pi, pi, True)
+    ctx.arc(x, y, radius, tau * progress, tau, 0)
     ctx.fill()
+
+
+def is_dir(path: str) -> bool:
+    st_mode = uos.stat(path)[0]  # Can fail with OSError
+    return stat.S_ISDIR(st_mode)
+
+
+def rmdirs(base_path):
+    for entry in uos.listdir(base_path):
+        path = f"{base_path}/{entry}"
+        if is_dir(path):
+            rmdirs(path)
+        else:
+            uos.remove(path)
+            print(f"deleted file: {path}")
+    uos.rmdir(base_path)
+    print(f"deleted folder: {base_path}")