fasterthanli.meamos likes to tinkerhttps://fasterthanli.me/2025-12-31T22:00:00ZAmos Wengerhttps://fasterthanli.mehomehttps://cdn.fasterthanli.me/content/img/logo-square-2~e095809d741c846c.webp2025 Recap: so many projectshttps://fasterthanli.me/articles/2025-recap2025-12-31T22:00:00Z<p data-bo="197">I’ve been working on so many projects in 2025, I thought it was important for me
to make a recap, if only just to clear my head.</p>
<p data-bo="328">There are many, many, many things to go through and we don’t have a sponsor
today, so I’m gonna start right away with facet!</p>
<a id="facet" href="#facet" class="anchor"><h2>facet</h2></a>
<p data-bo="464">facet is a project that I started working on in March of this year — that’s
right, it’s only been ten months, yet it feels like an eternity.</p>
<a id="in-the-beginning" href="#in-the-beginning" class="anchor"></a>
<a id="the-first-golden-age" href="#the-first-golden-age" class="anchor"></a>
<a id="disappointment" href="#disappointment" class="anchor"></a>
<a id="refocus" href="#refocus" class="anchor"></a>
<a id="volte-face" href="#volte-face" class="anchor"></a>
<a id="just-in-time-for-the-new-year" href="#just-in-time-for-the-new-year" class="anchor"></a>
<a id="arborium" href="#arborium" class="anchor"></a>
<a id="dodeca" href="#dodeca" class="anchor"></a>
<a id="rapace" href="#rapace" class="anchor"></a>
<a id="tracey" href="#tracey" class="anchor"></a>
<a id="picante" href="#picante" class="anchor"></a>
<a id="pikru" href="#pikru" class="anchor"></a>
<a id="aasvg-rs" href="#aasvg-rs" class="anchor"></a>
<a id="facet-keeps-growing" href="#facet-keeps-growing" class="anchor"></a>
<a id="fs-kitty" href="#fs-kitty" class="anchor"></a>
<a id="vixen" href="#vixen" class="anchor"></a>
<a id="conclusion" href="#conclusion" class="anchor"></a>
Introducing arborium, a tree-sitter distributionhttps://fasterthanli.me/articles/introducing-arborium2025-12-13T10:00:00Z<p data-bo="212">About two weeks ago I entered a discussion with the docs.rs team about,
basically, why we have to look at this:</p>
<p data-bo="325"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~2b51213d5080e49f.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~a02d3ed6405331c3.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~aae2486dd16622f8.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~90422bc81a3bae50.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~eb49252c206ebe40.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~eb49252c206ebe40.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~d4be62e07bbcfeb9.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~3ccc4eaf851fd7f9.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~31f05cfeabbb1816.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~c880f407070dec4f.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~246a726e3edb536e.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~246a726e3edb536e.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~108d2cd88f7f82ed.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~db28f2a92f2ec608.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~2560bc7eefd9f111.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~2620f2eb578c2766.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~c8ad806e4a94f7fb.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~c8ad806e4a94f7fb.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-no-colors@2x~eb49252c206ebe40.jxl" loading="lazy" width="1083" height="750" data-kind="media" data-input-path="/content/articles/introducing-arborium/[email protected]" class="" title="" alt="My browser showing a docs.rs page for a crate that I published myself, which contains a lot of different code blocks with different languages but they're all white on black. It's sad.
"></picture></p><p data-bo="578">When we could be looking at this:</p>
<p data-bo="613"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~9b1deb8e99fb2682.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~27ccd79ae4035c0a.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~bd30aa29b5400eb0.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~e20e499d5e65834e.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~708d0f07e1265747.jxl 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~708d0f07e1265747.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~e3f1a432e9c668ba.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~3a4993aab8948d72.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~53e5fad71d3fc327.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~08073874f4cd098d.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~fc0a176cb0cca55d.avif 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~fc0a176cb0cca55d.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~467941c8faca5817.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~fb9d0efd7aea8f39.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~5ddf9c85ebaa74b0.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~a1a3796821a165a5.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~c3080fac6477bf93.webp 1x,https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~c3080fac6477bf93.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/introducing-arborium/docsrs-yes-colors@2x~708d0f07e1265747.jxl" loading="lazy" width="1083" height="750" data-kind="media" data-input-path="/content/articles/introducing-arborium/[email protected]" class="" title="" alt="My browser showing a docs.rs page for a crate that I published myself, which contains a lot of different code blocks with different languages. this time it's colored.
"></picture></p><p data-bo="850">And of course, as always, there are reasons why things are the way they are.
In an effort to understand those reasons, I opened a GitHub issue which resulted
in a <a href="https://github.com/rust-lang/docs.rs/issues/3040">short but productive</a> discussion.</p>
<p data-bo="1100">I walked away discouraged, and then decided to, reasons be damned, attack this
problem from three different angles.</p>
<a id="background" href="#background" class="anchor"></a>
<a id="problems" href="#problems" class="anchor"></a>
<a id="solutions" href="#solutions" class="anchor"></a>
<a id="arborium" href="#arborium" class="anchor"></a>
<a id="angle-1-just-include-this-script" href="#angle-1-just-include-this-script" class="anchor"></a>
<a id="angle-2-it-goes-in-the-rustdoc-hole" href="#angle-2-it-goes-in-the-rustdoc-hole" class="anchor"></a>
<a id="angle-3-only-in-the-backend" href="#angle-3-only-in-the-backend" class="anchor"></a>
<a id="post-mortem" href="#post-mortem" class="anchor"></a>
<a id="conclusion" href="#conclusion" class="anchor"></a>
Does Dioxus spark joy?https://fasterthanli.me/articles/does-dioxus-spark-joy2025-11-22T18:30:00Z<div data-bo="230" class="dialog right">
<div class="dialog-head" title="Amos says:">
<picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~7e12af3470699bdc.w400.jxl 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~a0ea81dcadb8636e.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~d884997b42fbba49.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~55a3477398fe0cb1.jxl 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~55a3477398fe0cb1.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~45a3cd0b732f5ccc.w400.avif 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~e48742392d184dda.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~eea766689482b5e8.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~b094ff3737f6454a.avif 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~b094ff3737f6454a.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~87fb82d4e532a923.w400.webp 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~b07c9412606c8946.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~181264732affc244.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~3a98ffaa495414b6.webp 1x,https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~3a98ffaa495414b6.webp 2x"><img src="https://cdn.fasterthanli.me/content/img/reimena/amos-neutral~55a3477398fe0cb1.jxl" loading="lazy" width="42" height="42" data-kind="media" data-input-path="/content/img/reimena/amos-neutral.jxl" alt="Amos"></picture>
</div>
<div class="dialog-text markup-container">
<p data-bo="246">Note: this article is adapted from a presentation I gave at a Rust Paris Meetup — that’s why
it sounds a little different than usual. Enjoy!</p>
</div>
</div><p data-bo="390">Good evening! Tonight, I will attempt to answer the question: Does
<a href="https://github.com/dioxuslabs/dioxus">Dioxus</a> spark joy? Or at the very least,
whimsy.</p>
<p data-bo="546">What’s Dioxus, you ask? It is first and foremost a name that is quote: “legally
not inspired by any Pokémon”.</p>
<p data-bo="658"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~82c4e34c3c6f5eeb.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~0315622a9e29cfb9.w800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~d76eb1e3eeda1b65.jxl 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~d76eb1e3eeda1b65.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~d829d6b30cad4364.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~d790647bf99f1274.w800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~f00491aeaf5c170c.avif 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~f00491aeaf5c170c.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~5b906902d3583bdc.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~0d7b4e538adc55eb.w800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~5167eb18e833ab99.webp 1x,https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~5167eb18e833ab99.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/does-dioxus-spark-joy/deoxys@2x~d76eb1e3eeda1b65.jxl" loading="lazy" width="422" height="449" data-kind="media" data-input-path="/content/articles/does-dioxus-spark-joy/[email protected]" class="" title="" alt="The deoxys pokemon
"></picture></p><p data-bo="736">Even if the author concedes <a href="https://news.ycombinator.com/item?id=39853385">in a Hacker News comment</a> that the “Deoxys” Pokémon
is, I quote: “awesome”.</p>
<a id="a-short-and-mostly-wrong-history-of-web-apps" href="#a-short-and-mostly-wrong-history-of-web-apps" class="anchor"></a>
<a id="a-practical-example" href="#a-practical-example" class="anchor"></a>
<a id="love-hate" href="#love-hate" class="anchor"></a>
<a id="does-dioxus-spark-joy" href="#does-dioxus-spark-joy" class="anchor"></a>
<a id="afterword" href="#afterword" class="anchor"></a>
Engineering a Rust optimization quizhttps://fasterthanli.me/articles/engineering-a-rust-optimization-quiz2025-11-01T11:08:00Z<p data-bo="198">There are several Rust quizzes online, including one that’s literally called the
“Unfair Rust Quiz” at <a href="https://this.quiz.is.fckn.gay/">https://this.quiz.is.fckn.gay/</a>, but when I was given the
opportunity to record an episode of the <a href="https://sdr-podcast.com/">Self-Directed Research
podcast</a> live on the main stage of <a href="https://eurorust.eu/2025/">EuroRust
2025</a>, I thought I’d come up with something special.</p>
<figure
class="paragraph-like"
data-bo="576"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~ed108ab5e398e6fd.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~9093a2a7a70cc8e5.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~d7a48a7116058709.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~478a86d3446281d7.jxl 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~478a86d3446281d7.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~aa7feff27338fbf1.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~90dd56ba49448e1b.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~f09872ca2f77e192.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~fed46df743b852f4.avif 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~fed46df743b852f4.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~08ad393242985292.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~ee23edc5aa00cc8d.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~c25e7d8684579398.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~58012d0f832f4c70.webp 1x,https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~58012d0f832f4c70.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/engineering-a-rust-optimization-quiz/unfair-rust-quiz@2x~478a86d3446281d7.jxl" loading="lazy" width="795" height="714" data-kind="media" data-input-path="/content/articles/engineering-a-rust-optimization-quiz/[email protected]" title="The unfair rust quiz really deserves its name. It is best passed with a knowledgeable friend by your side. " alt="Question Misc 6 of the unfair Rust quiz, about drop order. "></picture><figcaption><div class="markup-container figure-caption"><p>The unfair rust quiz really deserves its name. It is best passed with a knowledgeable friend by your side.</p>
</div></figcaption>
</figure>
<a id="coming-up-with-the-questions" href="#coming-up-with-the-questions" class="anchor"></a>
<a id="nih-syndrome-ppt" href="#nih-syndrome-ppt" class="anchor"></a>
<a id="server-side-shenanigans-and-room-codes" href="#server-side-shenanigans-and-room-codes" class="anchor"></a>
<a id="deploying-the-beast" href="#deploying-the-beast" class="anchor"></a>
<a id="d-2-starting-fights-at-a-paris-meetup" href="#d-2-starting-fights-at-a-paris-meetup" class="anchor"></a>
<a id="d-1-panic-mode-and-missing-explanations" href="#d-1-panic-mode-and-missing-explanations" class="anchor"></a>
<a id="day-of-github-oauth-and-swipe-gestures" href="#day-of-github-oauth-and-swipe-gestures" class="anchor"></a>
<a id="showtime" href="#showtime" class="anchor"></a>
Making our own spectrogramhttps://fasterthanli.me/articles/making-our-own-spectrogram2025-09-15T07:30:00Z<p data-bo="251">A couple months ago <a href="/articles/the-science-of-loudness">I made a loudness meter</a>
and went way too in-depth into how humans have measured loudness over time.</p>
<p data-bo="409"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~83011aa317956c90.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~afea7ab87cdb0d3f.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~183d2ded1d67416a.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~d5e6b54e3ade21f5.jxl 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~d5e6b54e3ade21f5.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~816fc9f55dd14a52.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~26770327a4ceaf2c.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~be2cf2c157a31891.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~93c9325ad8ad55a3.avif 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~93c9325ad8ad55a3.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~c42c1310f17eb713.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~64ed1b3feeb6f6d3.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~13a8a3896d7c6881.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~23be1cc4f4574700.webp 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~23be1cc4f4574700.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/fasterthanlime-audio-meter@2x~d5e6b54e3ade21f5.jxl" loading="lazy" width="800" height="512" data-kind="media" data-input-path="/content/articles/making-our-own-spectrogram/[email protected]" class="" title="" alt="A screenshot of the fasterthanlime audio meter, with RMS, sample peak, true peak, and various loudness metrics.
"></picture></p><p data-bo="600">Today we’re looking at a spectrogram visualization I made, which is a lot more entertaining!</p>
<p data-bo="694"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~bfdf5332bac1c4c4.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~03877fa1fba54fec.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~eadeb08efadadc5c.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~1524786a02c13b76.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~c22af608785d58b2.jxl 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~c22af608785d58b2.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~d96983639bdbfe10.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~b7e95b9e54f9f2cd.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~7ea414d9dfd2cc5b.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~2f742d7c1339e1f1.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~12c9a6d75574bcc5.avif 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~12c9a6d75574bcc5.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~0f38fae704871a33.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~ac8675f3843dc5b7.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~2854399479c75822.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~fca0532957b2cf6c.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~8fe32b7d298a0aaa.webp 1x,https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~8fe32b7d298a0aaa.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/making-our-own-spectrogram/spectrogram-sonata-op25-3@2x~c22af608785d58b2.jxl" loading="lazy" width="1100" height="632" data-kind="media" data-input-path="/content/articles/making-our-own-spectrogram/[email protected]" class="" title="" alt=""></picture></p><p data-bo="753">We’re going to talk about how to extract frequencies from sound waves, but also
how my spectrogram app is assembled from different Rust crates, how it
handles audio and graphics threads, how it draws the spectrogram etc.</p>
<a id="the-humble-sine-wave" href="#the-humble-sine-wave" class="anchor"></a>
<a id="approximating-a-square-wave" href="#approximating-a-square-wave" class="anchor"></a>
<a id="a-real-world-sample" href="#a-real-world-sample" class="anchor"></a>
<a id="chunking-and-windowing" href="#chunking-and-windowing" class="anchor"></a>
<a id="the-gabor-limit" href="#the-gabor-limit" class="anchor"></a>
<a id="interpolation" href="#interpolation" class="anchor"></a>
<a id="color-mapping" href="#color-mapping" class="anchor"></a>
<a id="frequency-mapping" href="#frequency-mapping" class="anchor"></a>
<a id="audio-input" href="#audio-input" class="anchor"></a>
<a id="drawing-the-ui" href="#drawing-the-ui" class="anchor"></a>
<a id="updating-the-texture" href="#updating-the-texture" class="anchor"></a>
<a id="profiling-my-program" href="#profiling-my-program" class="anchor"></a>
<a id="having-fun" href="#having-fun" class="anchor"></a>
<a id="closing-words" href="#closing-words" class="anchor"></a>
crates.io phishing attempthttps://fasterthanli.me/articles/crates-io-phishing-attempt2025-09-12T14:00:00Z<p data-bo="160">Earlier this week, an <a href="/articles/color-npm-package-compromised">npm supply chain attack</a>.</p>
<p data-bo="251">It’s turn for <a href="https://crates.io">crates.io</a>, the main public repository for <a href="https://rust-lang.org">Rust</a>
crates (packages).</p>
<p data-bo="378">The phishing e-mail looks like this:</p>
<figure
class="paragraph-like"
data-bo="416"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~13b1cc912a064403.w400.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~052f360399d29116.jxl 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~052f360399d29116.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~0965dc15bd5795cf.w400.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~4adc3538b38cd5d0.avif 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~4adc3538b38cd5d0.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~569c8ebc7740ee8f.w400.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~f5f81e8409287a73.webp 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~f5f81e8409287a73.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/phishing-email~052f360399d29116.jxl" loading="lazy" width="708" height="653" data-kind="media" data-input-path="/content/articles/crates-io-phishing-attempt/phishing-email.jxl" title="" alt="A phishing e-mail: Important: Breach notification regarding crates.io Hi, BurntSushi! We recently discovered that an unauthorized actor had compromised the crates.io infrastructure and accessed a limited amount of user information. The attacker's access was revoked, and we are currently reviewing our security posture. We are currently drafting a blog post to outline the timeline and the steps we took to mitigate this. In the meantime, we strongly suggest you to rotate your login info by signing in here to our internal SSO, which is a temporary fix to ensure that the attacker cannot modify any packages published by you. "></picture><figcaption><cite><a href="https://bsky.app/profile/burntsushi.net/post/3lynehptw6c2n
">Andrew Gallant on BlueSky
</a></cite></figcaption>
</figure><p data-bo="1262">And it leads to a GitHub login page that looks like this:</p>
<figure
class="paragraph-like"
data-bo="1321"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~f39542ca9ae9f0c3.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~5429425a2625794b.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~ff6053b5d2562aeb.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~e78524d35ede5efb.jxl 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~e78524d35ede5efb.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~de116d2a77f034de.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~e4d40b2fbaa0c23e.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~5f55c6d615078899.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~f28292ec85f76009.avif 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~f28292ec85f76009.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~21a0077b4369fdc4.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~34797abd3fa323ca.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~324abfa3a52194e8.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~bf34180d4a8419bd.webp 1x,https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~bf34180d4a8419bd.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/crates-io-phishing-attempt/github-phish~e78524d35ede5efb.jxl" loading="lazy" width="1322" height="1378" data-kind="media" data-input-path="/content/articles/crates-io-phishing-attempt/github-phish.jxl" title="" alt="A fake GitHub sign-in page. "></picture><figcaption><cite><a href="https://github.com/rust-lang/crates.io/discussions/11889#discussion-8886064
">Barre on GitHub
</a></cite></figcaption>
</figure><p data-bo="1548">Several maintainers received it — the issue is being discussed <a href="https://github.com/rust-lang/crates.io/discussions/11889">on GitHub</a>.</p>
<p data-bo="1685">The <a href="https://www.rust-lang.org/governance/teams/dev-tools#team-crates-io">crates.io team</a> has acknowledged
the attack and said they’d see if they can do something about it.</p>
color npm package compromisedhttps://fasterthanli.me/articles/color-npm-package-compromised2025-09-08T16:50:00Z<p data-bo="148">On September 8 2025, around 13:00 UTC, someone compromised <a href="https://www.npmjs.com/~qix">Josh Junon’s npm
account (qix)</a> and started publishing backdoored
versions of his package.</p>
<p data-bo="328">Someone noticed and let Josh know:</p>
<figure
class="paragraph-like"
data-bo="364"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~0ad954d4042cc44e.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~a5ec0b93bba7ec30.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~f4efba9875cd59ab.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~4a9e74f87760a4af.jxl 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~4a9e74f87760a4af.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~a77a052fa30939ec.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~2c4593ff2e898847.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~424c77cae67f76e3.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~2a91320fae70db76.avif 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~2a91320fae70db76.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~4b4a2f263f473471.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~4b2488064d4e9adb.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~ca45cce753873308.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~33efd9d23f34908e.webp 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~33efd9d23f34908e.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/charlie-noticed@2x~4a9e74f87760a4af.jxl" loading="lazy" width="595" height="177" data-kind="media" data-input-path="/content/articles/color-npm-package-compromised/[email protected]" title="" alt="Hey. Your npm account seems to have been compromised. 1 hour ago it started posting packages with backdoors to all your popular packages. "></picture><figcaption><cite><a href="https://bsky.app/profile/charlieeriksen.bsky.social/post/3lydffcyulc2n
">Charlie Eriksen on BlueSky
</a></cite></figcaption>
</figure><p data-bo="713">Josh confirmed he’d gotten pwned by a fake 2FA (two-factor authentication) reset e-mail:</p>
<figure
class="paragraph-like"
data-bo="803"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~a179783538de1f4d.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~7c915c01a7921e87.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~1a4695014120bb13.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ca37f72d582a4442.jxl 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ca37f72d582a4442.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~a8368bb997745224.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~731a6e9261ec5aa6.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~2ef1999737857730.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~0625d89fd48a2fad.avif 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~0625d89fd48a2fad.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~97b58ff0b566eb23.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ab64f921056092b7.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~6f99f1fb2081d88c.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ff16c7198a608a53.webp 1x,https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ff16c7198a608a53.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/color-npm-package-compromised/josh-fake-2fa@2x~ca37f72d582a4442.jxl" loading="lazy" width="592" height="396" data-kind="media" data-input-path="/content/articles/color-npm-package-compromised/[email protected]" title="" alt="Yep, I've been pwned. 2FA reset email, looked very legitimate. Only NPM affected. I've sent an email off to @npmjs.bsky.social to see if I can get access again. Sorry everyone, I should have paid more attention. Not like me; have had a stressful week. Will work to get this cleaned up. "></picture><figcaption><cite><a href="https://bsky.app/profile/bad-at-computer.bsky.social/post/3lydioq5swk2y
">Josh Junon on BlueSky
</a></cite></figcaption>
</figure><p data-bo="1329">The phishing e-mail came from <code>npmsj.help</code> (registered 3 days prior) and claimed
users had to reset their 2FA:</p>
<a id="the-payload" href="#the-payload" class="anchor"></a>
<a id="current-situation" href="#current-situation" class="anchor"></a>
The science of loudnesshttps://fasterthanli.me/articles/the-science-of-loudness2025-08-22T20:30:00Z<p data-bo="246">My watch has a “Noise” app: it shows <math display="inline"><mrow><mi mathvariant="normal">d</mi><mi mathvariant="normal">B</mi></mrow></math>, for decibels.</p>
<p data-bo="313"><video controls playsinline preload="none" loading="lazy" width="3840" height="2160" data-kind="media" data-input-path="/content/articles/the-science-of-loudness/apple-watch-decibel-meter.mp4" poster="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/apple-watch-decibel-meter.thumb" class="" title="" alt="A video of my Apple Watch showing me how loud the sound coming from my speakers is.
"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/apple-watch-decibel-meter~d96a722c49fdda5d.mp4" type="video/mp4; codecs=av01.0.08M.08,opus"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/apple-watch-decibel-meter~e53858e76f36614d.h264+aac.mp4" type="video/mp4; codecs=avc1.640034,mp4a.40.2"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/apple-watch-decibel-meter~fb169411c4d8653e.vp9+opus.webm" type="video/webm; codecs=vp09.00.41.08,opus">Your browser does not support the video tag.</video></p><p data-bo="464">My amp has a volume knob, which also shows decibels, although.. negative ones, this time.</p>
<p data-bo="555"><video controls playsinline preload="none" loading="lazy" width="3840" height="2160" data-kind="media" data-input-path="/content/articles/the-science-of-loudness/adjust-volume.mp4" poster="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/adjust-volume.thumb" class="" title="" alt="A video of me adjusting my volume on my Cambridge AXR100 amplifier. The
volume goes from -61 to -32 decibels in that video.
"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/adjust-volume~46153fb1ad60f230.mp4" type="video/mp4; codecs=av01.0.08M.08,opus"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/adjust-volume~a8af11cbe6d90598.h264+aac.mp4" type="video/mp4; codecs=avc1.640034,mp4a.40.2"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/adjust-volume~9a7388f3f768f948.vp9+opus.webm" type="video/webm; codecs=vp09.00.41.08,opus">Your browser does not support the video tag.</video></p><p data-bo="746">And finally, my video editing software has a ton of meters — which are all in decibel or
decibel-adjacent units.</p>
<p data-bo="862"><video controls playsinline preload="none" loading="lazy" width="2002" height="1127" data-kind="media" data-input-path="/content/articles/the-science-of-loudness/[email protected]" poster="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/[email protected]" class="" title="" alt="A screenshot of DaVinci Resolve, showing various meters: we have Bus 1,
Control Room with TP, Loudness, YouTube (LUFS), then Loudness History
with Integrated, Momentary and Short Term. In the Mixer, each track has
its meter.
"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/tons-of-meters@2x~9cc29f2809d1202a.mp4" type="video/mp4; codecs=av01.0.08M.08,opus"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/tons-of-meters@2x~da316f2ab0e0aeb5.h264+aac.mp4" type="video/mp4; codecs=avc1.640034,mp4a.40.2"><source src="https://cdn.fasterthanli.me/content/articles/the-science-of-loudness/tons-of-meters@2x~e50f9e76cf48090c.vp9+opus.webm" type="video/webm; codecs=vp09.00.41.08,opus">Your browser does not support the video tag.</video></p><p data-bo="1178">How do all these decibels fit together?</p>
<a id="what-even-is-sound" href="#what-even-is-sound" class="anchor"></a>
<a id="under-pressure" href="#under-pressure" class="anchor"></a>
<a id="signal-processing" href="#signal-processing" class="anchor"></a>
<a id="root-mean-square" href="#root-mean-square" class="anchor"></a>
<a id="sample-peak-true-peak" href="#sample-peak-true-peak" class="anchor"></a>
<a id="the-loudness-wars" href="#the-loudness-wars" class="anchor"></a>
<a id="a-weighting" href="#a-weighting" class="anchor"></a>
Summer fasterthanlime updatehttps://fasterthanli.me/articles/summer-fasterthanlime-update2025-08-15T07:30:00Z<p data-bo="147">There are news!</p>
<div data-bo="166" class="tip markup-container">
<div class="tip-header bear-mark">
<picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~f8031274cabccdf6.w400.jxl 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~451aa11f175f8fc4.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~2add1c1dfe1cf845.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~85823bd96ffd0bb6.jxl 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~85823bd96ffd0bb6.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~e1d6acd7da0c8bd7.w400.avif 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~fe0a016a137f310a.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~909ed9e4ba5c3317.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~165a8452ce0788cd.avif 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~165a8452ce0788cd.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~c7e5fe0bec9ef4d6.w400.webp 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~430cbb8ae0eaa295.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~9df5546a60e748fc.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~ff0c651f16d7613d.webp 1x,https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~ff0c651f16d7613d.webp 2x"><img src="https://cdn.fasterthanli.me/content/img/reimena/coolbear-idea~85823bd96ffd0bb6.jxl" loading="lazy" width="48" height="48" data-kind="media" data-input-path="/content/img/reimena/coolbear-idea.jxl" class="bear" alt="Cool bear"></picture>
Cool Bear's hot tip
</div>
<p data-bo="177">TL;DR: If you’re a patron or sponsor, check your <a href="/profile">Profile</a> page to
get detailed explainers of every perk. You’ll need to log in. Duh.</p>
</div><p data-bo="322">Here are all the changes I’m implementing, summarized as a table:</p>
<div class="responsive-table" data-bo="389"><table><thead><td>Before</td><td>After</td></thead><tr><td>📚 Articles remain exclusive for <strong>6 months</strong></td><td>Early access (<strong>couple weeks</strong>) for Silver tier</td></tr><tr><td>🎞️ No early access for video</td><td><strong>Video early access</strong> on Patreon and website</td></tr></table></div>
<a id="looking-back" href="#looking-back" class="anchor"></a>
<a id="a-discord-server" href="#a-discord-server" class="anchor"></a>
<a id="early-access-revamp" href="#early-access-revamp" class="anchor"></a>
<a id="dual-rss-feeds" href="#dual-rss-feeds" class="anchor"></a>
<a id="bye-ko-fi" href="#bye-ko-fi" class="anchor"></a>
<a id="more-casual-posting" href="#more-casual-posting" class="anchor"></a>
<a id="what-about-content-that-was-still-exclusive" href="#what-about-content-that-was-still-exclusive" class="anchor"></a>
All color is best-efforthttps://fasterthanli.me/articles/all-color-is-best-effort2025-06-27T07:30:00Z<p data-bo="224">I do not come to you with answers today, but rather some observations and a lot of questions.</p>
<a id="the-weird-glitch" href="#the-weird-glitch" class="anchor"><h2>The weird glitch</h2></a>
<p data-bo="340">Recently I was editing some video and I noticed this:</p>
<p data-bo="395">
<picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~bc15541e1738a8c6.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~611f0686a4304520.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~6c16884b3cbe3da0.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~9665a9740fd741f6.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~3c031f1f42693336.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~3c031f1f42693336.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~787a8860926cf053.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~bb7c6eecccd060d7.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~a7e8c949bca8b7a5.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~855d2918693652f1.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~9b7179dcf280ef82.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~9b7179dcf280ef82.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~3eeef026e1c97bfb.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~2bd7304336f5d982.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~56622c6c2c1cd39a.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~80cae7813b764663.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~ecaf6f4a32a1a287.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~ecaf6f4a32a1a287.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/dvr-circles@2x~3c031f1f42693336.jxl" loading="lazy" width="1966" height="1126" data-kind="media" data-input-path="/content/articles/all-color-is-best-effort/[email protected]" title="A screenshot of the video, there are visible circles at various places in the image. Some of them are black, some of them are white. The image itself shows some blue and white text composited on some blurry background, which doesn’t really matter for this, and there’s a red line horizontal up in the image. It’s very confusing." alt="A screenshot of the video, there are visible circles at various places in the image. Some of them are black, some of them are white. The image itself shows some blue and white text composited on some blurry background, which doesn’t really matter for this, and there’s a red line horizontal up in the image. It’s very confusing."></picture></p>
<p data-bo="748">Not what the finger is pointing at — the dots.</p>
<p data-bo="798">Here are the separate layers this image is made up of: the background is a stock image
I’ve licensed from Envato Elements:</p>
<p data-bo="922"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~82e4590a81a91669.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~396cd6f7cdde1715.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~01c4c3e679fc97cb.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~64b3776886a40bc2.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~dcfb5771209ddbd5.jxl 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~dcfb5771209ddbd5.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~64e6c779ed984060.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~948fae22ff317166.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~43dd5455b145f94a.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~13f8922bbb8fc4ec.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~8b931c6a0152f29e.avif 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~8b931c6a0152f29e.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~43323deba1e5ee6f.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~62b8e8b4e862037b.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~0ec387af3f70ae28.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~5cf8c742dcce6076.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~39bff319d2958a1f.webp 1x,https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~39bff319d2958a1f.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/all-color-is-best-effort/canyon-background@2x~dcfb5771209ddbd5.jxl" loading="lazy" width="1966" height="1126" data-kind="media" data-input-path="/content/articles/all-color-is-best-effort/[email protected]" title="A picture of a canyon, darker than you’d expect." alt="A picture of a canyon, darker than you’d expect."></picture></p>
<p data-bo="1001">Because I use it as a background image, I’ve cranked down the exposition in the Color tab:</p>
<a id="playing-with-color-spaces" href="#playing-with-color-spaces" class="anchor"></a>
<a id="cie-chromaticity-diagram" href="#cie-chromaticity-diagram" class="anchor"></a>
<a id="our-first-transfer-function" href="#our-first-transfer-function" class="anchor"></a>
<a id="parade-scope" href="#parade-scope" class="anchor"></a>
<a id="more-transfer-functions" href="#more-transfer-functions" class="anchor"></a>
<a id="how-white-is-your-white" href="#how-white-is-your-white" class="anchor"></a>
<a id="conclusion" href="#conclusion" class="anchor"></a>
Introducing facet: Reflection for Rusthttps://fasterthanli.me/articles/introducing-facet-reflection-for-rust2025-06-05T19:00:01Z<p data-bo="946">I have long been at war against Rust compile times.</p>
<p data-bo="999">Part of the solution for me was to buy my way into Apple Silicon dreamland,
where builds are, like… faster. I remember every time I SSH into an x86_64
server, even the nice 64-core ones.</p>
<p data-bo="1189">And another part was, of course, to get dirty with Rust itself.</p>
<p data-bo="1254">I wrote <a href="/articles/why-is-my-rust-build-so-slow">Why is my Rust build so slow?</a>,
which goes in-depth into rust build performance, down to rustc self-profiling even!</p>
<a id="let-they-who-are-without-syn" href="#let-they-who-are-without-syn" class="anchor"></a>
<a id="monomorphization" href="#monomorphization" class="anchor"></a>
<a id="a-different-strategy" href="#a-different-strategy" class="anchor"></a>
<a id="today-tomorrow" href="#today-tomorrow" class="anchor"></a>
The virtue of unsynnhttps://fasterthanli.me/articles/the-virtue-of-unsynn2025-05-29T17:20:01Z<a id="addressing-the-rumors" href="#addressing-the-rumors" class="anchor"><h2>Addressing the rumors</h2></a>
<p data-bo="229">There have been rumors going around, in the Reddit thread for
<a href="https://github.com/facet-rs/facet">facet</a>, my take on reflection in Rust, which
happened a bit too early, but here we are, cat’s out of the bag, let’s talk
about it!</p>
<p data-bo="459">Rumors that I, podcaster/youtuber fasterthanlime, want to kill
<a href="https://lib.rs/crates/serde">serde</a>, serialization / deserialization
framework loved by many and which contributed greatly to Rust’s success, and I
just wanted to address those rumors and say that…</p>
<a id="comparing-apples-and-nuclear-submarines" href="#comparing-apples-and-nuclear-submarines" class="anchor"></a>
<a id="macro-expansion" href="#macro-expansion" class="anchor"></a>
<a id="build-times-again" href="#build-times-again" class="anchor"></a>
<a id="enter-unsynn" href="#enter-unsynn" class="anchor"></a>
<a id="warm-builds" href="#warm-builds" class="anchor"></a>
<a id="putting-things-in-perspective" href="#putting-things-in-perspective" class="anchor"></a>
<a id="tooling" href="#tooling" class="anchor"></a>
Open sourcing the home CMShttps://fasterthanli.me/articles/open-sourcing-the-home-cms2025-04-22T12:00:00Z<p data-bo="140">I’ve been bragging about my website software for years! For… whew, it’s been 5 years!</p>
<figure
class="paragraph-like"
data-bo="229"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~dadde2bc6ed46bf5.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~ab5c6b2bc29c9c6a.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~c3237faef055e8cf.w900.jxl 1x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~57268eaf77aa3bf7.jxl 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~57268eaf77aa3bf7.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~915be8263dc3d03a.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~6a9ff5bff6b59f1e.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~35d8aeeaeda09852.w900.avif 1x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~7f0f4780f7ca8970.avif 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~7f0f4780f7ca8970.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~e8487760338c207e.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~f891dcabcc93ee1e.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~9bf9ba8dd701061f.w900.webp 1x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~d969fd1992b4a4b2.webp 1x,https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~d969fd1992b4a4b2.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/open-sourcing-the-home-cms/early-git-history@2x~57268eaf77aa3bf7.jxl" loading="lazy" width="643" height="527" data-kind="media" data-input-path="/content/articles/open-sourcing-the-home-cms/[email protected]" title="I didn't want to make a CMS! I did it out of spite! " alt="A screenshot of github commits for the beginning of my website. It has commit messages like 'learning rusqlite', 'walk stuff', 'import input files', 'parse frontmatter and stuff', and 'do stuff in parallel'. "></picture><figcaption><div class="markup-container figure-caption"><p>I didn't want to make a CMS! I did it out of spite!</p>
</div></figcaption>
</figure><p data-bo="581">I’ve been teasing folks about the cool things I did from the beginning — here are
all the articles and series I’ve written that mention it:</p>
<ul><li><a href="/articles/a-new-website-for-2020">2020: A new website</a></li>
<li><a href="/series/dont-shell-out">2021: Don’t shell out!</a></li>
<li><a href="/series/updating-fasterthanli-me-for-2022">2022: Updating fasterthanli.me</a></li>
<li><a href="/articles/face-cams-the-missing-guide">2024: Face cams: the missing guide</a></li>
<li><a href="/articles/state-of-the-fasterthanlime-2024">2024: State of the fasterthanlime</a></li>
</ul>
<a id="open-sourced-components" href="#open-sourced-components" class="anchor"></a>
<a id="bearcove-home-tree-sitter" href="#bearcove-home-tree-sitter" class="anchor"></a>
<a id="bearcove-home-drawio" href="#bearcove-home-drawio" class="anchor"></a>
<a id="bearcove-homebrew-tap" href="#bearcove-homebrew-tap" class="anchor"></a>
<a id="bearcove-timelord" href="#bearcove-timelord" class="anchor"></a>
<a id="bearcove-beardist" href="#bearcove-beardist" class="anchor"></a>
<a id="bearcove-home" href="#bearcove-home" class="anchor"></a>
<a id="closing-words" href="#closing-words" class="anchor"></a>
The promise of Rusthttps://fasterthanli.me/articles/the-promise-of-rust2025-04-16T20:30:00Z<p data-bo="200">The part that makes Rust scary is the part that makes it unique.</p>
<p data-bo="266">And it’s also what I miss in other programming languages — let me explain!</p>
<p data-bo="344">Rust syntax starts simple.</p>
<p data-bo="372">This function prints a number:</p>
<figure role="region" aria-label="Rust code block" class="code-block has-language-tag" translate="no" data-lang="rust" data-bo="404"><span class="language-tag" title="Rust"></span><code class="scroll-wrapper"><a-k>fn</a-k> <a-f>show</a-f><a-p>(</a-p><a-v>n</a-v><a-p>:</a-p> <a-t>i64</a-t><a-p>)</a-p> <a-p>{</a-p>
<a-m>println!</a-m><a-p>(</a-p><a-s>"n = {n}"</a-s><a-p>);</a-p>
<a-p>}</a-p>
</code></figure><p data-bo="462">And this program calls that function — it looks like any C-family language so far,
we got parentheses, we got curly brackets, we got, uhh…</p>
<div data-bo="608" class="dialog">
<div class="dialog-head bear-mark" title="Cool bear says:">
</div>
</div><a id="rust-move-semantics" href="#rust-move-semantics" class="anchor"></a>
<a id="javascript-semantics" href="#javascript-semantics" class="anchor"></a>
<a id="go-semantics" href="#go-semantics" class="anchor"></a>
<!-- playwall -->
<a id="deep-cloning" href="#deep-cloning" class="anchor"></a>
<a id="constness-in-c-and-c" href="#constness-in-c-and-c" class="anchor"></a>
<a id="linear-types" href="#linear-types" class="anchor"></a>
<a id="immutability-in-rust" href="#immutability-in-rust" class="anchor"></a>
<a id="unsafe-rust" href="#unsafe-rust" class="anchor"></a>
<a id="aliasing-xor-mutability" href="#aliasing-xor-mutability" class="anchor"></a>
That health is mentalhttps://fasterthanli.me/articles/that-health-is-mental2025-04-09T11:00:00Z<div data-bo="108" class="disclosure paragraph-like">
<p data-bo="55"><strong>Disclaimer</strong>:</p>
<p data-bo="126">Trigger warning: depression, talk of suicide.</p>
</div><p data-bo="173">It’s been a while since I wrote a mental health piece — but I think it’s
important to occasionally stop, take a breather, and think about how we feel.</p>
<p data-bo="327">So.</p>
<p data-bo="332"><em>deep breath</em></p>
<p data-bo="347">I’m okay, I think? Just a little restless.</p>
<a id="a-bit-of-personal-context" href="#a-bit-of-personal-context" class="anchor"><h2>A bit of personal context</h2></a>
<p data-bo="421">For those keeping score, I went through major life events in 2023 — a divorce, a move,
and the news that I might need a second round of jaw surgery.</p>
<a id="meds-meds-meds" href="#meds-meds-meds" class="anchor"></a>
<a id="autism-adhd-audhd" href="#autism-adhd-audhd" class="anchor"></a>
<a id="what-now" href="#what-now" class="anchor"></a>
More devops than I bargained forhttps://fasterthanli.me/articles/more-devops-than-i-bargained-for2025-04-07T19:10:00Z<a id="background" href="#background" class="anchor"><h2>Background</h2></a>
<p data-bo="149">I recently had a bit of <a href="/articles/impromptu-disaster-recovery">impromptu disaster recovery</a>,
and it gave me a hunger for more! More downtime! More kubernetes manifest!
More DNS! Ahhhh!</p>
<p data-bo="336">The plan was really simple. I love dedicated Hetzner servers with all my heart but they are not very fungible.</p>
<p data-bo="448">You have to wait entire minutes for a new dedicated server to be provisioned.
Sometimes you pay a setup fee, et cetera. And at some point to server static
websites and serve as a K3S server, it’s simply just too big, and approximately
twice the price that I should pay.</p>
<a id="has-anyone-ever-built-it-for-arm64-linux-before" href="#has-anyone-ever-built-it-for-arm64-linux-before" class="anchor"></a>
<a id="multi-arch-container-images" href="#multi-arch-container-images" class="anchor"></a>
<a id="base-images-regctl" href="#base-images-regctl" class="anchor"></a>
<a id="more-like-ipv5" href="#more-like-ipv5" class="anchor"></a>
<a id="the-nat-king-calls" href="#the-nat-king-calls" class="anchor"></a>
<a id="why-the-fuck-is-everything-broken" href="#why-the-fuck-is-everything-broken" class="anchor"></a>
<a id="bye-natalie" href="#bye-natalie" class="anchor"></a>
Impromptu disaster recoveryhttps://fasterthanli.me/articles/impromptu-disaster-recovery2025-03-28T14:10:00Z<a id="background" href="#background" class="anchor"><h2>Background</h2></a>
<blockquote><p data-bo="145">im-promp-tu (<code>im-ˈpräm(p)-(ˌ)tü</code>)</p>
<ol start="1"><li><p data-bo="190">made, done, or formed on or as if on the spur of the moment: <a href="https://www.merriam-webster.com/dictionary/improvised">improvised</a></p>
</li>
<li><p data-bo="326">composed or uttered without previous preparation: <a href="https://www.merriam-webster.com/dictionary/extemporaneous">extemporaneous</a></p>
</li>
</ol>
<p data-bo="456"><a href="https://www.merriam-webster.com/dictionary/impromptu">Merriam-Webster</a></p>
</blockquote>
<p data-bo="529">On March 18th, 2025, I thought I would look into self-hosted project management
solutions — something kanban-y, but.. better?</p>
<figure
class="paragraph-like"
data-bo="658"><picture><source type="image/jxl" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~9ad11f43cb6d0e44.w400.jxl 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~100ea6c822aa7dec.w800.jxl 2x"><source type="image/jxl" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~ff8c2d0c0067f846.w900.jxl 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~7bd1d6fe30b467e7.w1800.jxl 2x"><source type="image/jxl" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~b4bc2b8bc06d1645.jxl 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~b4bc2b8bc06d1645.jxl 2x"><source type="image/avif" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~ea2de89efdf9d35e.w400.avif 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~237380f7c9c2d1d0.w800.avif 2x"><source type="image/avif" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~3845793759a93024.w900.avif 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~7136e281b03f3ffa.w1800.avif 2x"><source type="image/avif" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~745613f6aaadf300.avif 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~745613f6aaadf300.avif 2x"><source type="image/webp" media="(max-width: 400px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~ad8467cd28541565.w400.webp 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~9aec87da7a230bbb.w800.webp 2x"><source type="image/webp" media="(max-width: 900px)" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~c509af87e260fb83.w900.webp 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~449c9db68cc15783.w1800.webp 2x"><source type="image/webp" srcset="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~24e9b79bc76baa2c.webp 1x,https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~24e9b79bc76baa2c.webp 2x"><img src="https://cdn.fasterthanli.me/content/articles/impromptu-disaster-recovery/wekan@2x~b4bc2b8bc06d1645.jxl" loading="lazy" width="1084" height="793" data-kind="media" data-input-path="/content/articles/impromptu-disaster-recovery/[email protected]" title="This one does not spark joy. " alt="A screenshot of WeKan, the open-source Kanban. It looks.. from another age."></picture><figcaption><div class="markup-container figure-caption"><p>This one does not spark joy.</p>
</div></figcaption>
</figure><p data-bo="833">After discovering that <a href="https://teamhood.com">Teamhood</a> was awesome (and
EU-based), but had a 3-seat minimum on their subscriptions, I resigned to
reluctantly self-host something.</p>
<a id="how-i-deploy-stuff-on-k3s" href="#how-i-deploy-stuff-on-k3s" class="anchor"></a>
<a id="the-k3s-reconciler" href="#the-k3s-reconciler" class="anchor"></a>
<a id="the-reformattening" href="#the-reformattening" class="anchor"></a>
<a id="no-thoughts-head-weighty" href="#no-thoughts-head-weighty" class="anchor"></a>
<a id="gpt-4o" href="#gpt-4o" class="anchor"></a>
<a id="claude-3-7-sonnet" href="#claude-3-7-sonnet" class="anchor"></a>
<a id="deepseek-r1" href="#deepseek-r1" class="anchor"></a>
<a id="mistral-s-le-chat" href="#mistral-s-le-chat" class="anchor"></a>
<a id="the-aftermath" href="#the-aftermath" class="anchor"></a>
<a id="from-bad-to-worse" href="#from-bad-to-worse" class="anchor"></a>
<a id="no-matter-how-prepared" href="#no-matter-how-prepared" class="anchor"></a>
<a id="rsync-or-swim" href="#rsync-or-swim" class="anchor"></a>
<a id="debian-12-reinstall" href="#debian-12-reinstall" class="anchor"></a>
<a id="a-new-k3s-cluster" href="#a-new-k3s-cluster" class="anchor"></a>
<a id="just-enough-ansible-to-be-dangerous" href="#just-enough-ansible-to-be-dangerous" class="anchor"></a>
<a id="ca-troubles" href="#ca-troubles" class="anchor"></a>
<a id="bringing-back-the-essentials" href="#bringing-back-the-essentials" class="anchor"></a>
<a id="traefik-v3" href="#traefik-v3" class="anchor"></a>
<a id="cert-manager" href="#cert-manager" class="anchor"></a>
<a id="minio" href="#minio" class="anchor"></a>
<a id="k8up" href="#k8up" class="anchor"></a>
<a id="reflector" href="#reflector" class="anchor"></a>
<a id="cloudnativepg" href="#cloudnativepg" class="anchor"></a>
<a id="bringing-forgejo-back" href="#bringing-forgejo-back" class="anchor"></a>
<a id="postgres" href="#postgres" class="anchor"></a>
<a id="persistent-volume-restore" href="#persistent-volume-restore" class="anchor"></a>
<a id="deployment-service-ingress" href="#deployment-service-ingress" class="anchor"></a>
<a id="bringing-home-back" href="#bringing-home-back" class="anchor"></a>
<a id="zero-downtime-deploys" href="#zero-downtime-deploys" class="anchor"></a>
<a id="closing-words" href="#closing-words" class="anchor"></a>
The case for sans-iohttps://fasterthanli.me/articles/the-case-for-sans-io2025-02-07T18:53:01Z<p data-bo="208">The most popular option to decompress ZIP files from the Rust programming
language is a crate simply named <a href="https://lib.rs/crates/zip" id="zip">zip</a> — At the time of this writing, it has 48
million downloads. It’s fully-featured, supporting various compression methods,
encryption, and even supports writing zip files.</p>
<p data-bo="528">However, that’s not the crate <em>everyone</em> uses to read ZIP files. Some
applications benefit from using asynchronous I/O, especially if they decompress
archives that they download from the network.</p>
<a id="character-encoding-differences" href="#character-encoding-differences" class="anchor"></a>
<a id="platform-differences" href="#platform-differences" class="anchor"></a>
<a id="the-end-of-central-directory-record" href="#the-end-of-central-directory-record" class="anchor"></a>
<a id="boundary-confusion" href="#boundary-confusion" class="anchor"></a>
<!-- playwall -->
<a id="not-doing-any-i-o-at-all" href="#not-doing-any-i-o-at-all" class="anchor"></a>
<a id="the-structure-of-rc-zip" href="#the-structure-of-rc-zip" class="anchor"></a>
<a id="bringing-io-uring-into-it" href="#bringing-io-uring-into-it" class="anchor"></a>
<a id="plugging-rc-zip-into-monoio" href="#plugging-rc-zip-into-monoio" class="anchor"></a>
<a id="closing-words" href="#closing-words" class="anchor"></a>
Catching up with async Rusthttps://fasterthanli.me/articles/catching-up-with-async-rust2024-12-25T07:30:00Z<p data-bo="211">In December 2023, a minor miracle happened: <a href="https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html">async fn in traits</a> shipped.</p>
<p data-bo="354">As of Rust 1.39, we already had free-standing async functions:</p>
<figure role="region" aria-label="Rust code block" class="code-block has-language-tag" translate="no" data-lang="rust" data-bo="418"><span class="language-tag" title="Rust"></span><code class="scroll-wrapper"><a-k>pub</a-k> <a-k>async</a-k> <a-k>fn</a-k> <a-f>read_hosts</a-f><a-p>()</a-p> -> eyre<a-p>::</a-p><a-t>Result</a-t><a-p><</a-p><a-t>Vec</a-t><a-p><</a-p><a-t>u8</a-t><a-p>>></a-p> <a-p>{</a-p>
<a-c>// etc.</a-c>
<a-p>}</a-p>
</code></figure><p data-bo="498">…and async functions in impl blocks:</p>
<figure role="region" aria-label="Rust code block" class="code-block has-language-tag" translate="no" data-lang="rust" data-bo="538"><span class="language-tag" title="Rust"></span><code class="scroll-wrapper"><a-k>impl</a-k> <a-t>HostReader</a-t> <a-p>{</a-p>
<a-k>pub</a-k> <a-k>async</a-k> <a-k>fn</a-k> <a-f>read_hosts</a-f><a-p>(</a-p><a-o>&</a-o><a-v>self</a-v><a-p>)</a-p> -> eyre<a-p>::</a-p><a-t>Result</a-t><a-p><</a-p><a-t>Vec</a-t><a-p><</a-p><a-t>u8</a-t><a-p>>></a-p> <a-p>{</a-p>
<a-c>// etc.</a-c>
<a-p>}</a-p>
<a-p>}</a-p>
</code></figure><p data-bo="655">But we did not have async functions in traits:</p>
<a id="the-size-of-locals" href="#the-size-of-locals" class="anchor"></a>
<!-- playwall -->
<a id="just-boxing-it" href="#just-boxing-it" class="anchor"></a>
<a id="dynamic-dispatch" href="#dynamic-dispatch" class="anchor"></a>
<a id="dyn-compatibility" href="#dyn-compatibility" class="anchor"></a>
<a id="associated-types" href="#associated-types" class="anchor"></a>
<a id="a-refreshed-service-trait" href="#a-refreshed-service-trait" class="anchor"></a>
<a id="unnameable-types" href="#unnameable-types" class="anchor"></a>
<a id="lifetimes-a-refresher" href="#lifetimes-a-refresher" class="anchor"></a>
<a id="hidden-captures" href="#hidden-captures" class="anchor"></a>
<a id="relaxing-lifetime-bounds" href="#relaxing-lifetime-bounds" class="anchor"></a>
<a id="sendness" href="#sendness" class="anchor"></a>
<a id="afterword" href="#afterword" class="anchor"></a>
Highlighted code in slideshttps://fasterthanli.me/articles/highlighted-code-in-slides2024-11-23T17:40:00Z<p data-bo="223">I have obsessed about this long enough, I think it’s only fair I (and you!) get some content out of it.</p>
<p data-bo="328">When I started writing this article, I was working on my <a href="https://p99conf.io">P99 CONF</a> slides.
Those slides happen to include some bits of code. And because I’m a perfectionist, I would like this
code to be syntax highlighted, like this:</p>
<figure role="region" aria-label="Rust code block" class="code-block has-language-tag" translate="no" data-lang="rust" data-bo="568"><span class="language-tag" title="Rust"></span><code class="scroll-wrapper"><a-k>let</a-k> addr<a-p>:</a-p> <a-t>SocketAddr</a-t> = config<a-p>.</a-p><a-pr>address</a-pr><a-p>.</a-p><a-pr>parse</a-pr><a-p>()</a-p>?<a-p>;</a-p>
<a-k>let</a-k> ln = <a-cr>TcpListener</a-cr><a-p>::</a-p><a-f>bind</a-f><a-p>(</a-p>addr?
config
</code></figure>
<a id="not-invented-here" href="#not-invented-here" class="anchor"></a>
<!-- playwall -->
<a id="adding-a-button" href="#adding-a-button" class="anchor"></a>
<a id="computed-styles" href="#computed-styles" class="anchor"></a>
<a id="code-listing" href="#code-listing" class="anchor"></a>
<a id="color-is-hard" href="#color-is-hard" class="anchor"></a>
<a id="closing-thoughts" href="#closing-thoughts" class="anchor"></a>
ktls now under the rustls orghttps://fasterthanli.me/articles/ktls-now-under-rustls-org2024-09-26T21:10:00Z<a id="what-s-a-ktls" href="#what-s-a-ktls" class="anchor"><h2>What’s a ktls</h2></a>
<p data-bo="151">I started work on <a href="https://lib.rs/crates/ktls">ktls</a> and <a href="https://lib.rs/crates/ktls-sys">ktls-sys</a>,
a pair of crates exposing <a href="https://www.kernel.org/doc/html/latest/networking/tls-offload.html">Kernel TLS offload</a>
to Rust, about <a href="https://github.com/rustls/ktls/commit/798466d7c3e659ecdf035afac0bea2b679aea4c4">two years ago</a>.</p>
<p data-bo="486">kTLS lets the kernel (and, in turn, any network interface that supports it) take care of encryption, framing,
etc., for the entire duration of a TLS connection… as soon as you <em>have</em> a TLS connection.</p>
<p data-bo="690">For the handshake itself (hellos, change cipher, encrypted extensions,
certificate verification, etc.), you still have to use a userland TLS
implementation.</p>
<a id="the-ktls-api-today" href="#the-ktls-api-today" class="anchor"></a>
<a id="coordinating-collaborating" href="#coordinating-collaborating" class="anchor"></a>
State of the fasterthanlime 2024https://fasterthanli.me/articles/state-of-the-fasterthanlime-20242024-08-07T07:14:05Z<p data-bo="147">It’s time for some personal <em>and</em> professional news!</p>
<p data-bo="201">TL;DR: I started <a href="https://sdr-podcast.com">a podcast</a> with <a href="https://jamesmunns.com/contact/">James</a>,
I’m stable on antidepressants, I’m giving a <a href="https://www.p99conf.io/">P99 CONF</a> about my <a href="https://github.com/bearcove/fluke">Rust/io_uring/HTTP work</a>,
I’m trying on “they/them” as pronouns, I’m open-sourcing <a href="https://github.com/bearcove/merde_json">merde_json</a>,
<a href="https://github.com/bearcove/rubicon">rubicon</a> and others, I got a divorce in 2023, I found a new business model.</p>
<p data-bo="679">Now that we’re on the same page: let’s unpack this a bit!</p>
<a id="a-podcast-with-james" href="#a-podcast-with-james" class="anchor"></a>
<a id="all-the-personal-news" href="#all-the-personal-news" class="anchor"></a>
<a id="we-re-so-back" href="#we-re-so-back" class="anchor"></a>
<a id="a-new-business-model" href="#a-new-business-model" class="anchor"></a>
<a id="corporate-sponsorships" href="#corporate-sponsorships" class="anchor"></a>
<a id="that-s-it" href="#that-s-it" class="anchor"></a>
Face cams: the missing guidehttps://fasterthanli.me/articles/face-cams-the-missing-guide2024-03-02T18:00:00Z<p data-bo="129">I try to avoid doing “meta” / “behind the scenes” stuff, because I usually feel
like it has to be “earned”. How many YouTube channels are channels about making
YouTube videos? Too many.</p>
<p data-bo="316">Regardless, because I’ve had the opportunity to make my own mistakes now for a
few years (I started <a href="https://www.youtube.com/@fasterthanlime">doing the video thing</a> in earnest in 2019), and because I’ve recently made a few leaps
in quality-of-life re: shooting and editing video, I thought I’d publish a few
notes, if only for reference for my future self.</p>
<a id="where-to-buy-stuff" href="#where-to-buy-stuff" class="anchor"></a>
<a id="about-the-money-thing" href="#about-the-money-thing" class="anchor"></a>
<a id="what-i-m-going-for" href="#what-i-m-going-for" class="anchor"></a>
<a id="microphones-mic-arms" href="#microphones-mic-arms" class="anchor"></a>
<a id="audio-interfaces-32-bit-floating-point" href="#audio-interfaces-32-bit-floating-point" class="anchor"></a>
<a id="studio-headphones" href="#studio-headphones" class="anchor"></a>
<a id="cameras" href="#cameras" class="anchor"></a>
<a id="hdmi-capture-cards" href="#hdmi-capture-cards" class="anchor"></a>
<a id="lighting-frame-rate-flickering" href="#lighting-frame-rate-flickering" class="anchor"></a>
<a id="recording-software" href="#recording-software" class="anchor"></a>
<a id="editing-software" href="#editing-software" class="anchor"></a>
<a id="subtitling-captioning-software" href="#subtitling-captioning-software" class="anchor"></a>
<a id="keyboards-mice-graphic-tablets-misc" href="#keyboards-mice-graphic-tablets-misc" class="anchor"></a>
<a id="secondary-cameras" href="#secondary-cameras" class="anchor"></a>
<a id="storage" href="#storage" class="anchor"></a>
<a id="script-writing-tools" href="#script-writing-tools" class="anchor"></a>
<a id="teleprompter" href="#teleprompter" class="anchor"></a>
<a id="co2-monitor" href="#co2-monitor" class="anchor"></a>
<a id="full-rig-as-of-march-2024" href="#full-rig-as-of-march-2024" class="anchor"></a>
Just paying Figma $15/month because nothing else fucking workshttps://fasterthanli.me/articles/just-paying-figma-15-dollars2023-10-19T16:50:00Z<p data-bo="168">My family wasn’t poor by any stretch of the imagination, but I was raised to
avoid spending money whenever possible.</p>
<p data-bo="286">I was also taught “it’s a poor craftsman that blames their tools”, which
apparently means “take responsibility for your fuckups”, but, to young-me,
definitely sounded more like “you don’t deserve nice things”.</p>
<p data-bo="497">I was also taught from an early age that I was born a sinner, incapable of doing
good by myself, and that all the earthly things were temptations, sent by the
devil to corrupt me (further I guess?) but also temporary, and that I shouldn’t
attach myself.</p>
<a id="draw-io-the-good" href="#draw-io-the-good" class="anchor"></a>
<a id="draw-io-the-bad" href="#draw-io-the-bad" class="anchor"></a>
<a id="figma" href="#figma" class="anchor"></a>
<a id="the-worst-possible-business-strategy-protestant-ethics" href="#the-worst-possible-business-strategy-protestant-ethics" class="anchor"></a>
<a id="penpot" href="#penpot" class="anchor"></a>
<a id="closing-words" href="#closing-words" class="anchor"></a>
Cracking Electron apps openhttps://fasterthanli.me/articles/cracking-electron-apps-open2023-07-03T16:30:00Z<p data-bo="148">I use the <a href="https://github.com/jgraph/drawio-desktop">draw.io desktop app</a> to
make diagrams for my website. I run it on an actual desktop, like Windows or
macOS, but the asset pipeline that converts <code>.drawio</code> files, to <code>.pdf</code>, to
<code>.svg</code>, and then to <code>.svg</code> again (but smaller) runs on Linux.</p>
<p data-bo="440">So I have a Rust program somewhere that opens headless chromium, and loads just
the HTML/JS/CSS part of draw.io I need to render my diagrams, and then use
Chromium’s “print to PDF” functionality to save a PDF.</p>
<a id="windows-installer" href="#windows-installer" class="anchor"></a>
<a id="windows-no-installer" href="#windows-no-installer" class="anchor"></a>
<a id="macos-universal" href="#macos-universal" class="anchor"></a>
<a id="linux-deb" href="#linux-deb" class="anchor"></a>
<a id="linux-rpm" href="#linux-rpm" class="anchor"></a>
<a id="linux-appimage" href="#linux-appimage" class="anchor"></a>
<a id="linux-snap" href="#linux-snap" class="anchor"></a>
<a id="google-chrome-os" href="#google-chrome-os" class="anchor"></a>
<a id="does-this-work-for-any-electron-app" href="#does-this-work-for-any-electron-app" class="anchor"></a>
<a id="figma" href="#figma" class="anchor"></a>
<a id="discord" href="#discord" class="anchor"></a>
<a id="conclusion" href="#conclusion" class="anchor"></a>