commit a0ec3e1236bf15f75b39831c8792d0952b25be92
parent 7ed22017370127aec8a92413ddd880eec7d24724
Author: Willem Van Zwol <willem@vanzwol.com>
Date: Mon, 1 Jun 2026 16:26:13 -0700
Updated some styling and added blog post
Diffstat:
| M | blog.html | | | 53 | ++++++++++++++++++++++++++++++++++++++++------------- |
| A | blogs/jxl_and_compression_on_nix.html | | | 427 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | blogs/librenet.html | | | 119 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | index.html | | | 25 | ------------------------- |
| M | rss.xml | | | 13 | ++++++++++++- |
| M | styles.css | | | 91 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
6 files changed, 679 insertions(+), 49 deletions(-)
diff --git a/blog.html b/blog.html
@@ -13,9 +13,9 @@
<!-- LEFT -->
<div class="nav-left">
<a href="./index.html">index</a> ~
- <a href="./blog.html">blog</a> ~
- <a href="./projects.html">projects</a> ~
- <a href="./contact.html">contact</a>
+ <a href="">blog</a> ~
+ <a href="">projects</a> ~
+ <a href="">contact</a>
</div>
<!-- CENTER -->
<div class="nav-center">
@@ -50,22 +50,49 @@
</div>
</nav>
<h1>Blog posts</h1>
+ <a href="./rss.xml">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 -1 32 32"
+ fill="currentColor"
+ width="0.8em"
+ height="0.8em"
+ aria-hidden="true"
+ >
+ <path d="M20.885 30.996h-5.83c-0.037-7.745-6.306-14.014-14.047-14.052h-0.004v-5.83c10.958 0.055 19.826 8.924 19.881 19.877l0 0.005zM5.117 22.767c0.001 0 0.001 0 0.002 0 2.272 0 4.114 1.842 4.114 4.114s-1.842 4.114-4.114 4.114c-2.271 0-4.113-1.84-4.114-4.111v-0c0.003-2.271 1.842-4.112 4.112-4.118h0.001zM24.996 30.996c-0.064-13.225-10.767-23.928-23.986-23.993l-0.006-0v-5.998c16.531 0.082 29.91 13.461 29.991 29.984l0 0.008z" />
+ </svg> RSS Feed</a>
+ <button style="margin-left: 1ch" onclick="navigator.clipboard.writeText('willem.vanzwol.com/rss.xml')">
+ Copy
+ </button>
+ <br>
+ Filtering is not yet supported, files are listed in order of date.<br><br>
+
+ <a class="tile" href="./blogs/jxl_and_compression_on_nix.html">
+ <fieldset>
+ <span>scripting • compression • nix</span>
+ <h2>JXL and Compression on nix</h2>
+ <div>2026.00.01</div>
+ Showcase of a script and flake I made for compressing JPG/JEPG to JXL and compressing into a tarball or .7z file<br>
+ </fieldset>
+ </a>
<a class="tile" href="./blogs/on_the_blog.html">
<fieldset>
- <span>website ~ blog ~ complexity</span>
- <h2>On the blog</h2>
- <div>2026.05.21</div>
- Post about my thoughts on how the blog should work.<br>
- </fieldset></a>
+ <span>website • blog • complexity</span>
+ <h2>On the Blog</h2>
+ <div>2026.05.21</div>
+ Post about my thoughts on how the blog should work.<br>
+ </fieldset>
+ </a>
<a class="tile" href="./blogs/website.html">
<fieldset>
- <span>website</span>
- <h2>Website</h2>
- <div>2026.05.19</div>
- Overview of the point of this website.<br>
- </fieldset></a>
+ <span>website</span>
+ <h2>Website</h2>
+ <div>2026.05.21</div>
+ Overview of the point of this website.<br>
+ </fieldset>
+ </a>
</body>
</html>
diff --git a/blogs/jxl_and_compression_on_nix.html b/blogs/jxl_and_compression_on_nix.html
@@ -0,0 +1,427 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Portfolio</title>
+ <link rel="icon" type="image/x-icon" href="../favicon.ico">
+ <link rel="stylesheet" type="text/css" href="../styles.css">
+ <script src="../script.js"></script>
+</head>
+<body>
+<nav>
+ <!-- LEFT -->
+ <div class="nav-left">
+ <a href="../index.html">index</a> ~
+ <a href="../blog.html">blog</a> ~
+ <a href="../projects.html">projects</a> ~
+ <a href="../contact.html">contact</a>
+ </div>
+ <!-- CENTER -->
+ <div class="nav-center">
+ </div>
+ <!-- RIGHT -->
+ <div class="nav-right">
+ <a href="../git/website/log.html"
+ title="Self hosted repo of this website">
+
+ <!-- giticon -->
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"
+ style="width: 0.85em; position: relative; top: 0.06em;
+ fill:var(--text)">
+ <path d="M15,5.6,10.4,1A3.4,3.4,0,0,0,5.78.86L7.66,2.74a1.25,
+ 1.25,0,0,1,1.67,1.2V4a1.23,1.23,0,0,1-.08.38l2.45,
+ 2.4a1.17,1.17,0,0,1,.37-.08A1.3,1.3,0,1,1,10.77,
+ 8h0a1.17,1.17,0,0,1,.08-.37L8.6,5.38v5.23a1.28,1.28,0,
+ 0,1,.73,1.15,1.3,1.3,0,0,1-2.6,0,1.27,1.27,0,0,1,
+ .67-1.11V5.07A1.27,1.27,0,0,1,6.73,4a1.17,1.17,0,0,1,
+ .08-.37l-1.9-1.9L1,5.6a3.38,3.38,0,0,0,0,4.79H1L5.6,
+ 15a3.38,3.38,0,0,0,4.79,0h0L15,10.4a3.38,3.38,0,0,0,
+ 0-4.79Z"/>
+ </svg>
+ </a>
+ <input type="checkbox" id="theme-toggle" class="toggle">
+ <label for="theme-toggle" class="btn"
+ title="Light/Dark mode (JS required)">
+
+ <!-- sun -->
+ <svg class="sun" viewBox="0 0 24 24" fill=none
+ stroke=currentColor stroke-linecap=round
+ stroke-width="1.8">
+ <circle cx=12 cy=12 r=4.4 />
+ <path d="M12 2v3m0 14v3M2 12h3m14 0h3M5 5l2 2m10 10l2
+ 2M5 19l2-2m10-10l2-2"/>
+ </svg>
+
+ <!-- moon -->
+ <svg class="moon" viewBox="0 0 24 24">
+ <path d="M21 12.8A9 9 0 0 1 9 3c0-.3 0-.7.1-1A10 10 0 1
+ 0 23 12.4c-.3 0-.7.1-1 .1z"/>
+
+ </svg>
+ </label>
+ </div>
+</nav>
+
+<h1>JXL and Compression on Nix</h1>
+2026.05.29<br><br>
+
+I recently made a backup of photos
+I've been taking over the last few years on a Fujifilm x100 (2010).
+And I wanted to find the best format and way to compress it.<br><br>
+
+This can act as both a guide and a blog if I've written it well enough,
+so you can compress images on nixos (or linux) yourself.
+
+<h2>Why JXL?</h2>
+For this process,
+I will not just be making a <code>.zip</code> or <code>.tar.gz</code>
+I also will be converting the JPG/JPEG files,
+and will do so with <code>.jxl</code>.<br><br>
+
+I believe that <abbr
+title="JPG/JPEG XL Image Coding System, supporting both lossy and lossless">
+ <code>.jxl</code></abbr> is an incredible format,
+and it's really too bad <a
+href="https://groups.google.com/a/chromium.org/g/blink-dev/c/WjCKcBw219k/m/Ocsmn8NHAwAJ"
+ >Google is yet to support it in chrome</a>
+despite <details><summary>high demand</summary>
+
+ <aside class="left">
+<blockquote
+ cite="https://en.wikipedia.org/wiki/JPEG_XL#Industry_support_and_adoption">
+various representatives of well-known industry brand names
+have publicly voiced support for JPEG XL
+as their preferred choice,
+including Facebook, Adobe, Intel, ...
+
+<a class="quote-source"
+ href="https://en.wikipedia.org/wiki/JPEG_XL#Industry_support_and_adoption"
+ aria-label="Source"
+ ></a>
+</blockquote>
+According to Wikipedia<br><br>
+
+The list contines on a lot longer.
+For this reason Google's abilivalent stance on supporting it in Chrome
+resulted in a lot of pushback from the community.
+ </aside>
+</details>
+
+.
+Google had instead pushed for <a
+ href="https://lobste.rs/s/27hc2m/google_explains_why_it_s_removing_jpeg_xl">
+ AVIF a competitor to JXL</a>
+which in my opinion is inferior.
+<abbr title="The video codec that AVIF is derived from">AV1</abbr> however,
+is incredible!
+and I am patiently awaitng the release of AV2
+and it's adoption.<br><br>
+
+JXL in particular is built around JPEG interoperability,
+so converting files from JPEG to JXL can preserve visual quality
+while significantly reducing file size.
+But most importantly It also supports <abbr title=
+"decodes in multiple passes, a low quality image that refines displays faster">
+ Progressive loading</abbr>.<br><br>
+
+Years later,
+and google <a href="https://www.phoronix.com/news/Chrome-145-Released">
+ may support it</a> very soon!
+Firefox is likely to follow suit,
+and safari already does, along with a bunch of software like
+<a href="https://www.gimp.org/">Gimp</a>,
+<a href="https://krita.org/">Krita</a> and Photoshop do.
+Hopefully I am not just being overly optimistic,
+because I do believe that JXL is the future.
+
+<h2>JXL compression</h2>
+Firstly, you need <code>libjxl</code> installed.
+On nixos this is easy to do in a shell:
+<pre data-lang="bash">
+nix-shell -p libjxl
+</pre>
+<aside class="left">
+ You can click code blocks to copy there contents to clipboard!
+</aside>
+<pre data-lang="bash">
+cjxl input.jpg output.jxl
+</pre>
+<div class="callout note">
+ Installing packages like <code>libjxl</code> depends on package manager,
+ nearly all unix systems can <a href="https://nixos.org/download/">
+ install nix though</a>
+ (and i'd recommend it).
+</div>
+
+The command above may be all you need depending on your use case.
+For me I want a short script to automate this.
+
+<h2>Automate it!</h2>
+
+I've been
+<details>
+ <summary>wanting to learn</summary>
+ <aside class="left">
+Because I am using nix,
+it enables is for the reliablity and quality of your code to matter more
+than accessability of language.
+Normally I would just use bash so it's easy to run,
+but as I mention nushell catches more errors before runtime.
+ </aside>
+</details>
+<a href="https://www.nushell.sh/">
+ nushell</a> for scripting,
+and I don't regret it one bit.
+It has structured data,
+<abbr title="Nu speaks JSON, YAML, SQLite, Excel, and more out of the box">
+ batteries included</abbr>
+and <em>rust like errors</em>.
+In my experience languages with great error messages
+(and a cult following)
+are
+<details>
+ <summary>all amazing.</summary>
+<aside class="right">
+ Afterall, if a language doesn't have good compiler output
+ it's <abbr title=
+"Protocol that per langage gives inline errors and autocomplete in your editor"
+ >LSP</abbr> is going to be terrible too.
+
+ It's a good reason to avoid interpreted languages and C
+ unless it's very well suited for your use case
+ and you are willing to learn it well to do it well.
+</aside>
+</details>
+<br><br>
+
+In my case the DCIM folder has multiple folders inside that has the JPEGS,
+so I want to recursively go through every file
+and recreate the same file tree in a new directory
+for the compressed files.<br><br>
+
+Here is the script (<code>./compress.nu</code>)
+ I ended up making and how to use it.
+
+<!-- HTML generated using hilite.me --><pre id="compress.nu" data-lang="nu"><span></span><span style="color: #56B6C2">#</span><span style="color: #E06C75">!/usr/bin/env</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">nu</span>
+<span style="color: #56B6C2">#</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">compress.nu</span>
+
+<span style="color: #C678DD">def </span><span style="color: #E06C75">main</span><span style="color: #ABB2BF"> [</span><span style="color: #E06C75">out_dir</span><span style="color: #ABB2BF">: </span><span style="color: #E06C75">path</span><span style="color: #ABB2BF">] {</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">mkdir</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">$out_dir</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #C678DD">let </span><span style="color: #E06C75">files</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">= </span><span style="color: #ABB2BF">(</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">ls</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">**/*</span><span style="color: #ABB2BF"> </span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">where</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">type</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">== </span><span style="color: #E06C75">file</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">where</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">name </span><span style="color: #E06C75">=</span><span style="color: #56B6C2">~</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">'</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">?i</span><span style="color: #ABB2BF">)</span><span style="color: #98C379">\.</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">jpg|jpeg</span><span style="color: #ABB2BF">)</span><span style="color: #E06C75">$</span><span style="color: #56B6C2">'</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">get </span><span style="color: #E06C75">name</span>
+<span style="color: #ABB2BF"> )</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #C678DD">if </span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">$files</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">is-empty</span><span style="color: #ABB2BF">) {</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E5C07B">print </span><span style="color: #98C379">"No jpg/jpeg files found"</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">exit</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">1</span>
+<span style="color: #ABB2BF"> }</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #E5C07B">for </span><span style="color: #E06C75">f</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">in</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">$files</span><span style="color: #ABB2BF"> {</span>
+<span style="color: #ABB2BF"> </span><span style="color: #C678DD">let </span><span style="color: #E06C75">base</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">= </span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">$f</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">path parse </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">reject</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">path </span><span style="color: #E06C75">join</span><span style="color: #ABB2BF">)</span>
+<span style="color: #ABB2BF"> </span><span style="color: #C678DD">let </span><span style="color: #E06C75">out</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">= </span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">$out_dir</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">path join </span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">$base</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">+ </span><span style="color: #98C379">".jxl"</span><span style="color: #ABB2BF">))</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">mkdir</span><span style="color: #ABB2BF"> (</span><span style="color: #61AFEF; font-weight: bold">$out</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">path </span><span style="color: #E06C75">dirname</span><span style="color: #ABB2BF">)</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #C678DD">if </span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-weight: bold">$out</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">|</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">path </span><span style="color: #E06C75">exists</span><span style="color: #ABB2BF">) {</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">continue</span>
+<span style="color: #ABB2BF"> }</span>
+
+<span style="color: #ABB2BF"> </span><span style="color: #E5C07B">print </span><span style="color: #E06C75">$</span><span style="color: #98C379">"Converting: ($f) -> ($out)"</span>
+<span style="color: #ABB2BF"> </span><span style="color: #E06C75">cjxl</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">$f</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">$out</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">-d</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">-e</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">9</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">--lossless_jpeg=0</span>
+<span style="color: #ABB2BF"> }</span>
+<span style="color: #ABB2BF">}</span>
+</pre>
+<div class="callout note">
+ That last line does a lot of the heavy lifting.
+ Right now it’s set to maximum effort (9) and lossy output (0).
+ Lower the 9 to reduce effort (e.g. 3).
+ Flip the last 0 to 1 to enable lossless,
+ also then remove <code>-d 2 </code> segment.
+</div>
+
+<pre data-lang="bash">
+nix-shell -p nushell libjxl
+</pre>
+<pre data-lang="bash">
+nu compress.nu out_dir/
+</pre>
+
+This will work recursively into all sub directories of where the script is
+executed.
+It will also ignore all non JPG/JPEG files.
+So if you have other file formats as well mixed in (like I do <code>RAF</code>)
+then you need to convert that too.
+But you need not do it first,
+because this script will detect what files have already been done and not redo.
+
+<h2>Tarball compression</h2>
+Lastly compressing everything with a tarball.
+Zip works fine,
+
+<pre data-lang="bash">
+nix-shell -p zip
+zip -r archive.zip myfolder
+</pre>
+
+and in practice you should proabbly do both,
+but tar balls work better for lots of files.
+Zip losslessly compresses on a file-per-file basis
+and wouldn't actually help that much here
+given each file is already so efficiently compressed.
+Earlier I mentioned <code>.tar.gz</code>
+which is generated via <code>gzip</code> on a <code>.tar</code>,
+but there are more efficient compression algorithms but run for longer
+and use more power.
+<br><br>
+
+<details>
+ <summary>There are lots of options</summary>
+These are in increasing order of least to most compute time for smaller results.
+<pre data-lang="bash">
+tar -cf - dir/ | gzip -9 -n > archive.tar.gz</pre>
+<pre data-lang="bash">
+tar -cf - dir/ | bzip2 -9 > archive.tar.bz2</pre>
+<pre data-lang="bash">
+tar -cf - dir/ | xz -9 -e -T0 > archive.tar.xz</pre>
+<pre data-lang="bash">
+tar -cf - dir/ | zstd -22 -T0 --ultra -o archive.tar.zst</pre>
+<pre data-lang="bash">
+7z a -t7z -mx=9 -m0=lzma2 -mmt=on archive.7z dir/</pre>
+</details>.<br>
+
+I was planning on trying each and have it run overnight,
+but this part of the process ended up being a so much quicker than I expected.
+<br><br><hr><br>
+Using <code>p7zip</code> here were the final results.<br>
+I compressed 14.1 GB of photos down to 1.8 GB (lossy), and 9.2 GB (lossless).
+ <br>
+Only using p7zip without JXL then only compresses to 13.3 GB,
+so JXL ened up being well worth it.
+
+<h2>Nix flake stuff</h2>
+Especially if you don't use nix,
+then you can stop reading here.
+The script most likely worked fine.
+Eventually I will make another blog post about why I bother with nix.<br><br>
+
+Either way, here are two methods for running nushell via nix.
+One solution is you run it inline like this:
+<!-- HTML generated using hilite.me --><pre data-lang="nix" ><span></span><span style="color: #E6EDF3">{</span>
+ inputs<span style="color: #FF7B72; font-weight: bold">.</span>nixpkgs<span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">url</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"github:NixOS/nixpkgs/nixos-unstable"</span><span style="color: #E6EDF3">;</span>
+
+ <span style="color: #A5D6FF">outputs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">{</span> self<span style="color: #E6EDF3">,</span> nixpkgs <span style="color: #E6EDF3">}:</span>
+ <span style="color: #FF7B72">let</span>
+ <span style="color: #A5D6FF">system</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"x86_64-linux"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #A5D6FF">pkgs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">import</span> nixpkgs <span style="color: #E6EDF3">{</span> <span style="color: #FF7B72">inherit</span> system<span style="color: #E6EDF3">;</span> <span style="color: #E6EDF3">};</span>
+
+ <span style="color: #A5D6FF">app</span> <span style="color: #FF7B72; font-weight: bold">=</span> pkgs<span style="color: #FF7B72; font-weight: bold">.</span>writeShellScriptBin <span style="color: #A5D6FF">"run-nu"</span> <span style="color: #A5D6FF">''</span>
+<span style="color: #A5D6FF"> exec ${</span>pkgs<span style="color: #FF7B72; font-weight: bold">.</span>nushell<span style="color: #A5D6FF">}/bin/nu ${</span>pkgs<span style="color: #FF7B72; font-weight: bold">.</span>writeText <span style="color: #A5D6FF">"hello.nu"</span> <span style="color: #A5D6FF">''</span>
+<span style="color: #A5D6FF">def main [out_dir: path] {</span>
+
+<span style="color: #A5D6FF"> mkdir $out_dir</span>
+
+<span style="color: #A5D6FF"> let files = (</span>
+<span style="color: #A5D6FF"> ls **/* </span>
+<span style="color: #A5D6FF"> | where type == file</span>
+<span style="color: #A5D6FF"> | where name =~ '(?i)\.(jpg|jpeg)$'</span>
+<span style="color: #A5D6FF"> | get name</span>
+<span style="color: #A5D6FF"> )</span>
+
+<span style="color: #A5D6FF"> if ($files | is-empty) {</span>
+<span style="color: #A5D6FF"> print "No jpg/jpeg files found"</span>
+<span style="color: #A5D6FF"> exit 1</span>
+<span style="color: #A5D6FF"> }</span>
+
+<span style="color: #A5D6FF"> for f in $files {</span>
+<span style="color: #A5D6FF"> let base = ($f | path parse | reject extension | path join)</span>
+<span style="color: #A5D6FF"> let out = ($out_dir | path join ($base + ".jxl"))</span>
+
+<span style="color: #A5D6FF"> mkdir ($out | path dirname)</span>
+
+<span style="color: #A5D6FF"> if ($out | path exists) {</span>
+<span style="color: #A5D6FF"> continue</span>
+<span style="color: #A5D6FF"> }</span>
+
+<span style="color: #A5D6FF"> print $"Converting: ($f) -> ($out)"</span>
+<span style="color: #A5D6FF"> ${pkgs.cjxl} $f $out -d 2 -e 9 --lossless_jpeg=0</span>
+<span style="color: #A5D6FF"> }</span>
+<span style="color: #A5D6FF">}</span>
+
+<span style="color: #A5D6FF"> ''}</span>
+<span style="color: #A5D6FF"> ''</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #FF7B72">in</span> <span style="color: #E6EDF3">{</span>
+ apps<span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">${</span>system<span style="color: #A5D6FF">}</span><span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">default</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">{</span>
+ <span style="color: #A5D6FF">type</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"app"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #A5D6FF">program</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"${</span>app<span style="color: #A5D6FF">}/bin/run-nu"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #E6EDF3">};</span>
+ <span style="color: #E6EDF3">};</span>
+<span style="color: #E6EDF3">}</span>
+</pre>
+<pre data-lang="bash">
+nix runs -- ./path/to/dir
+</pre>
+
+The other solution is to put the nushell script in another file and link to it
+<code>(./scipt.nu)</code>.
+This is best if you want to modify and debug things,
+as you get actual syntax highlighting, <abbr title=
+"Protocol that per langage gives inline errors and autocomplete in your editor"
+ >LSP</abbr>
+and better erros running it from the dev shell.<br><br>
+The script is the <a href="#compress.nu">same one above</a>.
+
+<!-- HTML generated using hilite.me --><pre data-lang="nix" ><span></span><span style="color: #E6EDF3">{</span>
+ inputs<span style="color: #FF7B72; font-weight: bold">.</span>nixpkgs<span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">url</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"github:NixOS/nixpkgs/nixos-unstable"</span><span style="color: #E6EDF3">;</span>
+
+ <span style="color: #A5D6FF">outputs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">{</span> self<span style="color: #E6EDF3">,</span> nixpkgs <span style="color: #E6EDF3">}:</span> <span style="color: #FF7B72">let</span>
+ <span style="color: #A5D6FF">system</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"x86_64-linux"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #A5D6FF">pkgs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">import</span> nixpkgs <span style="color: #E6EDF3">{</span> <span style="color: #FF7B72">inherit</span> system<span style="color: #E6EDF3">;</span> <span style="color: #E6EDF3">};</span>
+
+ <span style="color: #A5D6FF">app</span> <span style="color: #FF7B72; font-weight: bold">=</span> pkgs<span style="color: #FF7B72; font-weight: bold">.</span>writeShellApplication <span style="color: #E6EDF3">{</span>
+ <span style="color: #A5D6FF">name</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"run-nu"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #A5D6FF">runtimeInputs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #FF7B72">with</span> pkgs<span style="color: #E6EDF3">;</span> <span style="color: #E6EDF3">[</span> nushell libjxl <span style="color: #E6EDF3">];</span>
+ <span style="color: #A5D6FF">text</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">''</span>
+<span style="color: #A5D6FF"> exec nu ${./script.nu} "$@"</span>
+<span style="color: #A5D6FF"> ''</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #E6EDF3">};</span>
+ <span style="color: #FF7B72">in</span> <span style="color: #E6EDF3">{</span>
+ apps<span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">${</span>system<span style="color: #A5D6FF">}</span><span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">default</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #E6EDF3">{</span>
+ <span style="color: #A5D6FF">type</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"app"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #A5D6FF">program</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #A5D6FF">"${</span>app<span style="color: #A5D6FF">}/bin/run-nu"</span><span style="color: #E6EDF3">;</span>
+ <span style="color: #E6EDF3">};</span>
+
+ devShells<span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">${</span>system<span style="color: #A5D6FF">}</span><span style="color: #FF7B72; font-weight: bold">.</span><span style="color: #A5D6FF">default</span> <span style="color: #FF7B72; font-weight: bold">=</span> pkgs<span style="color: #FF7B72; font-weight: bold">.</span>mkShell <span style="color: #E6EDF3">{</span>
+ <span style="color: #A5D6FF">buildInputs</span> <span style="color: #FF7B72; font-weight: bold">=</span> <span style="color: #FF7B72">with</span> pkgs<span style="color: #E6EDF3">;</span> <span style="color: #E6EDF3">[</span> nushell libjxl <span style="color: #E6EDF3">];</span>
+ <span style="color: #E6EDF3">};</span>
+ <span style="color: #E6EDF3">}</span>
+<span style="color: #E6EDF3">}</span>
+</pre>
+
+
+<div class="callout note">
+There is actually a simpler way to do this,
+but this is better setup in case you want to use it in other places
+or modify it.<br><br>
+Essentially <code>libjxl</code> is included here in the runtime inputs,
+because we aren't referring to packages as <code>pkgs.libjxl</code> for example,
+we instead need to declare them before trying to run the script.
+</div>
+<pre data-lang="bash">
+nix develop
+</pre>
+<pre data-lang="bash">
+nu ./script.nu ./path/to/dir
+</pre>
+Or simply:
+<pre data-lang="bash">
+nix runs -- ./path/to/dir
+</pre>
+
+</body>
+</html>
diff --git a/blogs/librenet.html b/blogs/librenet.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Portfolio</title>
+ <link rel="icon" type="image/x-icon" href="../favicon.ico">
+ <link rel="stylesheet" type="text/css" href="../styles.css">
+ <script src="../script.js"></script>
+</head>
+<body>
+<nav>
+ <!-- LEFT -->
+ <div class="nav-left">
+ <a href="../index.html">index</a> ~
+ <a href="../blog.html">blog</a> ~
+ <a href="../projects.html">projects</a> ~
+ <a href="../contact.html">contact</a>
+ </div>
+ <!-- CENTER -->
+ <div class="nav-center">
+ </div>
+ <!-- RIGHT -->
+ <div class="nav-right">
+ <a href="../git/website/log.html"
+ title="Self hosted repo of this website">
+
+ <!-- giticon -->
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"
+ style="width: 0.85em; position: relative; top: 0.06em;
+ fill:var(--text)">
+ <path d="M15,5.6,10.4,1A3.4,3.4,0,0,0,5.78.86L7.66,2.74a1.25,
+ 1.25,0,0,1,1.67,1.2V4a1.23,1.23,0,0,1-.08.38l2.45,
+ 2.4a1.17,1.17,0,0,1,.37-.08A1.3,1.3,0,1,1,10.77,
+ 8h0a1.17,1.17,0,0,1,.08-.37L8.6,5.38v5.23a1.28,1.28,0,
+ 0,1,.73,1.15,1.3,1.3,0,0,1-2.6,0,1.27,1.27,0,0,1,
+ .67-1.11V5.07A1.27,1.27,0,0,1,6.73,4a1.17,1.17,0,0,1,
+ .08-.37l-1.9-1.9L1,5.6a3.38,3.38,0,0,0,0,4.79H1L5.6,
+ 15a3.38,3.38,0,0,0,4.79,0h0L15,10.4a3.38,3.38,0,0,0,
+ 0-4.79Z"/>
+ </svg>
+ </a>
+ <input type="checkbox" id="theme-toggle" class="toggle">
+ <label for="theme-toggle" class="btn"
+ title="Light/Dark mode (JS required)">
+
+ <!-- sun -->
+ <svg class="sun" viewBox="0 0 24 24" fill=none
+ stroke=currentColor stroke-linecap=round
+ stroke-width="1.8">
+ <circle cx=12 cy=12 r=4.4 />
+ <path d="M12 2v3m0 14v3M2 12h3m14 0h3M5 5l2 2m10 10l2
+ 2M5 19l2-2m10-10l2-2"/>
+ </svg>
+
+ <!-- moon -->
+ <svg class="moon" viewBox="0 0 24 24">
+ <path d="M21 12.8A9 9 0 0 1 9 3c0-.3 0-.7.1-1A10 10 0 1
+ 0 23 12.4c-.3 0-.7.1-1 .1z"/>
+
+ </svg>
+ </label>
+ </div>
+</nav>
+
+<h1>Librenet</h1>
+2026.04.26
+
+<p>
+This concept is Massive.
+I will most likely neeed to make multiple blog posts
+to really cover the entirety of what Librenet would be.
+That said, I do think I've found a consice enough way to talk about a
+part of it that gets you to infer the other features I may want.
+</p>
+
+<h2>A Network of Relays</h2>
+<p>
+
+You have a collection of devices that all in a network
+call them <em>nodes</em>.
+
+They all have IP addresses,
+are listening on ports for TCP/UDP
+and know the <code>IP:port</code> touples of every other node.
+
+The goal is for this network to act as a cloud for storage.
+Each node also can represent a user
+that wants files stored on this cloud.<br><br>
+
+<details>
+ <summary>erasure codes</summary>
+ <aside>
+
+ A naive approach is a node selects a device at random to store a file for them
+ the node in question gets to just store a pointer to the device that has it.
+
+ <br><br>
+
+ Under imperfect uptime and trust that's fine.
+ But in reality you want some way to break up the file into pieces
+ such that you only need a fraction of those pieces to reconstruct the file.
+
+ <br><br>
+
+ This is called erasure codes
+ </aside>
+</details>
+
+would be used eg.
+<a href=
+ "https://www.cs.cmu.edu/~guyb/realworld/reedsolomon/reed_solomon_codes.html"
+ title="Block-based error correcting codes with a wide range of applications"
+ >Reed-Solomon
+</a>
+
+</p>
+</body>
+</html>
diff --git a/index.html b/index.html
@@ -106,30 +106,5 @@
<a href="./git/website/log.html">
self hosted git</a>.
</p>
- <hr>
- <p>
- I have created the <code>styles.css</code> myself,
- checkout the markdown emulated syntax!<br>
- <em>italics</em>, <b>bold</b>, <del>strickthrough</del>
- <blockquote cite="https://example.com">
- Software is like sex, it's better when it's free<br>
- - Linus Torvalds
- <a class="quote-source"
- href="https://example.com"
- aria-label="Source"
- ></a>
- </blockquote>
- <ul><li>item</li>
- <li>item</li></ul>
- <ol><li>item</li>
- <li>item</li></ol>
-
- <div class="callout note">
- This is a note.
- </div>
-
- <div class="callout important">
- This is important.
- </div>
</body>
</html>
diff --git a/rss.xml b/rss.xml
@@ -7,6 +7,17 @@
<language>en-us</language>
<item>
+ <title>JXL and Compression on nix</title>
+ <description>Showcase of a script and flake I made for compressing JPG/JEPG to JXL and compressing into a tarball or .7z file</description>
+ <category>scripting</category>
+ <category>compression</category>
+ <category>nix</category>
+ <pubDate>Tue, 01 June 2026 23:23:00 +0000</pubDate>
+ <link>./blogs/jxl_and_compression_on_nix.html</link>
+ <guid>./blogs/jxl_and_compression_on_nix</guid>
+ </item>
+
+ <item>
<title>On the Blog</title>
<description>Post about my thoughts on how the blog should work.</description>
<category>website</category>
@@ -21,7 +32,7 @@
<title>Website</title>
<description>Overview of the point of this website.</description>
<category>website</category>
- <pubDate>Tue, 19 May 2026 23:23:00 +0000</pubDate>
+ <pubDate>Tue, 21 May 2026 23:23:00 +0000</pubDate>
<link>./blogs/website.html</link>
<guid>./blogs/website.html</guid>
</item>
diff --git a/styles.css b/styles.css
@@ -14,7 +14,6 @@
--text: #eaeaea;
--border: #262626;
--link: #6aa7ff;
- --accent: #6aa7ff;
a[href^="http://"]::after,
a[href^="https://"]::after {
@@ -28,7 +27,6 @@ html.dark {
--text: #eaeaea;
--border: #262626;
--link: #6aa7ff;
- --accent: #6aa7ff;
}
html.light {
@@ -38,6 +36,10 @@ html.light {
--link: #0645ad;
}
+html {
+ scroll-behavior: smooth;
+}
+
a[href^="http"]::after {
content: "";
display: inline-block;
@@ -129,12 +131,23 @@ blockquote {
ul,
ol {
- padding-left: 1.5em;
+ padding-left: 2ch;
margin: 0;
}
+ul {
+ padding-left: 0ch;
+ list-style: none;
+}
-li::marker {
- color: color-mix(in srgb, var(--text) 50%, transparent);
+ul li::before {
+ content: "* ";
+ font-family: "Liberation Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+ color: var(--border);
+ font-weight: 600;
+}
+
+ol li::marker {
+ color: var(--border);
}
legend {
@@ -219,7 +232,7 @@ button:active {
}
button:focus-visible {
- outline: 2px solid var(--accent);
+ outline: 2px solid var(--link);
outline-offset: 2px;
}
@@ -232,6 +245,7 @@ code {
}
pre {
+ background: var(--bg);
position: relative;
margin: 1em 0em;
@@ -240,12 +254,13 @@ pre {
padding: 1rem;
overflow-x: auto;
+ resize: vertical;
white-space: pre;
word-break: break-word;
font-size: 0.9em;
+ max-height: 32em;
- background: var(--bg);
border: 0.0625rem solid var(--border);
cursor: pointer;
@@ -268,10 +283,26 @@ pre::before {
pre:hover::before {
content: "COPY";
+ border-bottom: 0.0625rem solid var(--text);
}
pre[data-copied="true"]::before {
content: "COPIED";
+ border: 0.125rem solid var(--link);
+}
+
+details {
+ display: inline-block;
+}
+
+details summary {
+ color: var(--link);
+ text-decoration: underline dotted;
+ list-style: none;
+
+ text-decoration-thickness: 2px;
+ text-decoration-color: color-mix(in srgb, currentcolor 50%, transparent);
+ cursor: help;
}
abbr[title] {
@@ -281,17 +312,37 @@ abbr[title] {
cursor: help;
}
-aside {
+aside.left {
position: absolute;
- left: calc(50% - 60ch - 2.5em - 2ch);
+ box-sizing: border-box;
+ left: calc(50% - 60ch - 2.5em);
padding-right: 2ch;
transform: translateY(calc(-50% - 0.9em));
width: 20ch;
border-right: 0.0625rem solid var(--border);
}
+aside.right {
+ position: absolute;
+ box-sizing: border-box;
+ right: calc(50% - 60ch - 2.5em);
+ padding-left: 2ch;
+ transform: translateY(calc(-50% - 0.9em));
+ width: 20ch;
+ border-left: 0.0625rem solid var(--border);
+}
+
+aside.right::before {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: -4.5ch;
+ width: 4.5ch;
+ height: 0.0625rem;
+ background: var(--border);
+}
-aside::after {
+aside.left::after {
content: "";
position: absolute;
top: 50%;
@@ -301,6 +352,26 @@ aside::after {
background: var(--border);
}
+@media (max-width: 134ch) {
+ aside {
+ position: static;
+ left: auto;
+ transform: none;
+
+ width: auto;
+
+ margin: 1em 0.4em;
+ padding: 0.15em 0.8em;
+
+ border: none;
+ border-left: 0.3em solid var(--border);
+ }
+
+ aside::after {
+ display: none;
+ }
+}
+
.callout {
position: relative;