diff --git a/blinkenlicht/README.md b/blinkenlicht/README.md
new file mode 100644
index 0000000..f1992b8
--- /dev/null
+++ b/blinkenlicht/README.md
@@ -0,0 +1,37 @@
+# Blinkenlicht
+
+Yet another "bar" program for wlroots-basd Wayland compositors, but
+this one is written in [Fennel](https://fennel-lang.org/) and
+therefore better than all the others because it is more niche.
+
+More seriously: you might prefer this over another status bar program
+if you want fine-grained control over what is shown in your bar and
+you are happy to exert that control in a Lua-based Lisp language.
+
+```fennel
+(bar
+ {
+  :anchor [:top :right]
+  :orientation :horizontal
+  :indicators
+  [
+   (indicator {
+               :interval 200
+               :icon #(if (> loadavg 2) "sad-face" "happy-face")
+               })
+   (let [f (io.open "/tmp/statuspipe" "r")]
+     (indicator {
+                 :poll [f]
+                 :text #(f:read:sub 1 10)
+                 }))
+   (indicator {
+               :interval 5000
+               :text #(.. (disk-free-percent "/") "%")
+               :on-click #(spawn "baobab")
+               })
+   (indicator {
+               :interval 1000
+               :text #(os.date "%X")
+               })
+   ]})
+```
diff --git a/blinkenlicht/bl.fnl b/blinkenlicht/bl.fnl
new file mode 100644
index 0000000..1e11817
--- /dev/null
+++ b/blinkenlicht/bl.fnl
@@ -0,0 +1,42 @@
+(local {: bar : indicator : run} (require :blinkenlicht))
+
+(fn loadavg []
+  (with-open [f (io.open "/proc/loadavg" :r)]
+    (tonumber (: (f:read "*a") :match "[0-9.]+" ))))
+
+(fn disk-free-percent []
+  83)
+
+(fn spawn []
+  true)
+
+(bar
+ {
+  :anchor [:top :right]
+  :orientation :horizontal
+  :indicators
+  [
+   (indicator {
+               :interval 200
+               :icon #(if (> (loadavg) 2) "face-sad" "face-smile")
+               })
+   ;; (let [f (io.open "/tmp/statuspipe" "r")]
+   ;;   (indicator {
+   ;;               :poll [f]
+   ;;               :text #((f:read):sub 1 10)
+   ;;               }))
+   (indicator {
+               :text "HI!"
+               })
+   (indicator {
+               :interval 5000
+               :text #(.. (disk-free-percent "/") "%")
+               :on-click #(spawn "baobab")
+               })
+   (indicator {
+               :interval 1000
+               :text #(os.date "%X")
+               })
+   ]})
+
+(run)
diff --git a/blinkenlicht/blinkenlicht.fnl b/blinkenlicht/blinkenlicht.fnl
new file mode 100644
index 0000000..1d95f14
--- /dev/null
+++ b/blinkenlicht/blinkenlicht.fnl
@@ -0,0 +1,95 @@
+(local {: Gtk
+        : Gdk
+        : GdkPixbuf
+        : GLib
+        : cairo } (require :lgi))
+
+(local {: view} (require :fennel))
+
+(local icon-theme (Gtk.IconTheme.get_default))
+
+(local HEIGHT 48)
+
+(fn resolve [f]
+  (match (type f)
+    "string" f
+    "function" (f)))
+
+(fn find-icon-pixbuf [name]
+  (var found nil)
+  (each [_ res (pairs [HEIGHT 128 64 48]) :until found]
+    (let [pixbuf (icon-theme:load_icon
+                  name res
+                  (+ Gtk.IconLookupFlags.FORCE_SVG
+                     Gtk.IconLookupFlags.USE_BUILTIN))]
+      (when pixbuf
+        (set found (pixbuf:scale_simple
+                    HEIGHT (* pixbuf.width (/ HEIGHT pixbuf.height))
+                    GdkPixbuf.InterpType.BILINEAR)))))
+  found)
+
+(fn find-icon [name]
+  (if (= (name:sub 1 1) "/")
+      ;; From a direct path
+      (GdkPixbuf.Pixbuf.new_from_file_at_scale name HEIGHT -1 true)
+      ;; From icon theme
+      (Gtk.Image.new_from_pixbuf (find-icon-pixbuf name))))
+
+(fn update-button [button icon text]
+  (match (button:get_child) it (button:remove it))
+  (let [i (resolve icon)]
+    (print :update i (resolve text))
+    (if i
+        (button:add (find-icon i))
+        (button:add (Gtk.Label {:label (resolve text)})))
+    (button:show_all)
+    ))
+
+(fn indicator [{: interval
+                : icon
+                : poll
+                : text
+                : on-click}]
+  (let [button
+        (Gtk.Button { :relief  Gtk.ReliefStyle.NONE})
+        update #(update-button button icon text)]
+    (update)
+    {
+     : interval
+     : poll
+     : button
+     : update
+     }))
+
+(local bars [])
+
+(fn bar [{ : anchor : orientation : indicators }]
+  (let [window (Gtk.Window  {} )
+        orientation (match orientation
+                      :vertical Gtk.Orientation.VERTICAL
+                      :horizontal Gtk.Orientation.HORIZONTAL)
+        box (Gtk.Box { :orientation orientation})]
+    (table.insert bars { : window : anchor : indicators })
+    (each [_ i (ipairs indicators)]
+      (box:pack_start i.button false false 0))
+    (window:add box)))
+
+(fn run []
+  (GLib.timeout_add
+     0
+     1000
+     (fn []
+       (print :update)
+       (each [_ bar (ipairs bars)]
+         (each [_ indicator (ipairs bar.indicators)]
+           (indicator:update)))
+       true))
+  (each [_ b (ipairs bars)]
+    (b.window:show_all))
+  (Gtk.main))
+
+{
+ : bar
+ : indicator
+ : run
+ }
diff --git a/blinkenlicht/default.nix b/blinkenlicht/default.nix
new file mode 100644
index 0000000..395d557
--- /dev/null
+++ b/blinkenlicht/default.nix
@@ -0,0 +1,70 @@
+{ stdenv
+, callPackage
+, fetchFromGitHub
+, fetchurl
+, gobject-introspection
+, gtk3
+, lib
+, librsvg
+, lua53Packages
+#, luaDbusProxy
+, lua5_3
+, makeWrapper
+, writeText
+}:
+let
+  pname = "blinkenlicht";
+  fennel = fetchurl {
+    name = "fennel.lua";
+    url = "https://fennel-lang.org/downloads/fennel-1.0.0";
+    hash = "sha256:1nha32yilzagfwrs44hc763jgwxd700kaik1is7x7lsjjvkgapw7";
+  };
+  inifile = let lua = lua5_3; in lua53Packages.buildLuaPackage rec {
+    pname  = "inifile";
+    name = "${pname}-${version}";
+    version  = "1.0.2";
+    src = fetchFromGitHub {
+      owner = "bartbes";
+      repo = "inifile";
+      rev = "f0b41a8a927f3413310510121c5767021957a4e0";
+      sha256 = "1ry0q238vbp8wxwy4qp1aychh687lvbckcf647pmc03rwkakxm4r";
+    };
+    buildPhase = ":";
+    installPhase = ''
+        mkdir -p "$out/share/lua/${lua.luaversion}"
+        cp inifile.lua "$out/share/lua/${lua.luaversion}/"
+      '';
+  };
+
+  lua = lua5_3.withPackages (ps: with ps; [
+    #      luaDbusProxy
+    #      inifile
+    #      inspect
+    lgi
+    luafilesystem
+    luaposix
+    #      penlight
+    readline
+  ]);
+
+in stdenv.mkDerivation {
+  inherit pname;
+  version = "0.1";
+  src =./.;
+
+  inherit fennel;
+
+  buildInputs = [ lua gtk3 gobject-introspection.dev ];
+  nativeBuildInputs = [ lua makeWrapper ];
+
+  makeFlags = [ "PREFIX=${placeholder "out"}" ];
+  # GDK_PIXBUF_MODULE_FILE setting is to support SVG icons without
+  # their having been transformed to bitmaps.
+  # This makes a big difference to how many icons are displayed on
+  # my machine
+  postInstall = ''
+    mkdir -p $out/share/dbus-1/services
+
+    wrapProgram $out/bin/${pname} --set GDK_PIXBUF_MODULE_FILE ${librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache --set GI_TYPELIB_PATH "$GI_TYPELIB_PATH"
+  '';
+}
diff --git a/blinkenlicht/shell.nix b/blinkenlicht/shell.nix
new file mode 100644
index 0000000..e404c8c
--- /dev/null
+++ b/blinkenlicht/shell.nix
@@ -0,0 +1,2 @@
+with import <nixpkgs> {} ;
+callPackage ./. {}