<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog on Ó hAoḋa</title><link>https://ohaodha.ie/blog/</link><description>Ó hAoḋa — Blog RSS Feed</description><generator>Hugo -- gohugo.io</generator><language>en-ie</language><copyright>Copyright © 2024 Aindréas Ó hAodha. Content licensed under CC BY-NC-SA 4.0. Code licensed under GPLv3.</copyright><lastBuildDate>Sat, 15 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://ohaodha.ie/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>How I Listen to Music</title><link>https://ohaodha.ie/blog/how-i-listen-to-music/</link><pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate><guid>https://ohaodha.ie/blog/how-i-listen-to-music/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Several years ago now, I decided to stop using Spotify as my primary means of listening to music, for reasons which I may or may not expand on in a future blog post, but which primarily boil down to three key points:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Privacy concerns:&lt;/strong> while the annual &lt;a href="https://support.spotify.com/td-en/article/spotify-wrapped/">Spotify Wrapped&lt;/a> analysis is a lot of fun, it troubles me that Spotify has this kind of data on the general population as a whole. Listening habits can be used to infer emotional states, religious beliefs, &amp;amp; political affiliations&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, and Spotify can further identify names and geographical locations of their users from their account information.
Although I don&amp;rsquo;t find it particularly concerning that Spotify may have this data on &lt;em>me&lt;/em>, I find it objectionable that a large corporation would have this kind of data on broader society &amp;mdash;&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> and I don&amp;rsquo;t think it&amp;rsquo;s implausible that this sort of data &lt;em>could&lt;/em> be used to manipulate or predict significant societal events like elections.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Audio quality:&lt;/strong> Spotify infamously &lt;del>has&lt;/del> &lt;em>had&lt;/em> limited audio quality, with the highest audio quality setting (up until recently) being 320 kbps&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>, which is quite good, but not audiophile quality.
However, Spotify have recently released lossless support which largely negates this point, although the &lt;a href="https://www.pocket-lint.com/review-spotify-lossless-audio-option/">ever-dissatisfied audiophile community seem to disagree&lt;/a>.
I&amp;rsquo;m not going to claim, however, that I have either the audio equipment or the hearing sensitivity to detect these subtle differences:
I&amp;rsquo;ve &lt;em>very rarely&lt;/em> upon occasion noticed a slight difference between my own lossless-audio files and Spotify&amp;rsquo;s default-quality audio, but the truth of the matter is that I simply do not have audio hardware of sufficient quality to detect these differences.
I build my music library with lossless audio not because I can definitely tell the difference, but in the interest of future-proofing, as I expect (or hope) that I will one day own the hardware necessary to detect these differences.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Access to music not on Spotify&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>:&lt;/strong> not all of the music I want to listen to is on Spotify.
My then-favourite singer &lt;a href="https://www.youtube.com/channel/UCS42Pk262iOKUXUW9XW_SOg">Mars Argo&lt;/a> wasn&amp;rsquo;t on Spotify at the time, and although she since has returned from her extended disappearance, the majority of her discography still isn&amp;rsquo;t on Spotify.
There are a number of other songs and artists to which I would like to listen but are still not on Spotify, and with an increasing number of artists now electing to &lt;a href="https://www.thestreet.com/entertainment/spotify-faces-global-boycott-over-ceos-military-investments">boycott Spotify&lt;/a>, I imagine this issue is more relevant than ever.
Furthermore, I went through a brief phase of editing songs I listened to with the goal of removing parts which I didn&amp;rsquo;t like or extending parts I did like, and this requires you to have that audio file locally&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup> (although I rarely do anything like that these days).&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>For these reasons, as well as a general migration in my life away from proprietary software to FOSS solutions, I decided to build a locally-stored music library consisting of lossless audio files.
I recently lost my library of music files while running reckless &lt;code>rm&lt;/code> commands while sleep-deprived circa 04:00 one morning, which is one of the primary drawbacks of having everything locally, but anyone who&amp;rsquo;s not an absolute fool would be smart enough to have a back-up &lt;del>(except me)&lt;/del>.
So, while I&amp;rsquo;m rebuilding my music collection from scratch, I was inspired to write about my system in detail, as it&amp;rsquo;s grown quite sophisticated over the years.&lt;/p>
&lt;h2 id="how-i-organise-my-music-library">How I Organise My Music Library&lt;/h2>
&lt;p>The majority of music players organise music by the tags within the file metadata, deriving song titles, album titles, and band names in this manner;
however, this is something that I strongly dislike, as it&amp;rsquo;s it&amp;rsquo;s less explicit than a directory-based organisation, requires specialist programs to edit and update properly, and can be very inconsistent, as not all music files one obtains will be tagged properly &amp;mdash; arguably the majority won&amp;rsquo;t be tagged properly.
Therefore, while I make an effort to properly tag my music library for maximum portability, I primarily use a &lt;strong>directory-based organisation&lt;/strong> for my music:
each file is stored under the directory structure of Band Name ☞ Album Name ☞ Track Name.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">~/media/music
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">├── &amp;#39;Addison Rae&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">│ └── &amp;#39;(2025) Addison&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ ├── &amp;#39;01. New York.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ ├── &amp;#39;01. New York.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ ├── &amp;#39;02. Diet Pepsi.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ ├── &amp;#39;02. Diet Pepsi.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">│ ├── &amp;#39;03. Money Is Everything.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ ├── &amp;#39;03. Money Is Everything.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ ├── &amp;#39;04. Aquamarine.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ ├── &amp;#39;04. Aquamarine.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">│ &amp;lt;etc&amp;gt; ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">├── &amp;#39;Alice in Chains&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">│ ├── &amp;#39;(1995) Alice in Chains&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;01. Grind.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;01. Grind.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;02. Brush Away.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;02. Brush Away.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;03. Sludge Factory.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;03. Sludge Factory.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;04. Heaven Beside You.flac&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">│ │ ├── &amp;#39;04. Heaven Beside You.lrc&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">│ │ &amp;lt;etc&amp;gt; ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There are three primary pet peeves which annoy me about other directory structures I&amp;rsquo;ve seen for music libraries:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Not numbering tracks:&lt;/strong> I want the file path to contain all the information about the track, including the artist name, the album name, the track&amp;rsquo;s number within the album, and the track title.
This makes it easy to manage the music library using standard file tools such as &lt;code>ls&lt;/code>, &lt;code>mv&lt;/code>, &lt;code>rm&lt;/code>, etc. and ensures that when a program is run upon the files in a directory, e.g., &lt;code>mpv 'The Velvet Underground'&lt;/code>, the program handles each track in the appropriate order.
Putting the album release year before the album title achieves a similar effect.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>No leading zeros in track numbering:&lt;/strong> if there are more than 9 tracks in a directory, the track numbering &lt;strong>must&lt;/strong> start with a leading zero for tracks 0&amp;ndash;9.
This ensures that the tracks are naturally self-ordering when using programs like &lt;code>ls&lt;/code> or &lt;code>sort&lt;/code>, which order items alphabetically:
failure to include a leading zero will result in item 12 appearing before item 2.
Each track number should have the same number of digits as every other track number to ensure proper sorting.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Not making use of the available character set:&lt;/strong>
you&amp;rsquo;ll often see filenames in which every non-alphanumeric character has been replaced with an underscore &lt;code>_&lt;/code> for no apparent reason.
While I understand removing characters which are especially difficult to handle in filenames, such as single-quotes &lt;code>'&lt;/code>, double-quotes &lt;code>&amp;quot;&lt;/code>, and forward-slashes &lt;code>/&lt;/code> (although single and double-quotes can be used in filenames without issue if escaped properly), and I myself remove these characters from filenames in my library, it seems to me to be a terrible waste to remove characters that are easy to handle in filenames such as parentheses &lt;code>()&lt;/code>.
Furthermore, removed characters are invariably replaced with underscores &lt;code>_&lt;/code> to create horrific, unreadable, and ugly filenames.&lt;/p>
&lt;p>There&amp;rsquo;s a near-superstitious hysteria around non-alphanumeric characters in filenames when the majority of non-alphanumeric ASCII characters are perfectly fine for most modern filesystems.
There are, of course, some characters which you do have to remove for certain filesystems, but these characters are usually few in number and vary from filesystem to filesystem.
The popular choices for desktop UNIX-like operating systems (such as ext4 and btrfs) are very permissive with the characters which they allow, and the only outright forbidden character is &lt;code>/&lt;/code>.
Of course, if you require portability to more arcane filesystems like FAT (for Android) or exFAT (for Windows), you&amp;rsquo;re quite hamstrung with the characters you can use, as they forbid &lt;code>\&lt;/code>, &lt;code>/&lt;/code>, &lt;code>:&lt;/code>, &lt;code>*&lt;/code>, &lt;code>?&lt;/code>, &lt;code>&amp;quot;&lt;/code>, &lt;code>&amp;lt;&lt;/code>, &lt;code>&amp;gt;&lt;/code>, and &lt;code>|&lt;/code>&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup>.
However, these still leave a large characterspace to play with, especially when you consider that any modern filesystem will support Unicode, even if it didn&amp;rsquo;t originally.&lt;/p>
&lt;p>The only characters I recommend removing from filenames for most Linux-based desktop operating systems are the aforementioned single-quotes &lt;code>'&lt;/code>, double-quotes &lt;code>&amp;quot;&lt;/code>, and forward-slashes &lt;code>/&lt;/code>;
however, I do not recommend just deleting them or replacing them with underscores &lt;code>_&lt;/code>, but replacing them with the following more appropriate characters which are far less offensive to the eye:&lt;/p>
&lt;ul>
&lt;li>Replace &lt;strong>&lt;code>'&lt;/code>&lt;/strong> (&lt;code>U+0027 APOSTROPHE&lt;/code>) with &lt;strong>&lt;code>’&lt;/code>&lt;/strong> (&lt;code>U+2019 RIGHT SINGLE QUOTATION MARK&lt;/code>).&lt;/li>
&lt;li>Replace &lt;strong>&lt;code>&amp;quot;&lt;/code>&lt;/strong> (&lt;code>U+0022 QUOTATION MARK&lt;/code>) with &lt;strong>&lt;code>“&lt;/code>&lt;/strong> (&lt;code>U+201C LEFT DOUBLE QUOTATION MARK&lt;/code>) and &lt;strong>&lt;code>”&lt;/code>&lt;/strong> (&lt;code>U+201D RIGHT DOUBLE QUOTATION MARK&lt;/code>).&lt;/li>
&lt;li>Replace &lt;strong>&lt;code>/&lt;/code>&lt;/strong> (&lt;code>U+002F SOLIDUS&lt;/code>) with &lt;strong>&lt;code>⧸&lt;/code>&lt;/strong> (&lt;code>U+29F8 BIG SOLIDUS&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>If, however, you want to ensure the portability of your music library to different operating systems &amp;amp; filesystems, you&amp;rsquo;ll have to consider the restrictions of those filesystems as well; if you want completely generalisable, filesystem-agnostic filename normalisation, then I can concede that perhaps &lt;em>in that particular situation for compatibility with &lt;strong>all&lt;/strong> filesystems&lt;/em> that a general replace of special characters with underscores is justifiable.
For my uses, I also want my music library to be synchronised to my Android smartphone:
since Android uses the FAT32 filesystem, which is rather archaic in the characterset it supports, I am forced to be more aggressive in my substitution of characters that I would otherwise like to be.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Regardless of my directory-based approach instead of a tags-based approach, I try to ensure that my music is properly tagged in an effort to ensure portability across devices and for future-proofing.
I do this using &lt;a href="https://docs.beets.io/en/latest/index.html">&lt;strong>beets&lt;/strong>&lt;/a>, a music organiser which you can use to import a collection of music files, which it will tag using sources including the &lt;a href="https://musicbrainz.org/">MusicBrainz&lt;/a> database, rename as appropriate in keeping with your naming conventions, and run plug-ins or user-specified hooks on the imported files.
My beets configuration is pretty standard, with it naming and organising files in accordance with the approach I&amp;rsquo;ve outlined above, embedding the album cover art, updating the tags, and so on;
the only non-standard part of my configuration is the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">72&lt;/span>&lt;span class="cl">&lt;span class="nt">hook&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">73&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hooks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">74&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">event&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">album_imported&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">75&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;karaoke.pl &amp;#34;{album.path}&amp;#34;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This hook runs once an album has been imported and executes a script I wrote called &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/karaoke.pl">&lt;strong>&lt;code>karaoke.pl&lt;/code>&lt;/strong>&lt;/a> to
fetch lyrics for music files from the &lt;a href="https://lrclib.net/docs">LRCLIB API&lt;/a> and save them to &lt;code>*.lrc&lt;/code> files.
Surprisingly, I couldn&amp;rsquo;t find a good solution for downloading the synchronised lyrics for a song programmatically, so I had to write my own script to do so.&lt;/p>
&lt;p>Beyond what I&amp;rsquo;ve mentioned already, I find the following two command-line utilities indispensable for making files in a directory match a naming convention:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://man.archlinux.org/man/vidir.1.en">&lt;strong>&lt;code>vidir&lt;/code>&lt;/strong>&lt;/a>: allows you to edit a directory like a normal body of text in a Vi-buffer, the most useful feature of which I find to be using visual block mode to edit, add, or delete a column of characters in a list of filenames.&lt;/li>
&lt;li>&lt;a href="">&lt;strong>&lt;code>perl-rename&lt;/code>&lt;/strong>&lt;/a>: allows you to rename a number of files using regular expressions, without doing any overcomplicated nonsense like &lt;code>echo&lt;/code>ing filenames into &lt;code>sed&lt;/code> and interpolating the output into a &lt;code>mv&lt;/code> command.&lt;/li>
&lt;/ul>
&lt;h2 id="music-player">Music Player&lt;/h2>
&lt;p>I use &lt;a href="https://mpv.io/">&lt;strong>&lt;code>mpv&lt;/code>&lt;/strong>&lt;/a> for playing both video and audio;
it has nice features like the ability to play all the files in a directory in alphabetical order (which is why it&amp;rsquo;s good to number your tracks properly) with a command like &lt;code>mpv &amp;quot;King Crimson/(1969) In The Court Of The Crimson King&amp;quot;&lt;/code>, and it automatically detects &lt;code>*.lrc&lt;/code> files and displays the synchronised lyrics at the appropriate time on-screen.
I use the &lt;a href="https://github.com/mpv-player/mpv/blob/master/TOOLS/umpv">&lt;strong>&lt;code>umpv&lt;/code>&lt;/strong>&lt;/a> script to achieve an enqueueing effect with mpv:
by default, running &lt;code>mpv file1.mp3; mpv file2.mp3&lt;/code> will spawn two separate mpv windows, one playing each file;
running &lt;code>umpv file1.mp3; umpv file2.mp3&lt;/code> will append &lt;code>file2.mp3&lt;/code> to the original umpv window&amp;rsquo;s queue.&lt;/p>
&lt;figure>
 &lt;a href="./images/mpv_lyrics.png">
 &lt;img src="./images/mpv_lyrics.png" alt="Screenshot of mpv displaying lyrics">
 &lt;/a>&lt;figcaption>&lt;code>mpv&lt;/code> displaying lyrics&lt;/figcaption>&lt;/figure>&lt;p>I use the &lt;a href="https://github.com/gokcehan/lf">&lt;strong>&lt;code>lf&lt;/code>&lt;/strong>&lt;/a> terminal file manager to manage and browse all my files, including music files.
Previews for each file are generated using a script I wrote called &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/file_previewer.sh">&lt;strong>&lt;code>file_previewer.sh&lt;/code>&lt;/strong>&lt;/a>:
if the album cover is available, it&amp;rsquo;s displayed in the terminal using &lt;a href="https://hpjansson.org/chafa/">&lt;code>chafa&lt;/code>&lt;/a>&lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup> &amp;mdash; otherwise, a waveform image is generated using &lt;a href="https://ffmpeg.org/">&lt;code>ffmpeg&lt;/code>&lt;/a> and also displayed with &lt;code>chafa&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl"> audio/*&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl"> &lt;span class="c1"># attempt to preview the audio file&amp;#39;s embedded image&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl"> &lt;span class="c1"># if no embedded image, attempt to preview a file named &amp;#39;cover.*&amp;#39; in the same directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl"> &lt;span class="c1"># else, generate a waveform image and display it&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> exiftool -Picture -b &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$file&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> chafa --size &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$((&lt;/span>&lt;span class="nv">$width&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="k">))&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>x&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$height&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">||&lt;/span> chafa cover.* --size &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$((&lt;/span>&lt;span class="nv">$width&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="k">))&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>x&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$height&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">||&lt;/span> ffmpeg -i &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$file&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> -filter_complex &lt;span class="s2">&amp;#34;showwavespic=s=1280x720:colors=pink&amp;#34;&lt;/span> -frames:v &lt;span class="m">1&lt;/span> -f image2pipe -vcodec png - &lt;span class="p">|&lt;/span> chafa --size &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$((&lt;/span>&lt;span class="nv">$width&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="k">))&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>x&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$height&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl"> exiftool &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$file&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> bat --theme&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;base16&amp;#39;&lt;/span> --terminal-width &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$((&lt;/span>&lt;span class="nv">$width&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="k">))&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> --force-colorization&lt;span class="p">;;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;figure>
 &lt;a href="./images/lf_album_art.png">
 &lt;img src="./images/lf_album_art.png" alt="Screenshot of lf displaying the cover image for an album">
 &lt;/a>&lt;figcaption>&lt;code>lf&lt;/code> with album art preview&lt;/figcaption>&lt;/figure>&lt;figure>
 &lt;a href="./images/lf_waveform.png">
 &lt;img src="./images/lf_waveform.png" alt="Screenshot of lf displaying the audio waveform in the absence of a cover image">
 &lt;/a>&lt;figcaption>&lt;code>lf&lt;/code> with audio waveform preview in the absence of cover image&lt;/figcaption>&lt;/figure>&lt;p>I&amp;rsquo;ve also written a number of scripts to play music in different ways over the years, although I only really use the last script listed here now:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>&lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/music_dmenu.sh">&lt;code>music_dmenu.sh&lt;/code>&lt;/a>:&lt;/strong> creates a hierarchical &lt;a href="https://tools.suckless.org/dmenu/">&lt;code>dmenu&lt;/code>&lt;/a> prompt based on the files in your music directory which allows you to play a specific artist, album, or track.
Cumbersome to use, as playing a particular track requires you to select &lt;code>Track&lt;/code> in the first prompt, then the artist, then the album, and then the track itself &amp;mdash; probably only really useful for playing a particular artist as you would just select &lt;code>Artist&lt;/code> and then the artist&amp;rsquo;s name.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/play_music.sh">&lt;code>play_music.sh&lt;/code>&lt;/a>:&lt;/strong> plays an album based off a given artist name and album name.
Usage: &lt;code>play_music.sh &amp;quot;Fontaines D.C.&amp;quot; &amp;quot;(2022) Skinty Fia&amp;quot;&lt;/code>.
This worked well before I started including release years in the filepath, as while it&amp;rsquo;s not especially unreasonable to require the user to know the precise artist name and album name, it&amp;rsquo;s a bit much to expect them to know the album&amp;rsquo;s release year as well, and so I don&amp;rsquo;t really use it anymore.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/fzfplay.sh">&lt;code>fzfplay.sh&lt;/code>&lt;/a>:&lt;/strong> uses &lt;a href="https://github.com/sharkdp/fd">&lt;code>fd&lt;/code>&lt;/a> (a faster&lt;sup id="fnref:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup> alternative to the POSIX &lt;a href="https://www.man7.org/linux/man-pages/man1/find.1.html">&lt;code>find&lt;/code>&lt;/a>) to list all the tracks in the music directory&lt;sup id="fnref:9">&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref">9&lt;/a>&lt;/sup>, and allows the user to search them with &lt;a href="https://github.com/junegunn/fzf">&lt;code>fzf&lt;/code>&lt;/a> (a command-line fuzzy finder).
For me, this is not only far superior to the other two scripts mentioned here, it&amp;rsquo;s superior to any other method of enqueueing music I&amp;rsquo;ve encountered in any other application &amp;mdash; its speed and convenience are unparalleled.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># Script that allows a music file or directory to be selected via `fzf` to be enqueued with `umpv`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nv">media_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/media/music&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">fd --base-directory &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$media_directory&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --exclude &lt;span class="s2">&amp;#34;*.jpg&amp;#34;&lt;/span> --exclude &lt;span class="s2">&amp;#34;*.png&amp;#34;&lt;/span> --exclude &lt;span class="s2">&amp;#34;*.lrc&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --absolute-path &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> fzf &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> xargs -I&lt;span class="o">{}&lt;/span> sh -c &lt;span class="s1">&amp;#39;umpv &amp;#34;{}&amp;#34; &amp;amp;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I bound this script to a hotkey using &lt;a href="https://github.com/baskerville/sxhkd">&lt;code>sxhkd&lt;/code>&lt;/a> (my preferred hotkey manager for X), and use this to play my music 90% of the time &amp;mdash; the remaining 10% being via &lt;code>lf&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="ln">219&lt;/span>&lt;span class="cl">super + control + shift + m
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">220&lt;/span>&lt;span class="cl"> alacritty --class &amp;#34;fzfplay.sh&amp;#34; --command sh -c &amp;#34;nohup fzfplay.sh &amp;gt; /dev/null&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;figure>
 &lt;a href="./images/fzfplay.png">
 &lt;img src="./images/fzfplay.png" alt="Screenshot of fzfplay.sh">
 &lt;/a>&lt;figcaption>&lt;code>fzfplay.sh&lt;/code>&lt;/figcaption>&lt;/figure>&lt;p>I have the idea in my head to (probably at some point in the near future) update this script to allow the user to search by lyrics as well, not unlike the search bar on Spotify.
I&amp;rsquo;m unsure as of yet whether I would want this functionality united in the same script for convenience, or if I would prefer to have it isolated to its own &amp;ldquo;search-by-lyrics&amp;rdquo; script:
at present, I&amp;rsquo;m leaning towards the latter, as it would be both simpler and more UNIXy, but also because it would circumvent the issue of weighting lyrics versus titles in search.
What I mean by this is that a unified search interface would have decide whether to weight a matching title or lyric more heavily (i.e., treat it as more relevant), which would turn this program from a handy little script to an open research question in the field of Information Retrieval.&lt;/p>
&lt;ul>
&lt;li>The most likely approach would be to weight the title more heavily: e.g., if a user were to search &amp;ldquo;i love you&amp;rdquo;, the song &lt;em>I Love You&lt;/em> by Fontaines D.C. would be more likely to be a relevant result than any of the other thousands of songs that contain that string.&lt;/li>
&lt;li>You could probably take it as an axiom that given that song titles are forced to be quite short and are often chosen with some view towards uniqueness or recognisability, whereas song lyrics have no such length constraints and have far less of a focus on uniqueness (though, of course, still have some focus on uniqueness), that the likelihood of a any given sub-string occurring in a song title is far lower than the likelihood of it occurring in a song&amp;rsquo;s lyrics, and that therefore its presence in a song&amp;rsquo;s title is more meaningful for providing relevant search results.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="sharing-music-between-devices">Sharing Music Between Devices&lt;/h2>
&lt;p>I use &lt;a href="https://syncthing.net/">Syncthing&lt;/a> for sharing music between my laptop and my phone;
on my phone, I use the Android Syncthing client &lt;a href="https://github.com/researchxxl/syncthing-android">Syncthing-Fork&lt;/a> (set to open directly to the web view window, as I find that the most intuitive interface).
Because my music collection consists of FLAC files, it&amp;rsquo;s rather large and therefore takes up a lot of storage space:
it was for this reason that one of my primary &amp;ldquo;must-haves&amp;rdquo; for my most recent smartphone purchase was 256Gb of storage.
In the future, I may look into compressing my music before syncing it to other devices, and using the FLAC files primarily for archival purposes, or on my home computer where I can attach additional storage without having to worry about transporting it.
For the time being, my music collection does still fit on my phone, so I can worry about that another day.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Besides the following research papers which substantiate this claim, this is intuitively self-evident: someone who listens to the Dead Kennedys is likely to have anarcho-lefist views, a Tom MacDonald listener is likely to have conservative views, a Wolfe Tones listener is likely to have Irish republican views &amp;mdash;&lt;sup id="fnref1:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> and so on, and doubly so for inferring one&amp;rsquo;s religion if they tend to listen to Orthodox chants or Islamic &lt;em>nasheeds&lt;/em>.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>I swear that this is an organic em-dash, not a GPT-generated one&lt;sup id="fnref:10">&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref">10&lt;/a>&lt;/sup>.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>&lt;a href="https://support.spotify.com/us/article/audio-quality/">https://support.spotify.com/us/article/audio-quality/&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>Spotify does give you a way to address its lack of certain tracks with its &lt;a href="https://support.spotify.com/us/article/local-files/">Local Files feature&lt;/a> which allows you to not only listen to files you have stored locally on your devices, but to add them to playlists and to download these playlists onto other devices under the same account, where the tracks will be playable even if you never manually copied the local files to that device.
I made extensive use of this feature when I primarily used Spotify.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>The most notable example of me doing this was removing the repeated lines &lt;em>When Richard one calm summer night / put a bullet through his head&lt;/em> before a breakdown from the &lt;a href="https://www.youtube.com/watch?v=yO3VhLYSWgE">Tiny Little Houses song&lt;/a> based upon the Edwin Arlington Robinson poem &lt;a href="https://www.poetryfoundation.org/poems/44982/richard-cory">&lt;em>Richard Cory&lt;/em>&lt;/a>, the repetition of which I felt destroyed much of the impact of the original poem, as in the original poem the final line very much strikes the reader out of nowhere and then ends abruptly in silence.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>&lt;a href="https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file">https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file&lt;/a>&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>I will most likely stop using &lt;code>chafa&lt;/code> in favour of switching to a terminal emulator with graphics support; at present, I use &lt;a href="https://alacritty.org/">Alacritty&lt;/a> for its simplicity, but I would probably be better of using a terminal emulator with graphics/Sixel support such as &lt;a href="https://sw.kovidgoyal.net/kitty/">kitty&lt;/a>.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8">
&lt;p>&lt;a href="https://github.com/sharkdp/fd#benchmark">https://github.com/sharkdp/fd#benchmark&lt;/a>&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:9">
&lt;p>My implementation actually just finds all the files that are not JPEGs, PNGs, or LRC files, which is fine in the case of my music directory, as I know it won&amp;rsquo;t contain any other filetypes. An alternative approach would be to search specifically for MP3 files, FLAC files, etc., but this approach would exclude any tracks in a non-specified file format: I&amp;rsquo;ve prioritised track filetype flexibility over guaranteed exclusion of non-music files.
A more robust approach would be to check the MIME type of each file and to only use the ones which are of the &lt;strong>type&lt;/strong> &lt;code>audio&lt;/code>, but I consider this to be prohibitively computationally expensive for my use-case, as it would require actual reading of the contents of each file rather than just filename-based filtering.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:10">
&lt;p>Have you ever seen someone put a footnote on a punctuation mark like that before? I haven&amp;rsquo;t. Would be even weirder if someone put yet another footnote within that footnote&amp;hellip;&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>The Books I Read in March, April, May, &amp; June 2026</title><link>https://ohaodha.ie/blog/the-books-i-read-in-march-april-may-and-june-2026/</link><pubDate>Sat, 18 Jul 2026 12:30:03 +0100</pubDate><guid>https://ohaodha.ie/blog/the-books-i-read-in-march-april-may-and-june-2026/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>I didn&amp;rsquo;t get as much reading done in March as I had hoped, as I was laid low with sickness twice:
the first time, with a case of strep throat that the doctor described rather bluntly as &amp;ldquo;very bad&amp;rdquo; &amp;mdash; not exactly hope-inspiring words to hear from a medical professional &amp;mdash;
and I was prescribed a week of antibiotics;
on the final day of this course of antibiotics, I made the delightful discovery that I had developed an allergy to penicillin and swelled up like an obese lobster, and was forced to traipse back into the doctor&amp;rsquo;s office once more.
I tend not to read much when I&amp;rsquo;m unwell, and so I didn&amp;rsquo;t do a whole lot of reading for two-ish weeks of the month.
However, I don&amp;rsquo;t want to give the impression that this somehow means that the reading throughput of the month was somehow unusually low:
I have it on good authority that life is going to happen to me every month of my life, whether I like it or not!&lt;/p>
&lt;p>Then, between one thing and another I didn&amp;rsquo;t find the time to write this blog post and it languished half-finished in a Git worktree on my laptop, which I then broke, and then sort-of repaired, and now here we are in July.
During this time, I reflected on what I was finding so difficult about writing these blog posts and why they were taking me so long, and I realised that it came down to two things primarily:&lt;/p>
&lt;ol>
&lt;li>I, despite my better sense, felt some acute sense of pressure to have a meaty list of books to list in my blog post &amp;mdash; essentially prioritising quantity over quality, and seeking to impress some imaginary person who certainly would not care even if they did exist. Time has more-or-less cured me of this: I just couldn&amp;rsquo;t be bothered to care any more after letting this sit for so long.&lt;/li>
&lt;li>I think I was frankly putting too much effort into my little reviews: short though they may be, I genuinely did put quite a bit of effort into writing these overviews, trying my best to be fair, analytical, and interesting.
Going forward I shall make more of an effort to be pithy and opinionated.
Take for example, the entry on the &lt;em>Short Stories of Pádraic Pearse&lt;/em> below: in my original draft for this post, I intended to write an analysis of each of these short stories in Irish, and then translate it into English as well, while providing accurate and thought-provoking analysis in both languages &amp;mdash; who has the time for that sort of thing?&lt;/li>
&lt;/ol>
&lt;h2 id="the-idiot----fyodor-dostoevsky">&lt;em>The Idiot&lt;/em> &amp;ndash; Fyodor Dostoevsky&lt;/h2>
&lt;p>A heartbreaking tale of a man Christ-like in his simplicity, in an exploitative and false world with which he is incompatible.
The main character, Prince Myshkin, has a saintliness that could be mistaken for idiocy, or a child-like naïvety:
I am reminded how Christ is often portrayed as a vulnerable and innocent child or lamb, and draw a parallel to my discussions of Pearse&amp;rsquo;s short story &lt;span class="gaelic">Íosagán&lt;/span>
 below.
The book illustrates how the Russia of the day could not facilitate the existence of a truly Christ-like man, and one is reminded that of course, neither could Roman Galilee.&lt;/p>
&lt;h2 id="rashōmon----akutagawa-ryūnosuke">&lt;em>Rashōmon&lt;/em> &amp;ndash; Akutagawa Ryūnosuke&lt;/h2>
&lt;p>Extremely enjoyable, with some of the most vivid imagery and scene-setting in anything that I&amp;rsquo;ve read before, along with some excellent psychological characterisation.
&lt;em>Rashōmon&lt;/em> itself is particularly vivid and haunting.
&lt;em>Horse Legs&lt;/em>, in which a man is returned from the dead but with the legs of a horse in place of his original legs, reminded me strongly of the Irish tale of &lt;span class="gaelic">&lt;/span>
 in which a king goes to great lengths to prevent anyone finding out that he has donkey ears &amp;mdash; &lt;span class="gaelic">tá ḋá ċluis asail ar Laḃraiḋ Loingseaċ!&lt;/span>
.
&lt;em>Hell Screen&lt;/em> is similarly haunting and compelling, and contains the extremely relatable quote:&lt;/p>
&lt;blockquote>
&lt;p>Born stupid, I can never understand anything that isn&amp;rsquo;t perfectly obvious, and so I had no idea what to say to her.&lt;/p>
&lt;/blockquote>
&lt;p>I highly recommend this collection of short stories and will be keeping an eye out for more of Akutagawa&amp;rsquo;s works in the future.&lt;/p>
&lt;h2 id="thirst-for-love----mishima-yukio">&lt;em>Thirst for Love&lt;/em> &amp;ndash; Mishima Yukio&lt;/h2>
&lt;p>Like all of Mishima&amp;rsquo;s novels, deeply disturbing but nonetheless psychologically insightful.
The evolution of Etsuko&amp;rsquo;s mind from numb loneliness, to mounting obsession, to fatal cruelty inspires a sense of impending horror in the reader.
Her spiralling descent into increasingly irrational and erratic behaviour reminds me of the plight of Raskolnikov in &lt;em>Crime and Punishment&lt;/em>, Itakura Shuri in Akutagawa&amp;rsquo;s &lt;em>Loyalty&lt;/em>, and even, in a way, Esther in &lt;em>The Bell Jar&lt;/em>.
I found the following quote from the text particularly pertinent:&lt;/p>
&lt;blockquote>
&lt;p>Rumour sometimes follows a more precise logic than fact, and fact more than rumour is apt to have a lie in it somewhere.&lt;/p>
&lt;/blockquote>
&lt;h2 id="crime-and-punishment----fyodor-dostoevsky">&lt;em>Crime and Punishment&lt;/em> &amp;ndash; Fyodor Dostoevsky&lt;/h2>
&lt;p>I don&amp;rsquo;t think there&amp;rsquo;s any commentary I can add on this book that hasn&amp;rsquo;t been said already: a deeply religious masterpiece.
One detail that particularly struck me is how Raskolnikov&amp;rsquo;s guilt is cemented by the murder of Lizaveta.
He could have half-justified, in a brutal and utilitarian way, the murder of the old woman alone, but this crime against nature forces him into a position where he is no longer able to simply opt-in or opt-out of immorality:
the arrival of Lizaveta forces him to choose between being caught or just another murder &amp;mdash; because how different is 2 to 1 really?
However, Lizaveta was more than innocent, and no argument could be submitted to justify her murder in good faith.&lt;/p>
&lt;h2 id="at-swim-two-birds----flann-obrien">&lt;em>At Swim-Two-Birds&lt;/em> &amp;ndash; Flann O&amp;rsquo;Brien&lt;/h2>
&lt;p>Like all of Flann O&amp;rsquo;Brien&amp;rsquo;s works, a masterpiece.
Incredibly interesting, a unique and masterful layered approach to storytelling, and very funny.
The characterisation of Finn Mac Cool&amp;rsquo;s lengthy and boring explanations of personal histories will be very funny and relatable to anyone who&amp;rsquo;s read any medieval Irish texts (e.g., &lt;span class="gaelic">Agallaṁ na Seanóraċ&lt;/span>
), with the stories often being interrupted with some lengthy genealogy to explain some minor detail of a place name: obviously important from a histiographical perspective, but not always an enjoyable diversion from the story.
A must-read of Irish literature.&lt;/p>
&lt;p>An idea that occurred to me while reading this, which I&amp;rsquo;m sure has both occurred to many others before me (but I&amp;rsquo;ve never seen) and which I&amp;rsquo;m not at all qualified to properly commentate, is that the likes of James Joyce and Brian Ó Nualláin (the legal name of Flann O&amp;rsquo;Brien), far from being progenitors of a new form of multi-layered and complex writing (often associated with literary modernism), are following in a broader tradition of Irish writing with multiple layers of meaning.
Perhaps I&amp;rsquo;m making two kinds of stretches in this argument: first, that the multi-layered storytelling approach so associated with Joyce and his ilk is really comparable or indeed subsequent to the multi-layered symbolism of medieval writing, and secondly that this multi-layered symbolism can really be called uniquely Irish.
However, as an example of what I mean, I would direct the reader to the excellent lecture delivered by Dr. Jesse Patrick Harrington in the Dublin Institute for Advanced Studies lecture Series &lt;em>Samhain agus Science&lt;/em> &lt;a href="https://www.youtube.com/watch?v=U-BsdRl5R0g">&lt;em>Christian Curses in Medieval Ireland&lt;/em>&lt;/a>, in which (among a great many other interesting things), he discusses how St. Patrick was said to have cursed a British king named Coroticus with the words &amp;ldquo;Lord, if it possible, expel this godless man from this world and from the next&amp;rdquo;, and subsequently Coroticus was transformed into a fox and was never seen again.&lt;/p>
&lt;p>Dr. Harrington goes on to describe how in early Irish law, the word &lt;span class="gaelic">sionnaċ&lt;/span>
&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> or &lt;em>fox&lt;/em> was also used to refer to an &lt;em>exile&lt;/em>, and the excommunication from the Church is an &lt;em>exile&lt;/em> from the Christian community.
He goes on to explain further symbolism from the story, but to save from me (poorly) summarising his lecture here, I will suffice with a quotation:&lt;/p>
&lt;blockquote>
&lt;p>Medieval authors intended their stories to be read on multiple levels.
Some audiences may have taken the story literally, as if Coroticus was actually transformed into a fox;
the more educated, those familiar with the law, may have taken it figuratively, referring to his exile;
while the clergy, for their part, may have taken it morally, referring to his spiritual state.
All of these meanings are inherent in the text, and any one of them could have been emphasises to underscore Patrick&amp;rsquo;s spiritual authority, alongside the seriousness of Corticus&amp;rsquo; offence.&lt;/p>
&lt;/blockquote>
&lt;p>It may be interpreted here that I&amp;rsquo;m foolishly claiming that symbolism is a uniquely Irish invention, but that&amp;rsquo;s not what I mean;
what I mean is that the complex and unexplained symbolism and themes in something like Joyce&amp;rsquo;s &lt;em>Ulysses&lt;/em> is, far from being unique to Joyce, something that can be found in Irish literature going back centuries.
Brian Ó Nualláin for his part, having studied early Irish literature (evidenced by the inclusion of Finn Mac Cool and Mad King Sweeney in &lt;em>At Swim-Two-Birds&lt;/em>, but also having studied it formally at university, producing his M.A thesis on &lt;em>Nature in Irish Poetry&lt;/em>&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>) would have certainly been aware of this tradition, and therefore, I argue, seen his work as a continuation of this tradition, not as an invention of his or Joyce&amp;rsquo;s.&lt;/p>
&lt;h2 id="short-stories-of-pádraic-pearse----desmond-maguire">&lt;em>Short Stories of Pádraic Pearse&lt;/em> &amp;ndash; Desmond Maguire&lt;/h2>
&lt;p>This collection of short stories by Pádraic Pearse was a bilingual edition, with English text on the left page and the original Irish on the right.
I&amp;rsquo;ve been intending for a while now to increase my reading in both Irish and French, having held myself back for some time on the basis that my skill in whichever language wasn&amp;rsquo;t &amp;ldquo;at the level&amp;rdquo; for that sort of activity &amp;mdash; as if it would ever improve to that level without throwing myself into the deep end and trying to read in that language (my occasional perusal of the RSS feeds for &lt;em>RTÉ Nuacht&lt;/em>, &lt;em>Tuairisc&lt;/em>, and &lt;em>Le Monde&lt;/em> notwithstanding).
This edition being bilingual was a boon in that regard:
whenever I didn&amp;rsquo;t understand a word or phrase, I could glance to the left and read its translation, before returning to the text.&lt;/p>
&lt;p>I hummed and hawed at length about the optimal way to go about reading a bilingual text (making, as I am wont to do, perfection the enemy of good), and considered two potential lines of attack before settling on a third:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Read the English translation through in its entirety, and then read the original:&lt;/strong>
I&amp;rsquo;ve seen people recommending this approach online, and while it certainly sounds sensible, I knew that I simply could not be bothered to read the same piece of text twice in a row.&lt;/li>
&lt;li>&lt;strong>Reading the original text, and looking up any unfamiliar words or phrases, and adding them to deck of flashcards:&lt;/strong>
I think this sort of thing is called &amp;ldquo;mining&amp;rdquo; in language-learning communities (at least with respect to the acquisition of kanji in Japanese studies).
I made a good attempt at this in the beginning, but it was incredibly distracting and time-consuming&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>, and for me destroyed much of the enjoyment of actually reading the text.&lt;/li>
&lt;li>&lt;strong>Just ploughing through the original text, glancing at the translation where needs be:&lt;/strong>
ultimately, this approach was the only serious contender because it was the only approach in which &lt;strong>I actually read the original text in a timely manner&lt;/strong>, and that didn&amp;rsquo;t turn the enjoyable act of reading into an incredibly arduous chore.
The success of this approach in comparison with the previous two was such that I&amp;rsquo;ll probably continue &amp;ldquo;just ploughing through&amp;rdquo; for any future non-English texts I read, bilingual or not;
however, this remains to be seen.&lt;/li>
&lt;/ol>
&lt;p>Given the approach that was ultimately adopted, I&amp;rsquo;m afraid I can&amp;rsquo;t really comment on the quality of Maguire&amp;rsquo;s translation, as I didn&amp;rsquo;t really read it;
what little I did read however, was generally pretty good, although I wouldn&amp;rsquo;t always have made the same choices.
Sometimes, he chose to simplify a phrase or opted not to translate it literally, which I found inconveniencing when I tried to use his translation as a sort of dictionary, but I don&amp;rsquo;t think he can reasonably be faulted for this.&lt;/p>
&lt;p>Since the collection of short stories here was somewhat eclectic, I&amp;rsquo;ve chosen to list each one which I wish to comment on individually, which additionally neatly separates my comments on each;
the comments I wrote in Irish because I thought it most suitable for Gaelic subject matter, but my English translations of these can be found below its corresponding Gaelic original.&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>&lt;/p>
&lt;span class="gaelic">&lt;h3 id="íosagán">&lt;em>Íosagán&lt;/em>&lt;/h3>
&lt;p>Is scéal an-simplí an gearrscéal seo, aċ is scéal taitneaṁaċ é ar aon nós.
Cuireann sé i gcuiṁne dom an leiṫéid scéalta a d&amp;rsquo;inis an sean-sagart an tAṫair Ó Leaṁáin dúinn agus mé i mo ḋalta bunscoile.
D&amp;rsquo;inis sé réimse scéalta dúinn, agus tar éis a ṁáinliaċt inċinne ṫáinig caraċtar níos gruama orṫu: scéalta faoi bás sna trinsí, urċar gunna a raiḃ féin-déanta, agus leagan don &lt;em>Ofráil an Ḃaintreaċ&lt;/em> le fear alcólaċ a ṫug dȧ ḃuidéal ḃeoraċ mar ofráil don leanḃ Íosa ag Aifrean Óiċe Nollag.
D&amp;rsquo;ḟoġlaim mé a lán óna scéalta sin.&lt;/p>
&lt;p>Is scéal álainn é &lt;em>Íosagán&lt;/em>, agus tá an creideaṁ an Ṗiarsaiġ le feiceáil ann.
Léiríonn sé an bealaċ go Dia seaċas tríd an Eaglais aċ tríd ṡuáilce pearsantaċt, agus ar leiṫ an grá don ṗáistí;
b&amp;rsquo;ḟéidir nach an creideaṁ is Caitliciġ, aċ saintréiṫeaċ den saġas creideaṁ traidisiúnta a raiḃ ag sean-daoine na hÉireann.
Meaḃraíonn sé dúinn Críost a feiceáil i ngach páiste.&lt;/p>
&lt;/span>

&lt;p>&lt;em>This short story is a very simple story, but it&amp;rsquo;s an enjoyable story anyway.
It reminds me of the sort of story that the old priest Father Lavin told us when I was a primary school student.
He told us a range of stories, and after his brain surgery a gloomier character came upon them:
stories about death in the trenches, self-inflicted gunshots, and a version of&lt;/em> The Widow&amp;rsquo;s Offering &lt;em>with an alcoholic man who gave two bottles of beer as an offering to the baby Jesus at Christmas Eve Mass.
I learned a lot from those stories.&lt;/em>&lt;/p>
&lt;p>Íosagán&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup> &lt;em>is a beautiful story, and the faith of Pearse is to be seen in it.
It illustrates the path to God not through the Church but through personal virtue, and in particular love for children;
perhaps not the most Catholic belief, but characteristic of the sort of traditional faith of the old people of Ireland.
It reminds us to see Christ in every child.&lt;/em>&lt;/p>
&lt;span class="gaelic">&lt;h3 id="eoġainín-na-néan">&lt;em>Eoġainín na nÉan&lt;/em>&lt;/h3>
&lt;p>Ní ṫuig mé go raiḃ an scéal seo meafar don eitinn roiṁ a rinne mé plé faoin le mo ṁáṫair ar an guṫán: ċeap mé go raiḃ sé faoi féinṁarú no rud éigin mar sin, ach ba cuiṁin leiṫí é ón scoil agus go raiḃ an croí den scéal ná an frása &amp;ldquo;luisne ina leiceann&amp;rdquo; &amp;mdash; an coṁarṫa saintréiṫeaċ den eitinn, téarnaṁ dealraiṫeaċ roiṁ bás tapa.&lt;/p>&lt;/span>

&lt;p>&lt;em>I didn&amp;rsquo;t understand that this story was a metaphor for tuberculosis before I discussed it with my mother on the phone:
I thought that it was about suicide or something like that, but my mother remembered it from school and that the heart of the story was the phrase &amp;ldquo;glow in the cheek&amp;rdquo; &amp;mdash; the characteristic sign of tuberculosis, apparent recovery before rapid death.&lt;/em>&lt;/p>
&lt;span class="gaelic">&lt;h3 id="an-deargadaol">&lt;em>An Deargadaol&lt;/em>&lt;/h3>
&lt;p>Scéal uafásaċ aċ ana-spéisiúil faoi ḃean ṁallaiṫe i sráidḃaile beag agus na mná caidreamh le daonra an ċeantair sin.
Tá nasc idir an scéal seo agus an díospóireaċt a rinne mé i caibidil &lt;a href="#at-swim-two-birds--flann-obrien">§&lt;em>At Swim-Two-Birds&lt;/em>&lt;/a> ṫuas:
ċuir sagart a ṁallaċt ar an ḃean, agus ón lá sin, ba díbeartaċ í.
Aċ, cuireann an scéal seo ár gcoinċeap den ċeartas i gceist:
níl a ḟios againn cad a ḋearna an ḃean ṁallaiṫe, aċ ċuir an tsagart a ṁallaċt uirṫi agus tagann an ṁallaċt i ḃfeiḋm, agus mar sin dealraíonn sé gur duine olc í an ḃean, mar tá fearg an tsagairt ⁊ Dé uirṫi.
Áfaċ, feicimid naċ duine gan trócaire í: fuasclaíonn sí páiste óg óna ḃá, agus taispeánann sí cineáltas don páiste ina ḋiaḋ sin.
Tuigtear naċ duine gan suáilce í.
Faraor, téann an páiste faoi smaċt an ṁallaċt, agus faiġeann sí bás.
Cá bḟuil an ceartas nuair a ċuirtear pionós ar ṗáiste neaṁċiontaċ ar son gaisce duine eile?&lt;/p>&lt;/span>

&lt;p>&lt;em>Horrible yet very interesting story about a cursed woman in a small village and the interaction of the woman with the population of that area.
There is a link between this story and the discussion I made in the section &lt;a href="#at-swim-two-birds--flann-obrien">§&lt;em>At Swim-Two-Birds&lt;/em>&lt;/a> above:
a priest put his curse on the woman, and from that day, she was a pariah.
But, this story puts our concept of justice in question:
we don&amp;rsquo;t know what the cursed woman did, but the priest put his curse on her and the curse comes into effect, and therefore it appears that the woman is an evil person, because the anger of the priest &amp;amp; God is on her.
However, we see that she isn&amp;rsquo;t a person without mercy:
she delivers a young child from drowning, and she shows kindness to the child after that.
It is understood that she is not a person without virtue.
Alas, the child goes under the dominance of the curse, and she dies.
Where is the justice when a penalty is put on an innocent child for the good deed of another person?&lt;/em>&lt;/p>
&lt;span class="gaelic">&lt;h3 id="an-ḃean-ċaointe">&lt;em>An Ḃean Ċaointe&lt;/em>&lt;/h3>
&lt;p>Scéal coscraċ faoi sean-ḃaintreaċ a ḃíonn ag fanaċt i gconaí go dtiocfaiḋ a mac aḃaile;
áfaċ, níl a mac ag teaċt aḃaile &amp;mdash; bhí sé maraiṫe blianta ó shin ag na Sasanaiġ.
Feicimid náisiúnaċas an Ṗiarsaiġ agus a ḃuile fíréanta go díreaċ san scéal seo.
Mar ṡampla:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;Níor cailleaḋ cor ar biṫ é&amp;rdquo;, arsa m&amp;rsquo;aṫair agus aġaiḋ an-duḃ air. &amp;ldquo;MARAÍOḊ é&amp;rdquo;.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>&amp;ldquo;Cé ṁaraiġ do ṡeanaṫair féin? Cé ḃain an ḟuil ḋearg as guaillí mo ṡeanṁaṫar-sa le laisc? Cé ḋéanfaḋ é aċ na Gaill? Mo ṁallaċt ar &amp;mdash;&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Ban mé an-sult as an slioċt seo:&lt;/p>
&lt;blockquote>
&lt;p>Do ḃí mo ṁáṫair ċoṁ carṫannaċ sin nár ṁaiṫ léi an droċ-ḟocal do ċaiṫeaṁ leis an diaḃal féin.
Creidim go raiḃ trua aici ina croí do Ċáin agus do Iúdas agus Ḋiaramaid na nGall&lt;/p>
&lt;/blockquote>
&lt;p>Is slioċt an-Gaelaċ é an slioċt seo &amp;mdash; na triúr daoine is measa, dar leis an Ṗiarsaiġ: Cáin, Iúdas, ⁊ Diarmait Mac Murċada.
Triúr dúnṁarfóirí ⁊ fealltóirí &amp;mdash; cuirtear é seo i gcomparáid lena triúr peacaiġ is measa dar le Dante: Iúdas, Brutus, ⁊ Cassius.&lt;/p>
&lt;/span>

&lt;p>&lt;em>A heartbreaking story about an old widow who does be always waiting for her son coming home;
however, her son isn&amp;rsquo;t coming home &amp;mdash; he was killed years ago by the English.
We see Pearse&amp;rsquo;s nationalism and his righteous fury directly in this story.
For example:&lt;/em>&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;He didn&amp;rsquo;t die at all&amp;rdquo;, says my father with a black face on him. &amp;ldquo;He was MURDERED&amp;rdquo;.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>&amp;ldquo;Who killed your own grandfather? Who drew the red blood from the shoulders of my own grandmother with a lash? Who would do that but the Foreigners&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup>? My curse on &amp;mdash;&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>I drew great enjoyment from this extract:&lt;/em>&lt;/p>
&lt;blockquote>
&lt;p>My mother was so kind-hearted that she wouldn&amp;rsquo;t like to cast a bad word upon the devil himself.
I believe there was pity with her in her heart for Cain and for Judas and for Diarmaid of the Foreigners&lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup>.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>This extract is a very Irish extract &amp;mdash; the three worst people, according to Pearse: Cain, Judas, and Diarmait Mac Murchada.
Three murderers &amp;amp; traitors &amp;mdash; compare this with the three worst sinners according to Dante: Judas, Brutus, &amp;amp; Cassius.&lt;/em>&lt;/p>
&lt;h2 id="maxims--reflections----johann-wolfgang-von-goethesupsup">&lt;em>Maxims &amp;amp; Reflections&lt;/em> &amp;ndash; Johann Wolfgang von Goethe&lt;sup>†&lt;/sup>&lt;/h2>
&lt;p>I was quite excited to pick up this book on my most recent bookshop visit, as I greatly enjoyed reading &lt;em>Faust&lt;/em> and &lt;em>The Sorrows of Young Werther&lt;/em>;
Goethe, being one of the greatest German geniuses&lt;sup id="fnref:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup> of all time, was sure &amp;mdash; I felt &amp;mdash; to have a fascinating and rich collection of aphorisms.
Sadly, I found this collection to be mind-numbingly boring and abandoned reading it, with no intention to pick it back up (for the foreseeable future at least).
I didn&amp;rsquo;t find any of the contents particularly interesting, just the assorted thoughts of some guy on various subjects, none particularly interesting.&lt;/p>
&lt;p>In Goethe&amp;rsquo;s defence, these maxims were originally spread out across several different volumes and not all were published by him in his lifetime, so he can hardly be blamed for them producing a terribly boring book when piled together.
There are some really quite good quotes in there, but distilled into one volume like this they come across as little more than mildly interesting shower thoughts.
Some of my favourites (before I gave up circa p.54) included:&lt;/p>
&lt;blockquote>
&lt;p>Because he speaks, everyone believes that he can also speak about language.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>What we call good conduct and manners is meant to achieve what could otherwise only be effected by force, or not even by force.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>The miller thinks that no wheat grows except to keep his mill going.&lt;/p>
&lt;/blockquote>
&lt;p>And, for reasons which I&amp;rsquo;m sure are quite obvious, my least favourite was:&lt;/p>
&lt;blockquote>
&lt;p>A collection of anecdotes and maxims is the greatest treasure for a man of the world &amp;mdash; as long as he knows how to weave the former into apposite points of the course of conversation, and to recall the latter on fitting occasions.&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-screwtape-letters----cs-lewis">&lt;em>The Screwtape Letters&lt;/em> &amp;ndash; C.S. Lewis&lt;/h2>
&lt;p>An entertaining and illuminating read, dealing with the banality of evil and instructing one in how to avoid falling into devilish traps by counter-example.
Points out a number of easy traps one might fall into, like how easy it is to infer mal-intent when a household member annoys you, while simultaneously demanding that everything you do to annoy them be taking purely at face value and good-intentioned, and how relationships fail because the couple overlook issues in the honeymoon period and rely on their passion to make them tolerate these issues, and so when the honeymoon period ends they have not developed any of the tools or communication tools to deal with these issues directly.&lt;/p>
&lt;p>Often, I have found it useful in my life when facing a personal problem to imagine what I would do if I were a demonic entity actively sabotaging that aspect of my life.
For example, when going through a period of having difficulty sleeping, I ask myself what I would do if I were actively trying to sabotage my sleep:
I would drink lots of caffeine, go to bed at irregular times, wake up at irregular times, start and end my day by filling my mind with useless yet highly stimulating information from a blue-light-emitting screen, and maintain poor sleep hygiene by lying in bed all day.
I wouldn&amp;rsquo;t eat well, or exercise, and so know I wouldn&amp;rsquo;t be tired at the end of the day.
I would do most of my work at night, and stay inside during the day to maximally disrupt my circadian rhythm.
Then, I ask myself which of those things I am currently doing, and the answer is very often &amp;ldquo;essentially all of them&amp;rdquo;.&lt;/p>
&lt;h2 id="say-nothing----radden-keefe">&lt;em>Say Nothing&lt;/em> &amp;ndash; Radden Keefe&lt;/h2>
&lt;p>A harrowing and detailed account of IRA activity during the Troubles.
Well-researched, with a compelling narrative, and a very good entry-level historical account of the IRA&amp;rsquo;s role in the Troubles.
Would certainly recommend.&lt;/p>
&lt;h2 id="on-everything----hilaire-belloc">&lt;em>On Everything&lt;/em> &amp;ndash; Hilaire Belloc*&lt;/h2>
&lt;p>I picked up an old and faded copy of this book from some corner of my late grandfather&amp;rsquo;s house, which is plastered in bookshelves from head to toe.
My grandfather was evidently a big fan of Belloc, and bought all of his grandchildren Belloc&amp;rsquo;s &lt;em>Cautionary Tales for Children&lt;/em> which contains a wonderful selection of poems, each about a child who was naughty in some way or another and as a result of this naughtiness suffered some horrible death&lt;sup id="fnref:9">&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref">9&lt;/a>&lt;/sup> (the book is more child-friendly than I make it sound, think something on the level of &lt;em>Horrible Histories&lt;/em> or similar);
indeed, to this day, I can recite the tale of &lt;em>Matilda: Who told Lies, and was Burned to Death&lt;/em> by heart.&lt;/p>
&lt;p>This book contains of a series of essays on various subjects, none of which insult the reader by spelling out the meaning of the author directly, but illustrate it through example.
My favourite of these essays was &lt;em>On an Empty House&lt;/em> in which a man who is in the process of buying a home is joined in a viewing by a friend, whose presence in the house lit up the home with a life and spirit, and upon her leaving of the house shortly thereafter, he comes to see the house he was once enthusiastic to purchase as empty, bleak, cold, and desolate, and ultimately ends up aborting the whole thing and never buying a house at all.&lt;/p>
&lt;p>I also greatly enjoyed the essays in which Belloc creates portraits of geographical areas that would inspire anyone to visit: &lt;em>On the Approach to Western England&lt;/em>, &lt;em>The Weald&lt;/em>, and &lt;em>A Crossing of the Hills&lt;/em>, to name my favourites.
Another favourite was &lt;em>The Duel&lt;/em>, in which two students are set to kill one another over an insult that neither the giver nor the receiver really understood, over a matter neither seemed to remember, pressured by their social circumstances and a need to preserve one&amp;rsquo;s honour in the foreign country in which they both were living (France), despite both being English.&lt;/p>
&lt;p>Belloc was a truly fascinating man, and I look forward to reading more of his works in the future.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>For a discussion of the etymology of this word, see Criostóir Ó Loingsigh&amp;rsquo;s video &lt;a href="https://www.youtube.com/watch?v=ouAeZb9KIlU">&lt;em>Etymology of &amp;ldquo;Sionnach&amp;rdquo; (Fox) in Irish (Gaelic) &amp;amp; some Proto--Indo-European &amp;amp; Celtic sound-changes&lt;/em>&lt;/a>.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Of which, however, he later spoke of rather derisively (taken from &lt;em>Cruiskeen Lawn&lt;/em>, 16&lt;sup>th&lt;/sup> May 1951):
&lt;em>An M.A., by Gob? I, too, am an M.A. of the same wretched university and can prove documentarily (by producing the preposterous &amp;ldquo;thesis&amp;rdquo;) that the degree, like the university, is a fake.&lt;/em>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>The process was expedited greatly with the use of a script I (largely) vibe-coded to fetch word definitions from &lt;a href="https://en.wiktionary.org/wiki/Wiktionary:Main_Page">Wiktionary&lt;/a>, present the definition to the user, and create an Anki flashcard for that word.
I might discuss scripts for automating flashcard creation at a later date, but the AI slop script that I was using is certainly not in a state that I would consider fit for general consumption at the time of writing.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>I don&amp;rsquo;t think I&amp;rsquo;ve ever yapped at such length before getting into the actual content of what something I&amp;rsquo;m writing actually pertains to:
I feel like one of those people who write for cookery websites, who are apparently bound by oath to list their entire genealogy back to Adam and every thought they&amp;rsquo;ve ever had before telling you how to make pork belly ramen or something.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>The personal name &lt;em>Íosagán&lt;/em> isn&amp;rsquo;t readily translatable: literally speaking, it&amp;rsquo;s the diminutive form of the Irish for Jesus, so &amp;ldquo;Jesuseen&amp;rdquo; wouldn&amp;rsquo;t be a million miles away.
However, that sounds rather weird and gives the story away &amp;mdash; the story hinging on the premise that there is a child with that name who, to the protagonist&amp;rsquo;s surprise, &lt;em>is&lt;/em> actually the infant Jesus; while the symbolism of the name is a little heavy-handed, it&amp;rsquo;s not quite so heavy-handed as to pretend the name &amp;ldquo;Jesuseen&amp;rdquo; is a feasible name for a child.
Íosagán is a far more normal-sounding name in Irish, I suppose somewhat like how Jesús is a totally normal name in Latin countries:
I can envision now a short film, desperate to appear cool and break the tradition of all Irish short films being totally miserable, set in a favela of Rio, in which a decrepit old atheist witnesses the child Jesús playing soccer with the other children while their parents are at Mass, set to a terrible soundtrack of royalty-free reggaeton (calling dibs on the movie rights now!).
For the non-Irish-speaking reader, imagine the child is named something akin to Jesús: a little heavy-handed in terms of symbolism, but not that unusual a name for a normal child.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>The English.
Here, Pearse engages in a time-honoured Irish literary tradition by emphasising the unnaturalness of the presence of the invading people, and thus the virtue of those who fight against them.
A particular example of this is the Irish medieval text &lt;span class="gaelic">Cogad Gáedel re Gallaib&lt;/span>
 &lt;em>The War of the Gael with the Foreigners&lt;/em>, which describes the war of Brian Boru against the Vikings: despite there being a more specific word for the Norse (&lt;span class="gaelic">Loċlannaċ&lt;/span>
), the word for foreigner is used instead.
I think that this point is often overlooked when analysing the history of the Battle of Clontarf: there seems to be a desire by many historians to view Boru&amp;rsquo;s campaign as purely political and power-seeking, and while that very well may have been his own motives (I am not well-informed enough to comment on that subject), it is undeniable that the Irish of the time viewed themselves as a native, singular people, and the Norse as foreign invaders, despite the Norse in question having lived in Ireland for hundreds of years, and most probably never even having been anywhere in the general vicinity of Scandinavia.
Thus, a key motive for the war is overlooked, and the Irish victory is viewed as Pyrrhic, despite not being viewed as such at the time (in my opinion).
An example of what I&amp;rsquo;m talking about here is the &lt;em>Battle of Clontarf&lt;/em> episode of the BBC Radio 4 podcast &lt;em>In Our Time&lt;/em>, in which the expert guests repeatedly translate the word &lt;span class="gaelic">Gallaib&lt;/span>
 as &amp;ldquo;Vikings&amp;rdquo; instead of the more correct &amp;ldquo;foreigners&amp;rdquo;, and portray Boru as a political figure and his victory as Pyrrhic.
This analysis, in my opinion, fails to account for how Boru motivated the armies of Ireland to unite with him and fight such a formidable force without appealing to their sense of nationalist (? perhaps presumptuous to use such a word for the Irish medieval period) justice and ethnic resentment for the Norse, and why the Irish chroniclers, who were known to happily lambaste and satirise any powerful figure they pleased, portray the battle as a success rather than a tragedy.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>Diarmait Mac Murchada was deposed as King of Leinster by the High King of Ireland, and in an effort to to recover his kingdom, solicited military aid from Strongbow and initiated the Anglo-Norman invasion of Ireland, thus earning the epithet &amp;ldquo;Diarmait of the Foreigners&amp;rdquo; and general hatred.
In fairness to the man, he had been robbed of his kingdom and couldn&amp;rsquo;t have been expected to predict the next 800 years of history when he sought help to recover it.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8">
&lt;p>The alliteration of this phrase is much more satisfying if you read it without subvocalising &amp;mdash; curse the English language and its soft-G/hard-G distinction!
(/ˈɡɹeɪ.tɪst/ /ˈd͡ʒɜː.mən/ /ˈd͡ʒiː.ni.əs/ 😧).
I was tempted to call Goethe &amp;ldquo;the greatest German genius of a &lt;em>generation&lt;/em>&amp;rdquo; to enhance the alliteration, but I thought the disrespect to Goethe to qualify his his genius in that way, after I already couched it enough by prefixing it with &amp;ldquo;German&amp;rdquo; and &amp;ldquo;one of&amp;rdquo;, wasn&amp;rsquo;t worth the pay-off &amp;mdash; especially considering that I just wrote in this blogpost about how bad and boring I thought his maxims were.&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:9">
&lt;p>Upon re-inspection of the contents of this book, I realise not all of the poems were about children dying:
indeed, many of the children in the book just suffer mutilation or trauma, but some actually don&amp;rsquo;t suffer any ill-fate at all.
I think the efficaciousness of Belloc&amp;rsquo;s grim techniques for making the morals stick in the memory of children are greatly vindicated by this, as I only really remembered the tales of &lt;em>Jim: Who ran away from his Nurse, and was eaten by a Lion&lt;/em>, &lt;em>Henry King: Who chewed bits of string, and was early cut off in Dreadful agonies&lt;/em>, and &lt;em>George: Who played with a Dangerous Toy, and suffered a Catastrophe of considerable Dimensions&lt;/em> beyond the fiery demise of Matilda, and have retained the lessons throughout my life to not fun away from my caregivers, not to swallow string, not to play with explosive gasses, and not to cry wolf.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>How I Use Bookmarks (the Physical Kind)</title><link>https://ohaodha.ie/blog/how-i-use-bookmarks-the-physical-kind/</link><pubDate>Wed, 22 Apr 2026 23:12:15 +0000</pubDate><guid>https://ohaodha.ie/blog/how-i-use-bookmarks-the-physical-kind/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>When I created this blog, its purpose was primarily to give me a place to put my thoughts on issues that mattered to me but that were mind-numbingly boring to any other reasonable person, and in keeping with that original spirit, I want to talk today about how I use bookmarks (that is, the physical kind you put in books).
I want to make it clear that I make no pretence of being unaware of just how boring, irrelevant, and inconsequential any reasonable person would find the following &amp;mdash; so &lt;span style="font-variant: small-caps">all hope abandon, ye who enter here&lt;/span>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.
I have very particular views on what makes a good bookmark and how to use them, which can be primarily broken down into the following two headings:&lt;/p>
&lt;h2 id="double-orientability">Double-Orientability&lt;/h2>
&lt;p>The naïve approach to bookmarking with which we&amp;rsquo;re all familiar is just to put any piece of paper on the page of the book to which we want to return;
however, this approach is greatly limiting, as it narrows down the place where you left off in the book to two pages (a &lt;em>&lt;strong>spread&lt;/strong>&lt;/em> in bookbinding terms) &amp;mdash; helpful, sure, but you still have to search through the pages for where you left off unless you left off somewhere obvious, like the start of a new chapter.&lt;/p>
&lt;p>Some bookmarks I would describe as &lt;strong>orientable&lt;/strong> and others as &lt;strong>non-orientable&lt;/strong>:
what I mean by this is some bookmarks have a distinct front and back, whereas others have identical sides &amp;mdash; the former I describe as &lt;strong>orientable&lt;/strong> and the latter as &lt;strong>non-orientable&lt;/strong>.
If your bookmark is orientable, it allows you to narrow down your place in the book to a &lt;em>single page&lt;/em>, thus increasing the accuracy of your bookmark by 50%;
this can be achieved by always placing a particular side of your bookmark (say, the side you identify as the &amp;ldquo;back&amp;rdquo;) touching the page on which you left off.&lt;/p>
&lt;p>Indeed, some bookmarks have not only an identifiable front and back, but a top and bottom as well: these I describe as &lt;strong>doubly-orientable&lt;/strong>, as they have two dimensions in which they can be oriented.
Thus, a doubly-orientable bookmark can not just pinpoint your location in the text to a &lt;strong>spread&lt;/strong> (the two facing pages identified by a non-orientable bookmark) or to an &lt;strong>individual page&lt;/strong> (identified by an orientable bookmark), but to the &lt;strong>top or bottom half&lt;/strong> of a particular page.
Thus, when you return to your book to pick up where you left off, you have to spend next to no time looking for your place, as there are usually only 1&amp;ndash;3 paragraph starts within a single half-page area (or &lt;em>spread-quadrant&lt;/em>) at which you could reasonably have left off.&lt;/p>
&lt;h2 id="double-bookmarking">Double-Bookmarking&lt;/h2>
&lt;p>Often, texts that have a great deal of footnotes (or &lt;em>&lt;strong>endnotes&lt;/strong>&lt;/em> rather) place these notes in one place at the end of the text rather than at the bottom of the page;
I tend to dislike this approach, as although it allows for a neater page and a cleaner reading experience without interruption by footnotes, it makes it more difficult to read the notes and to keep track of them.
In my view, if the publisher thought the endnote was worth the trouble of putting in the book, it&amp;rsquo;s worth reading, and since I&amp;rsquo;ll be reading it either way, I would prefer it to be in the most convenient location, i.e., the page where it&amp;rsquo;s relevant.
My view is that &lt;em>footnotes&lt;/em> should be used for essential or near-essential notes on the text (explanations, corrections, caveats, etc.) and that &lt;em>endnotes&lt;/em> should be used for incidental, non-essential further notes on the text (references, citations, etc.) that you don&amp;rsquo;t expect the reader to read the entirety of.&lt;/p>
&lt;p>Anyway, this is a long-winded lead-in to my suggestion that, in the situation that some accursèd publisher has done you the disservice of placing all the notes of the text in a single endnotes section, you can make use of two bookmarks to keep track of your progress through both the main body of text and through the notes;
otherwise, you spend an inordinate amount of time flipping to the back of the book and searching for the note&amp;rsquo;s number, only to repeat the search again in a few line&amp;rsquo;s time.
You can further cut down on the time needed to find where you left off by using the double-orientability of your bookmark to narrow down the location of the footnote for which you&amp;rsquo;re searching to a quarter-page area.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>There&amp;rsquo;s quite the rabbit hole of variants on this quote (inscribed above of the gates of Hell in Dante&amp;rsquo;s &lt;em>Inferno&lt;/em>) which I found myself at the mouth of when I was double-checking the placement of the &lt;span style="font-variant: small-caps">ye&lt;/span> in the quote:
if &lt;a href="https://en.wiktionary.org/wiki/abandon_hope,_all_ye_who_enter_here">Wiktionary&lt;/a> is to be believed, the original Italian &lt;em>lasciate ogne speranza, voi ch&amp;rsquo;intrate&lt;/em> translates literally to &amp;ldquo;all hope abandon ye who enter here&amp;rdquo;, which I&amp;rsquo;ve opted for here;
the variant &amp;ldquo;abandon hope, all ye who enter here&amp;rdquo; is in the iambic pentameter, hence its popularity in poetic translations.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>The Books I Read in January &amp; February 2026</title><link>https://ohaodha.ie/blog/the-books-i-read-in-january--february-2026/</link><pubDate>Sun, 15 Mar 2026 00:00:00 +0000</pubDate><guid>https://ohaodha.ie/blog/the-books-i-read-in-january--february-2026/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Over the past few months, I have been making an effort to regain the rate of reading that I once had as a child, and in the process of doing so I thought that it might be nice to document the books that I complete each month so that I can keep a record of the books that I have read, reflect on my experience reading, and make recommendations about them.&lt;/p>
&lt;p>I try to broaden my horizons where possible when reading, so none of the books listed below are to be taken as recommendations from me about their contents or their authors, unless explicitly stated.
However, in an effort to prevent any serious misinterpretation of my opinions, I have opted to exclude any books that could be considered particularly objectionable from the list below:
I have opted not to include in this list the manifesto of an African dictator which I read in the past month, so as not to allow any room for misunderstanding, and I will continue this practice with future reviews if I feel the inclusion a certain book could give rise to any regrettable conclusions.&lt;/p>
&lt;p>I would prefer to have given a monthly review, but seeing as this idea only occurred to me in February, I am combining both of the first two months of the year; incidentally, this works out nicely, as my reading velocity in January wasn&amp;rsquo;t as good as in February.
Whether or not I continue this habit of giving a monthly review of my reading into the future shall remain to be seen, depending on both my reading output and me having the free time and motivation to put together another such blog post.
Perhaps I will continue in bimonthly&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> instalments: who knows?&lt;/p>
&lt;p>My great fear with documenting my reading progress like this is that it will encourage poor habits with the perverse incentive of &amp;ldquo;number of books completed per month&amp;rdquo;, dissuading me from engaging critically with the text or reading lengthy tomes, and instead opting for light novels and surface-level readings.
If I suspect that this is beginning to negatively influence my reading habits, I will abandon any recording of my reading progress, and return to reading simply for reading&amp;rsquo;s sake.&lt;/p>
&lt;p>The books listed below are those I &lt;em>finished&lt;/em> in this two-month period; two&amp;mdash;indicated with an asterisk&lt;sup>*&lt;/sup>&amp;mdash;were started before the New Year.
The books are listed in reverse order of completion, that is, the first title listed below is the one I most recently finished.
As I said before, my reading velocity in January was poor, so out of the 9 books below, only 3 were completed in January, with all those from &lt;em>The Myth of Sisyphus&lt;/em> onward being completed in February.&lt;/p>
&lt;h2 id="tao-teh-ching----lao-tzu">&lt;em>Tao Teh Ching&lt;/em> &amp;ndash; Lao Tzu&lt;/h2>
&lt;p>&lt;em>Tao Teh Ching&lt;/em> was my first introduction to the field of Taoism, and therefore, I am confident in saying that I don&amp;rsquo;t have anything of worth to say on the subject.
Nonetheless, I found it to be an interesting read, and enjoyed the usual surface-level comparisons of the &lt;em>Tao&lt;/em> of Laozi with the λόγος (&lt;em>Logos&lt;/em>) of Aristotlean and Christian philosophy, particularly that of the Neo-Platonists.
Living in accordance with the &lt;em>Tao&lt;/em> seems to me to be comparable to the Stoic ideal of living in accordance with Nature (in the sense of Nature not as the inartificial state of the world as un-interfered with by man as in the modern view of the word, but as in (what I am told is) the ancient sense of the word, as the positive and generative flow of the universe &amp;mdash; regardless of whether man or beasts align with this).
Similarly, I wonder if the non-doing of &lt;em>Wu Wei&lt;/em> could be meaningfully compared to the Christian practice of meekness and cheek-turning.
I definitely ought to read more on Taoism in the future.&lt;/p>
&lt;h2 id="the-path-to-freedom----michael-collins">&lt;em>The Path to Freedom&lt;/em> &amp;ndash; Michael Collins&lt;/h2>
&lt;p>A collection of writings by the Irish revolutionary Michael Collins (&lt;span class="gaelic">Míċeál Ó Coileáin&lt;/span>&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>), that any Irishman would be hard-pressed to not find moving.
A fascinating glimpse into the period of history concerning the Anglo-Irish War of Independence and subsequent Civil War.
Proof of the skill and eloquence of Collins&amp;rsquo; speech, and rife with an inspirational hope for a free, equal, and prosperous Irish future.
Filled with arguments for the signing of the Anglo-Irish treaty so compelling that they would give even the most ardent of Irregulars pause;
his deeply persuasive rhetoric and palpable burning desire for peace are not easily dismissed.
Extraordinarily moving, and a must-read for anyone with the slightest interest in Ireland&amp;rsquo;s past, or indeed, her future.&lt;/p>
&lt;p>The book contains a great number of memorable quotes, but my particular favourite is:&lt;/p>
&lt;blockquote>
&lt;p>The English Die-hards said to Mr. Lloyd George and his Cabinet: &amp;ldquo;You have surrendered&amp;rdquo;.
Our own Die-hards said to us: &amp;ldquo;You have surrendered.&amp;rdquo;
There is a simple test.
Those who are left in possession of the battlefield have won.&lt;/p>
&lt;/blockquote>
&lt;p>Of course, there are times where the careful reader can detect that Collins is using the power of his rhetoric to strong-arm the reader into a particular view, in a way that might not be considered entirely fair.
For example, a critic of the Anglo-Irish Treaty would point that in the above quote alone there is a tacit confession that the British have won in the North.
One must be aware when reading that Collins is an exceptional rhetorician &amp;mdash; and therefore is to be examined carefully.&lt;/p>
&lt;h2 id="madonna-in-a-fur-coat----sabahattin-ali">&lt;em>Madonna in a Fur Coat&lt;/em> &amp;ndash; Sabahattin Ali&lt;/h2>
&lt;p>I picked this book out on a whim in a Belfast bookshop, not knowing anything about its contents beyond it being a novel about love, set in Berlin;
I wanted to seed my backlog of books with some light novels to help facilitate the ramp-up of my reading velocity.
A compelling read, and an interesting vignette of an incredibly meek&amp;mdash;but nonetheless compelling&amp;mdash;Turkish man and his hopeless love for a painter from Berlin.&lt;/p>
&lt;p>Originally written in Turkish, the version I read was translated by Maureen Freely and Alexander Dawe, who (so much as I can tell), seem to have done a good job.
The edition I have has a foreword written by a man named David Selim Sayers, which contains some of the worst trite and downright drivel I have had the misfortune of reading in quite some time, including the description of a character&amp;rsquo;s childhood sexual experiences as &amp;ldquo;thrilling&amp;rdquo; and the use of the incorrect form of a very basic idiom that shouldn&amp;rsquo;t have made it past the squiggly blue line in his word processor, much less a professional Editor.
If you have the same edition, don&amp;rsquo;t let the introduction dissuade you: the book itself is actually quite good.&lt;/p>
&lt;h2 id="the-desert-fathers-sayings-of-the-early-christian-monks----benedicta-ward">&lt;em>The Desert Fathers: Sayings of the Early Christian Monks&lt;/em> &amp;ndash; Benedicta Ward&lt;/h2>
&lt;p>A timeless collection of monastic anecdotes and aphorisms, teaching how to live a godly life through discipline, denial of the flesh, and resistance against temptation.
What struck me most about the sayings of these extremely hardy men and women, who are willingly subjecting themselves to the most brutal and harsh of conditions, is the tenderness and gentleness they show in recognising the struggles of others, and picking up others when they fall rather than shaming them.
Saintly examples of refusing to judge your fellow sinner, and letting he who is without sin cast the first stone.&lt;/p>
&lt;p>The focus on humility in particular in the sayings is interesting, as the Desert Fathers won&amp;rsquo;t accept praise even for their most righteous acts lest they become proud.
Indeed, the only exception to their refusal to judge others, is their brutal take-downs of those they suspect of the sin of Pride (because, I suppose, Pride is the only sin that can be cured with shame); in saying 21 of Chapter 8 &lt;em>Nothing Done for Show&lt;/em>, a monk merely publicly states among his brothers that he does not eat cooked food&amp;mdash;not even a brag, just a statement&amp;mdash;and is rebuked by another brother, saying:&lt;/p>
&lt;blockquote>
&lt;p>It would have been better for you to eat meat today in your cell than to have heard this said in front of many brothers.&lt;/p>
&lt;/blockquote>
&lt;p>I found Saying 3 in Chapter 18 &lt;em>Visions&lt;/em> particularly interesting, as it deals with a primitive form of the transubstantiation versus consubstantiation&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> debate, and declares belief in consubstantiation heretical; I hadn&amp;rsquo;t realised how old this issue truly is &amp;mdash; I had naïvely assumed it was only a couple of centuries old, dating from the Protestant Reformation, not as old as monasticism itself.&lt;/p>
&lt;blockquote>
&lt;p>They begged him, saying, &amp;ldquo;You mustn&amp;rsquo;t say that, abba; according to what the Catholic Church has handed down to us, even so do we believe, that is to say, this bread is the Body of Christ in very truth, and is not a mere symbol&amp;hellip;&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;h2 id="nausea----jean-paul-sartre">&lt;em>Nausea&lt;/em> &amp;ndash; Jean-Paul Sartre&lt;/h2>
&lt;p>I was gifted a copy of &lt;em>Nausea&lt;/em> by my girlfriend at Christmas, and certainly with my interest in the writings of Camus, it would be remiss not to venture into the world of his contemporary Sartre.
&lt;em>Nausea&lt;/em> is a masterful depiction of depersonalisation and derealisation as the protagonist grapples with the emptiness of his quotidian existence.
However, the existential ending of the novel seems a pale and colourless answer to the darkness evoked in the earlier pages; no doubt knowingly done, it nonetheless feels as though Sartre is describing a disease to which Camus, not he, is the answer.
Perhaps someone more knowledgeable than I would dismiss my opinions here as philosophically naïve (and it&amp;rsquo;s entirely possible that they are), but I would propose that Sartre&amp;rsquo;s Existentialism can be dismissed with the same mechanism with which Nietzsche dismissed other empty philosophies: knocking on idols with a hammer to see if they&amp;rsquo;re hollow.
And when one knocks on Sartre&amp;rsquo;s Existentialism, it answers with an empty ring.&lt;/p>
&lt;p>Nonetheless, &lt;em>Nausea&lt;/em> is a certainly a must-read, and I would thoroughly recommend.&lt;/p>
&lt;h2 id="the-myth-of-sisyphussupsup----albert-camus">&lt;em>The Myth of Sisyphus&lt;/em>&lt;sup>*&lt;/sup> &amp;ndash; Albert Camus&lt;/h2>
&lt;p>I&amp;rsquo;ve enjoyed the writing of Camus for some time now;
he hasn&amp;rsquo;t been as influential on me as Nietzsche was when I was a teenager, but he writes artistic and enjoyable novels, and although I feel his philosophical insight isn&amp;rsquo;t massively interesting when compared to someone like Nietzsche, his philosophy is good wholesome stuff.
&lt;em>The Myth of Sisyphus&lt;/em> is his most direct laying-down of his Absurdist philosophy, which I suppose I would subscribe to, although I don&amp;rsquo;t feel that it really adds enough to Nihilism to call it a distinct philosophy &amp;mdash; just a mode of viewing Nihilism.
I have somehow yet to read Nietzsche&amp;rsquo;s &lt;em>Will to Power&lt;/em>, so I will refrain from commenting too much on issues I know little about, but it doesn&amp;rsquo;t seem to me that Camus really builds much on Nietzschean philosophy.
The (semi-)popular view of Nietzsche as an Existentialist is one that I would reject;
I would, perhaps controversially, view him as a mode of Nihilist.
Absurdism seems to me to just be a mode of Nietzschean Nihilism.&lt;/p>
&lt;p>On the other hand, I view Camus&amp;rsquo; work as a healthy antidote to the hollow Existentialism that Sartre pushed;
there&amp;rsquo;s a healthiness and a robustness to Absurdist thought that Existentialism (in my view) fails to create.
The Existentialist idea of &amp;ldquo;creating your own meaning&amp;rdquo; to replace the meaninglessness of reality is empty:
that &amp;ldquo;meaning&amp;rdquo; is equally meaningless.
The only winning move in the meaning game is to refuse to play, as Camus does.
I think Camus has a slightly edgy view where he emphasises the important of &amp;ldquo;revolt&amp;rdquo; against the meaninglessness of life and enjoys this aesthetic;
I suppose I simply don&amp;rsquo;t care that life is meaningless, as opposed to wanting to &amp;ldquo;revolt&amp;rdquo; against anything.&lt;/p>
&lt;p>Not as interesting a read in my eyes as Camus&amp;rsquo; other works (like &lt;em>The Stranger&lt;/em>, &lt;em>A Happy Death&lt;/em>, and &lt;em>The Plague&lt;/em>), but certainly worth the read, and the philosophy laid out therein is, in my eyes, more or less the correct way to view life.&lt;/p>
&lt;h2 id="how-the-irish-saved-civilisation----thomas-cahill">&lt;em>How the Irish Saved Civilisation&lt;/em> &amp;ndash; Thomas Cahill&lt;/h2>
&lt;p>Despite its bold title, &lt;em>How the Irish Saved Civilisation&lt;/em> is a surprisingly accurate historical account of the Gaelic Christian mission to Europe.
A worthwhile read, as I think it nicely debunks some odd talking points that one is likely to encounter in modern discourse about the history of Ireland &amp;mdash; namely by showing that Ireland&amp;rsquo;s conversion was, unlike that of most other nations, voluntary and peaceful, and that Ireland didn&amp;rsquo;t meaningfully have a &amp;ldquo;Dark Age&amp;rdquo; like that of the rest of Europe (not withstanding post-Renaissance debates on whether or not there was actually a Dark Age).
Despite going into this book expecting it to be a somewhat chauvinistic and excessively proud account of Irish history based off its title, there are times in the book where one feels the author is trying hard to combat this perception by going too far in the other direction in his put-downs of barbaric ancient Gaels.
However, if one is prepared to critically assess the contents as they read it, they can expect an interesting and edifying account of the much-neglected role Ireland played in European history.&lt;/p>
&lt;h2 id="the-third-policeman----flann-obrien">&lt;em>The Third Policeman&lt;/em> &amp;ndash; Flann O&amp;rsquo;Brien&lt;/h2>
&lt;p>&lt;em>The Third Policeman&lt;/em> is a delightfully bizarre and unpredictable fever dream;
seemingly completely nonsensical, it follows a more-or-less coherent plot, and unlike other works which use absurdity as an end unto itself, the surreality of the story actually all makes sense in the end &amp;mdash; not that a reader would ever suspect that it would, or even &lt;em>could&lt;/em>.
It follows a man on his quest in an unnatural and other-worldly Irish village, the next one over from his own, on a quest for a missing moneybox, the owner of which he had beaten to death with a spade, and his interaction with the policemen of that village as he attempts to trick them into helping him to find it so that he might fund his passion project book on the fictional polymath &lt;em>De Selby&lt;/em>, whose fantastical life and achievements sometimes span pages worth of footnotes.&lt;/p>
&lt;p>I picked up this book not knowing anything about its contents, but knowing that Flann O&amp;rsquo;Brien was a writer who (although I had read little of his work) enjoyed greatly, and was one of the sharpest Irish wits of the 20&lt;sup>th&lt;/sup> century (and indeed you could remove both the &amp;ldquo;Irish&amp;rdquo; and &amp;ldquo;20&lt;sup>th&lt;/sup> century&amp;rdquo; qualifiers from that sentence).&lt;/p>
&lt;h2 id="zorba-the-greeksupsup----nikos-kazantzakis">&lt;em>Zorba the Greek&lt;/em>&lt;sup>*&lt;/sup> &amp;ndash; Nikos Kazantzakis&lt;/h2>
&lt;p>&lt;em>Zorba the Greek&lt;/em> is the portrait of a character who is more a force of Nature than a man.
The book follows the narrator, a bookish man who is notably detached from &amp;ldquo;real life&amp;rdquo;, and his newfound friend Zorba, a man who lives life with such a passion and lack of detachment that it often seems as though he has no regard for anything but the present moment, in their mining project on the island of Crete.
The book shows the classic struggle of an author between a love of words and a desire to experience life through literature juxtaposed with the separation that this practice puts between the writer and life itself.
Zorba is the kind of man that has no appreciation for literature, and is rather the kind of man about whom literary types write, but cannot become, separated from reality by their love of the abstraction of words.
Zorba is the picture of Nietzschean vitality, and the comparison between him and the narrator is that of Nietzsche&amp;rsquo;s Dionysian and Apollonian.&lt;/p>
&lt;p>In a way, I find the book reminiscent of the views expressed by &lt;span class="nihongo">&lt;ruby>三島 由紀夫&lt;rt>Mishima Yukio&lt;/rt>&lt;/ruby>&lt;/span> in his essay &lt;em>Sun and Steel&lt;/em>, in which the author compares his detached, childhood love of words with his newfound practical love of reality itself, unencumbered by abstraction and description.
Indeed, Mishima&amp;rsquo;s anxiety about having missed out on his opportunity to perform his patriotic duty&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup> in the military during the war is echoed&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup> in the contrast between the narrator&amp;rsquo;s friend Stavridakis who has gone off to fight the Bolsheviks and the narrator&amp;rsquo;s staying-put in Greece.
This separation between the writer and reality can be seen over and over in a large number of books beyond &lt;em>Sun and Steel&lt;/em>, like in &lt;em>Nausea&lt;/em> between the un-reality of Roquentin&amp;rsquo;s life and the richness of the historical life of the Marquis de Rollebon &amp;mdash; just to name those which are at the top of my mind.&lt;/p>
&lt;p>The subhuman treatment of women throughout this book, however, can make it difficult to enjoy; one hardly expects a perfect feminist utopia in a book from 1946, but there is some particularly disturbing abuse of women in this book.
Although probably standard, if not progressive, for Greek society during that period, it is nonetheless distinctly unpleasant at times for the modern reader (although not, in fairness, always without self-awareness &amp;mdash; there is &lt;em>some&lt;/em> recognition of this mistreatment within the text).&lt;/p>
&lt;p>In a way, I feel that the ending of &lt;em>Zorba the Greek&lt;/em> echoes that of &lt;em>Madonna in a Fur Coat&lt;/em>; I couldn&amp;rsquo;t go into more detail without spoiling either, but I think someone who has read both can appreciate the shared theme.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Ordinarily, I would deprecate the usage of the word &lt;em>bimonthly&lt;/em> or any of its &lt;em>bi-&lt;code>&amp;lt;time_period&amp;gt;&lt;/code>&lt;/em> brethren, but I think my meaning is clear in this context.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>As he signed the Anglo-Irish Treaty.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>Of course, pre-dating these terms.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>Or rather what he perceives to be his patriotic duty.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>&amp;ldquo;Echoed&amp;rdquo; in the broadest sense of the word: &lt;em>Sun and Steel&lt;/em> was published some twenty years after &lt;em>Zorba the Greek&lt;/em>.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>A Banner of Green, White, &amp; Gold?</title><link>https://ohaodha.ie/blog/a-banner-of-green-white--gold/</link><pubDate>Thu, 23 Oct 2025 22:51:58 +0100</pubDate><guid>https://ohaodha.ie/blog/a-banner-of-green-white--gold/</guid><description>&lt;figure>
 &lt;a href="./images/Flag_of_Ireland.svg.png">
 &lt;img src="./images/Flag_of_Ireland.svg.png" alt="The national flag of Ireland">
 &lt;/a>&lt;figcaption>The national flag of Ireland&lt;/figcaption>&lt;/figure>&lt;h2 id="the-importance-of-orange">The Importance of Orange&lt;/h2>
&lt;p>You probably don&amp;rsquo;t need me to tell you that the flag in the above image is the national flag of Ireland.
You also probably don&amp;rsquo;t need me to tell you that the colours in that image are green, white, &amp;amp; orange.
However, you may have heard people referring to the colours of the flag as &amp;ldquo;green, white, &amp;amp; gold&amp;rdquo; before, or possibly even people &amp;ldquo;correcting&amp;rdquo; those who describe the final stripe as &amp;ldquo;orange&amp;rdquo; to say &amp;ldquo;gold&amp;rdquo;, and you may therefore wonder: &amp;ldquo;is the flag is actually, officially, green, white, &amp;amp; gold?&amp;rdquo;&lt;/p>
&lt;p>The simple answer is &lt;strong>no&lt;/strong>: the flag is green, white, &amp;amp; orange.
This is specified in the Constitution of Ireland (&lt;em>Bunreacht na hÉireann&lt;/em>), which specifies only that &amp;ldquo;the national flag is the tricolour of green, white and orange&amp;rdquo;&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, and in the Department of the Taoiseach&amp;rsquo;s guidelines on &lt;em>The National Flag&lt;/em>&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>, which goes into further detail on the dimensions of the flag and the history behind it.&lt;/p>
&lt;p>This is not just a matter of semantics, the meaning of the colour &lt;em>orange&lt;/em>, specifically, is important: the green of the flag represents Irish Catholics,
the orange Irish Protestants, and the white represents peace between these two long-conflicting groups.
The Young Irelander, Thomas Francis Meagher, who is perhaps the most notable early adopter of the tricolour described its symbolism as follows&lt;sup id="fnref1:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>:&lt;/p>
&lt;blockquote>
&lt;p>The white in the centre signifies a lasting truce between Orange and Green and I trust that beneath its folds the hands of Irish Protestants and Irish Catholics may be clasped in generous and heroic brotherhood.&lt;/p>
&lt;/blockquote>
&lt;p>Thus, to refer to the flag as &amp;ldquo;green, white, &amp;amp; gold&amp;rdquo; is not only incorrect, but also misses the entire point of the flag.
While &amp;ldquo;gold&amp;rdquo; is often used with poetic license (presumably partly because, famously, nothing rhymes with &amp;ldquo;orange&amp;rdquo;), often those referring to the orange of the flag as &amp;ldquo;gold&amp;rdquo; are doing so in a sectarian attempt to erase Irish Protestants from the flag.
The Department of the Taoiseach&amp;rsquo;s publication &lt;em>The National Flag&lt;/em>&lt;sup id="fnref2:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> condemns this strongly:&lt;/p>
&lt;blockquote>
&lt;p>Sometimes shades of yellow or gold, instead of orange, are seen at civilian functions. This is a misrepresentation of the National Flag and should be actively discouraged.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Down to modern times yellow or gold has occasionally been used instead of orange, but this substitution destroys the symbolism of the National Flag.&lt;/p>
&lt;/blockquote>
&lt;h2 id="pedantry">Pedantry&lt;/h2>
&lt;h3 id="glas-or-uaine-oráiste-or-flannbhuí">&lt;em>Glas&lt;/em> or &lt;em>Uaine&lt;/em>; &lt;em>Oráiste&lt;/em> or &lt;em>Flannbhuí&lt;/em>?&lt;/h3>
&lt;p>The only thing more annoying than a pedant is a confidently incorrect pedant, and there are many who are incorrectly pedantic about the Irish flag.
The most prevalent form of incorrect pedantry is what I have discussed above: the incorrect insistence that the orange stripe of the flag is in fact gold.
However, there is ever-greater scope for pedantry regarding the national flag in the Irish language, as the terms used to describe the colours of the flag in the Constitution are not as simple as &amp;ldquo;green&amp;rdquo; and &amp;ldquo;orange&amp;rdquo;:
thus, someone who incorrectly insists that the colours of the flag are &lt;em>glas, bán, agus órga&lt;/em> (green, white, and gold), could arguably be said to be wrong about not just one, but two colours of the flag.&lt;/p>
&lt;p>The Irish text of Article 7 of the Irish Constitution&lt;sup id="fnref1:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> is as follows (translation my own, and very literal):&lt;/p>
&lt;blockquote>
&lt;p>An bhratach trí dhath .i. uaine, bán, agus flannbhuí, an suaitheantas náisiúnta.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>The banner of three colours, i.e., vert, white, and blood-yellow, is the national standard.&lt;/p>
&lt;/blockquote>
&lt;p>To be clear, this translation is overly literal and is not to be considered the intended translation of those words.
The chromonyms used in the Constitution are old-fashioned and unusual by modern standards, but are also subtly distinct from the more common words used to describe similar colours.&lt;/p>
&lt;p>&amp;ldquo;Green&amp;rdquo; in Irish is most commonly referred to as &lt;em>glas&lt;/em>, but the Constitution uses the word &lt;em>uaine&lt;/em>; I&amp;rsquo;ve chosen to translate the word &lt;em>uaine&lt;/em> as &lt;em>vert&lt;/em> in an attempt to emphasise the heraldic sense of the word, but a better translation would likely be &amp;ldquo;vivid green&amp;rdquo;.
&lt;em>Glas&lt;/em> is typically used to mean the green or grey of natural and living things, like the green of grass &amp;amp; trees, but also the grey of animals or the sky&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>;
&lt;em>uaine&lt;/em>, on the other hand, is a more vivid or unnatural green, like that of painted or dyed items, like flags or clothing.&lt;/p>
&lt;p>While &lt;em>oráiste&lt;/em>, a Romance loan word&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup>, is the more common word for &amp;ldquo;orange&amp;rdquo; in modern usage, &lt;em>flannbhuí&lt;/em> is a more traditional term, constructed from the words &lt;em>flann&lt;/em> (blood-red) and &lt;em>buí&lt;/em> (yellow).
Unlike &lt;em>glas&lt;/em> and &lt;em>uaine&lt;/em>, I don&amp;rsquo;t think there is any distinction between the colours to which &lt;em>oráiste&lt;/em> and &lt;em>flannbhuí&lt;/em> refer, the distinction between the words being purely a matter of vocabulary.&lt;/p>
&lt;p>I had a teacher in the Gaeltacht once correct those of us who referred to the flag as &lt;em>glas&lt;/em>, and said that the flag is not &lt;em>glas&lt;/em> but &lt;em>uaine&lt;/em>&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>, a correction which I too have issued in the past as a &amp;ldquo;fun fact&amp;rdquo;, and indeed the Constitution describes the flag as &lt;em>uaine&lt;/em> instead of &lt;em>glas&lt;/em>;
however, I&amp;rsquo;ve made reference to the Department of the Taoiseach&amp;rsquo;s &lt;em>The National Flag&lt;/em> in this post, the Irish text of which refers to the flag as &lt;em>glas&lt;/em> and &lt;em>oráiste&lt;/em> except when quoting the Constitution.
Clearly, the authors of that guideline were of the opinion that &lt;em>uaine&lt;/em> is simply a poetic term for the particular shade of &lt;em>glas&lt;/em> in the flag, and that &lt;em>flannbhuí&lt;/em> was simply an older term equivalent to &lt;em>oráiste&lt;/em>.
I will defer to the authority of the Department of the Taoiseach on this, as they are no doubt both more qualified than me in vexillology and in the Irish language, and I would say that although the Constitution says &lt;em>uaine, bán, agus flannbhuí&lt;/em>, &lt;em>glas, bán, agus oráiste&lt;/em> is just as correct.&lt;/p>
&lt;h3 id="the-flag-of-the-ivory-coast">The Flag of the Ivory Coast&lt;/h3>
&lt;p>The final bit of vexillological pedantry to which I wish to object in this blogpost is when people accuse others, typically their political enemies, of ignorantly flying the flag of the Ivory Coast (&lt;em>Côte d&amp;rsquo;Ivoire&lt;/em>), which is rather similar to the Irish flag, being a tricolour of orange, white, &amp;amp; green.
While this mistake does occur from time to time, I wish to argue here in defence of those accused of making this mistake that, often, the flag in question cannot be described as an Ivory Coast flag, but as an Irish flag displayed in an unusual, or perhaps reversed, manner.&lt;/p>
&lt;figure>
 &lt;a href="./images/Flag_of_C%c3%b4te_d%27Ivoire.svg">
 &lt;img src="./images/Flag_of_C%c3%b4te_d%27Ivoire.svg" alt="The flag of the Republic of the Ivory Coast">
 &lt;/a>&lt;figcaption>The flag of the Republic of the Ivory Coast&lt;/figcaption>&lt;/figure>&lt;p>Indeed, this mistake has happened on several notable occasions in the past, such as when the then-Taoiseach Leo Varadker&lt;sup id="fnref:6">&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref">6&lt;/a>&lt;/sup> posted a tweet in which he used the Ivory Coast flag instead of the Irish flag&lt;sup id="fnref:7">&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref">7&lt;/a>&lt;/sup>.&lt;/p>
&lt;figure>
 &lt;a href="./images/tweet.png">
 &lt;img src="./images/tweet.png" alt="2018 Leo Varadker tweet which used the flag of the Ivory Coast">
 &lt;/a>&lt;figcaption>2018 Leo Varadker tweet which used the flag of the Ivory Coast&lt;/figcaption>&lt;/figure>&lt;p>Typically, when someone is accused of displaying an Ivory Coast flag, it&amp;rsquo;s done by a political rival in an effort to make their opponent seem stupid and unpatriotic:
for this reason, I will refrain from showing any examples of what I would consider to be a false accusation of the use of the Ivory Coast flag here, as I don&amp;rsquo;t want to give the impression that I am endorsing either of the political groups in question in whatever example I could include.
There are two important points I wish to make on this matter:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>The flag of the Ivory Coast is not simply a reversed Irish flag&lt;/strong>: the Irish flag has a 1:2 aspect ratio, being twice as long as it is tall&lt;sup id="fnref3:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>, while the flag of the Ivory Coast has a 2:3 aspect ratio, more common among national flags&lt;sup id="fnref:8">&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref">8&lt;/a>&lt;/sup>.&lt;/li>
&lt;li>&lt;strong>Displaying a flag backwards does not change the nation which it represents:&lt;/strong> even if the flag of the Ivory Coast and the flag of Ireland had identical dimensions and were simply mirror images of one another, holding an Irish flag backwards would still not transform it into an Ivory Coast flag&lt;sup id="fnref:9">&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref">9&lt;/a>&lt;/sup>, as the staff side of the flag would likely still be identifiable and be green.
While the guidelines in &lt;em>The National Flag&lt;/em>&lt;sup id="fnref4:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> say the flag should be displayed with the green on the left of the observer when hung horizontally, this doesn&amp;rsquo;t mean that an Irish flag displayed with the orange on the observer&amp;rsquo;s left is a different flag: it&amp;rsquo;s just an incorrectly displayed Irish flag.&lt;/li>
&lt;/ol>
&lt;p>In conclusion, vexillological pedants are often not even correct about their pedantry, although I don&amp;rsquo;t feel that being correct would make their &amp;ldquo;corrections&amp;rdquo; much less irritating.
It&amp;rsquo;s also worth noting here that the tricolour used by Meagher had the orange stripe on the staff side, like the Ivory Coast flag, and the Constitution of Ireland does not specify a colour order or aspect ratio (although green, then white, then orange is implied).&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Article 7, Constitution of Ireland. &lt;a href="https://www.irishstatutebook.ie/eli/cons/en/html">https://www.irishstatutebook.ie/eli/cons/en/html&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Department of the Taoiseach, 2018. &lt;em>The National Flag&lt;/em>. &lt;a href="https://www.gov.ie/en/department-of-the-taoiseach/publications/the-national-flag/">https://www.gov.ie/en/department-of-the-taoiseach/publications/the-national-flag/&lt;/a>&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref2:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref3:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref4:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>From what I can tell as a non-native speaker of Irish, the use of the word &lt;em>glas&lt;/em> to mean &amp;ldquo;grey&amp;rdquo; is usually in older or poetic usage; the more common word for &amp;ldquo;grey&amp;rdquo; is &lt;em>liath&lt;/em>.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>eDIL s.v. “oráitse, oráiste.” eDIL – Electronic Dictionary of the Irish Language. &lt;a href="https://dil.ie/33937">https://dil.ie/33937&lt;/a>&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>Curiously, he didn&amp;rsquo;t issue a similar correction for description of the orange as &lt;em>oráiste&lt;/em> instead of &lt;em>flannbhuí&lt;/em>; I presume he considered &lt;em>uaine&lt;/em> to mean something distinct from &lt;em>glas&lt;/em>, whereas he considered &lt;em>flannbhuí&lt;/em> to be a synonym for &lt;em>oráiste&lt;/em>.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6">
&lt;p>I believe that he writes his own tweets (or at least &lt;em>did&lt;/em>) and does not have them ghost-written by a social media manager because only a boomer of his vintage would scroll through the emoji section of the keyboard visually searching for the emoji he wants to use instead of using the search feature to search for the &amp;ldquo;Ireland&amp;rdquo; emoji, which would both be far more convenient and preclude the possibility of this sort of mistake occurring.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7">
&lt;p>The esteemed &amp;amp; august reputable source that is the Irish Mirror: &lt;a href="https://www.irishmirror.ie/sport/rugby-union/taoiseach-leo-varadkar-ridiculed-after-12206028">https://www.irishmirror.ie/sport/rugby-union/taoiseach-leo-varadkar-ridiculed-after-12206028&lt;/a>&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8">
&lt;p>To be charitable to Varadker, this distinction makes his mistake more understandable, as in emoji form, the 1:2 aspect ratio of the Irish flag is not honoured, and instead is displayed in a 2:3 ratio, so the Ivory Coast flag really is just a reversed Irish flag: compare 🇮🇪 versus 🇨, if those emojis render correctly in your browser.&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:9">
&lt;p>Although this has been done in the past, in a pinch: &lt;a href="https://www.independent.ie/sport/other-sports/athletics/quick-thinking-irish-fans-come-to-the-rescue-of-victorious-ivory-coast-star-at-world-indoor-athletics-championships/36669933.html">https://www.independent.ie/sport/other-sports/athletics/quick-thinking-irish-fans-come-to-the-rescue-of-victorious-ivory-coast-star-at-world-indoor-athletics-championships/36669933.html&lt;/a>.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>`inutility_scripts`: Scripts That Don't Do Anything Useful</title><link>https://ohaodha.ie/blog/inutility_scripts-scripts-that-dont-do-anything-useful/</link><pubDate>Fri, 01 Aug 2025 15:45:45 +0100</pubDate><guid>https://ohaodha.ie/blog/inutility_scripts-scripts-that-dont-do-anything-useful/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Roughly two years ago now, I created my &lt;a href="https://github.com/0x4A0D4A/utility_scripts">&lt;code>utility_scripts&lt;/code>&lt;/a> repository on GitHub, which contains all the scripts that I&amp;rsquo;ve written for use on my GNU/Linux laptop.
These scripts are generally short, useful scripts, that make basic tasks on my computer easier, ranging from creating dynamic menus to select &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/bluetooth_dmenu.pl">Bluetooth devices&lt;/a> or &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/wifi_dmenu.sh">WiFi networks&lt;/a> to connect to, to listing the &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/bspwm_window_count.sh">count of open windows on a workspace&lt;/a> or &lt;a href="https://github.com/0x4A0D4A/utility_scripts/blob/main/src/bluetooth_info.pl">connected Bluetooth devices&lt;/a> in the status bar of my desktop, and more.
Originally a copy of my &lt;code>~/scripts/&lt;/code> directory, the repository now lives with all my other GitHub repositories at &lt;code>~/code/github/0x4A0D4A/utility_scripts/&lt;/code> and &lt;code>~/code/scripts/&lt;/code> is symlinked to the &lt;code>utility_scripts/src&lt;/code> directory, and is in my &lt;code>$PATH&lt;/code>, so any of these scripts are accessible as commands on my system.&lt;/p>
&lt;p>However, I eventually found that the &lt;code>utility_scripts&lt;/code> repository was restrictive in content:
it was restricted to containing only &lt;em>useful&lt;/em> scripts, and I had a number of scripts that I wanted to share that I couldn&amp;rsquo;t in good faith describe as &amp;ldquo;useful&amp;rdquo;, and so I created a new repository named &lt;code>inutilty_scripts&lt;/code>: a collection of miscellaneous scripts that don&amp;rsquo;t do anything useful.
These scripts are ones that I consider interesting (often proofs of concepts), but that I have yet to find any practical use for.
Some of these scripts are entirely useless, but others demonstrate some interesting concept that I think &lt;em>could&lt;/em> potentially be useful if the right situation arose.&lt;/p>
&lt;h2 id="inutility_scripts">&lt;code>inutility_scripts&lt;/code>&lt;/h2>
&lt;h3 id="recursivesh">&lt;code>recursive.sh&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/0x4A0D4A/inutility_scripts/blob/main/src/recursive.sh">&lt;code>recursive.sh&lt;/code>&lt;/a> is a script that calls itself, recursively.
While I can&amp;rsquo;t think of any uses for this, I don&amp;rsquo;t find it particularly difficult to imagine that a recursive shell script &lt;em>could&lt;/em> be useful for some task or another, as recursion has many useful applications with regards to function calls.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># A script that calls itself&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="nv">recursion_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">1&lt;/span>&lt;span class="k">:-&lt;/span>&lt;span class="nv">0&lt;/span>&lt;span class="si">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Recursion number &lt;/span>&lt;span class="nv">$recursion_count&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nv">$0&lt;/span> &lt;span class="k">$((&lt;/span>recursion_count &lt;span class="o">+&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="k">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;figure>
 &lt;a href="./images/recursive.png">
 &lt;img src="./images/recursive.png" alt="Screenshot of the output of recursive.sh">
 &lt;/a>&lt;figcaption>Output of &lt;code>./recursive.sh&lt;/code>&lt;/figcaption>&lt;/figure>&lt;p>This script also makes use of some interesting POSIX shell features:
to demonstrate that recursion is indeed occurring, the script prints the &lt;code>recursion_count&lt;/code> for each invocation, using parameter expansion &amp;amp; default value substitution to set the value of the &lt;code>recursion_count&lt;/code> variable to the first positional argument supplied to the script invocation (&lt;code>$1&lt;/code>), if present, or &lt;code>0&lt;/code> if &lt;code>$1&lt;/code> is unset.
It also exploits the fact that the &lt;code>$0&lt;/code> variable contains the filename with which the shell was invoked&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, i.e., the script file itself, and uses the value of that variable as a command within the script.&lt;/p>
&lt;figure>
 &lt;a href="./images/recursive_limit.png">
 &lt;img src="./images/recursive_limit.png" alt="/bin/sh: warning: shell level (1000) too high, resetting to 1">
 &lt;/a>&lt;figcaption>&lt;code>/bin/sh: warning: shell level (1000) too high, resetting to 1&lt;/code>&lt;/figcaption>&lt;/figure>&lt;p>Something interesting also happens when the recursion count hits 996:
a message is displayed which says &lt;code>/bin/sh: warning: shell level (1000) too high, resetting to 1&lt;/code>.
The variable &lt;code>$SHLVL&lt;/code> is incremented for each new instance of the shell spawned&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>, and thus tells you the current level of shell nesting.
If you run &lt;code>echo $SHLVL&lt;/code> in an open terminal window, you&amp;rsquo;ll probably get either &lt;code>1&lt;/code> or &lt;code>2&lt;/code> as your output:&lt;/p>
&lt;ul>
&lt;li>&lt;code>1&lt;/code> will be your output if this shell session is the first one spawned for your session, that is, a shell wasn&amp;rsquo;t automatically spawned when you logged in;&lt;/li>
&lt;li>&lt;code>2&lt;/code> will be your output if a login shell was automatically started when your user was logged in.&lt;/li>
&lt;/ul>
&lt;p>On my system, I get an output of &lt;code>3&lt;/code>: one of these shells being my login shell, the other being the terminal window that I have open, and the third probably being spawned by the &lt;code>zsh&lt;/code> plugins I source in my configuration, although I&amp;rsquo;ve never bothered to actually track down the what exactly is causing it.
This explains why the recursion count hits &lt;code>996&lt;/code> before the shell level reset warning is displayed:
the shell level is reset to &lt;code>1&lt;/code> once it hits &lt;code>1000&lt;/code>, and 1,000 shells can be accounted for by adding the 3 that we know are extant when a terminal window is opened on my system, and the 997 shells spawned by the &lt;code>recursive.sh&lt;/code> script (recalling that the first call of the script the &lt;code>$recursion_count&lt;/code> is &lt;code>0&lt;/code> so the number of shells is the &lt;code>$recursion_count&lt;/code> plus 1).&lt;/p>
&lt;h3 id="self_destructsh">&lt;code>self_destruct.sh&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/0x4A0D4A/inutility_scripts/blob/main/src/self_destruct.sh">&lt;code>self_destruct.sh&lt;/code>&lt;/a> is the simplest of inutility scripts, and simply deletes itself.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># A script that deletes itself&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">rm &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;figure>
 &lt;a href="./images/self_destruct.png">
 &lt;img src="./images/self_destruct.png" alt="Screenshot of the before &amp;amp; after of self_destruct.sh">
 &lt;/a>&lt;figcaption>Before &amp;amp; after of &lt;code>./self_destruct.sh&lt;/code>&lt;/figcaption>&lt;/figure>&lt;p>Similar to &lt;code>recursive.sh&lt;/code>, it exploits the &lt;code>$0&lt;/code> variable, and simply deletes the file at the filepath contained within that variable.
Since the script is under Git version control, it can easily be brought back after execution (in both senses of the word) by running &lt;code>git restore self_destruct.sh&lt;/code>.&lt;/p>
&lt;p>I can imagine a potential use for this self-destructive functionality being to ensure that a script is ran precisely once and never again, or perhaps to try and cover the tracks of a malware attack by deleting the malicious file as soon as possible to avoid detection.&lt;/p>
&lt;p>Interestingly, it&amp;rsquo;s possible to delete a script while it&amp;rsquo;s executing without interrupting execution at all.
Originally, I had assumed that, since shell scripts are only interpreted line-by-line (with a few exceptions), deleting a script while it&amp;rsquo;s executing would halt execution, so when I discovered that this was not the case, I imagined that this meant that the entire script was read into memory before execution, but this isn&amp;rsquo;t the case either&amp;hellip;&lt;/p>
&lt;p>Most people are probably aware that deleting a file on a UNIX system (or pretty much any computer for that matter) doesn&amp;rsquo;t actually destroy the data contained within that file:
instead, deleting a file removes the &lt;strong>directory entry&lt;/strong>, which is the mapping of the filename to its &lt;strong>inode&lt;/strong>&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> which is the underlying data structure used by the file system to store the file&amp;rsquo;s metadata and pointers to the data blocks at which the actual file data is stored on disk.
When there are no longer any filenames (or &lt;em>links&lt;/em>) pointing to the inode, the file is considered unreachable and data blocks are marked as &lt;strong>free&lt;/strong>, allowing them to be deallocated &amp;amp; overwritten.&lt;/p>
&lt;p>However, if a process has a file open, it continues to have a reference to that file&amp;rsquo;s inode, regardless of file deletion or renaming.
Thus, the process can continue to read from that file even after it has no extant links in the file system.
The deleted file&amp;rsquo;s data blocks cannot be deallocated until the process has closed the open file descriptor.&lt;/p>
&lt;p>I wrote a short script called &lt;code>test.sh&lt;/code> to demonstrate this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 4&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 6&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 7&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 9&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Step 10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(Obviously, this script would make more sense as a &lt;code>for&lt;/code>-loop if it served some practical purpose, but the point here is to demonstrate that lines that had not yet been reached when the file was deleted are still executed, which would not be demonstrated if a &lt;code>for&lt;/code>-loop was used.)
If the script is deleted mid-execution, we can see that execution is not interrupted:&lt;/p>
&lt;figure>
 &lt;a href="./images/test.png">
 &lt;img src="./images/test.png" alt="Screenshot of test.sh being deleted mid-execution yet continuing to run unaffected">
 &lt;/a>&lt;figcaption>Screenshot of &lt;code>test.sh&lt;/code> being deleted mid-execution yet continuing to run unaffected&lt;/figcaption>&lt;/figure>&lt;p>We can also use &lt;a href="https://www.man7.org/linux/man-pages/man8/lsof.8.html">&lt;code>lsof(8)&lt;/code>&lt;/a> to list the open files on the system to see that the executing process retains a file descriptor for the deleted file:&lt;/p>
&lt;figure>
 &lt;a href="./images/lsof.png">
 &lt;img src="./images/lsof.png" alt="Screenshot of the output of lsof | grep test.sh after deletion of test.sh mid-execution">
 &lt;/a>&lt;figcaption>Output of &lt;code>lsof | grep test.sh&lt;/code> after deletion of &lt;code>test.sh&lt;/code> mid-execution&lt;/figcaption>&lt;/figure>&lt;p>Thus we know that a shell script will continue to execute even if its source file is deleted, because it retains an open file descriptor for that file from which it can read its contents until termination.&lt;/p>
&lt;h3 id="disappearingsh">&lt;code>disappearing.sh&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/0x4A0D4A/inutility_scripts/blob/main/src/disappearing.sh">&lt;code>disappearing.sh&lt;/code>&lt;/a> is a script that disappears from the filesystem (or, more precisely, the directory entries) during execution and re-appears once it has finished executing.
It is something of a natural follow-on from &lt;code>self_destruct.sh&lt;/code> in that it is a script that deletes its own source code, and also because it exploits the fact that a script will continue to execute uninterrupted even if its source file is deleted.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># Script that deletes its source code while executing and replaces it when exiting, hence &amp;#34;disappearing&amp;#34; while running and re-appearing once finished&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nv">file_contents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>cat &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">rm &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="k">for&lt;/span> i in &lt;span class="k">$(&lt;/span>seq &lt;span class="m">1&lt;/span> 10&lt;span class="k">)&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;Doing something invisible&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> sleep &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$file_contents&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &amp;gt; &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> chmod +x &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Like &lt;code>self_destruct.sh&lt;/code>, it exploits &lt;code>$0&lt;/code> to delete its own source code, but it also uses &lt;code>$0&lt;/code> to read its source code into a variable called &lt;code>file_contents&lt;/code>, which it then &lt;code>echo&lt;/code>es into the filepath defined by &lt;code>$0&lt;/code> and makes said file executable before finally exiting.&lt;/p>
&lt;figure>
 &lt;a href="./images/disappearing.png">
 &lt;img src="./images/disappearing.png" alt="Screenshot of disappearing.sh being executed, including the output of ls before, after, and during its execution">
 &lt;/a>&lt;figcaption>Output of &lt;code>disappearing.sh&lt;/code>, including the output of &lt;code>ls&lt;/code> before, after, and during its execution&lt;/figcaption>&lt;/figure>&lt;p>Of course, the script hasn&amp;rsquo;t truly disappeared from the filesystem, only the directory entries, as the remaining open file descriptor can be seen with the use of &lt;code>lsof&lt;/code>:&lt;/p>
&lt;figure>
 &lt;a href="./images/grep_disappearing.png">
 &lt;img src="./images/grep_disappearing.png" alt="Screenshot of the output of lsof | grep disappearing.sh">
 &lt;/a>&lt;figcaption>Screenshot of the output of &lt;code>lsof | grep disappearing.sh&lt;/code>&lt;/figcaption>&lt;/figure>&lt;h3 id="suicidalsh">&lt;code>suicidal.sh&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/0x4A0D4A/inutility_scripts/blob/main/src/suicidal.sh">&lt;code>suicidal.sh&lt;/code>&lt;/a> is a script that kills itself, that is, its own process.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># A script that kills the process that&amp;#39;s executing it, i.e. itself&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">pkill &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>basename &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$0&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Similar in &amp;ldquo;function&amp;rdquo; (I use the word lightly) to &lt;code>self_destruct.sh&lt;/code>, it also uses the &lt;code>$0&lt;/code> variable;
it finds the name of the script by running the &lt;a href="https://www.man7.org/linux/man-pages/man3/basename.3.html">&lt;code>basename&lt;/code>&lt;/a> command with this &lt;code>$0&lt;/code> variable, and uses &lt;a href="https://linux.die.net/man/1/pkill">&lt;code>pkill&lt;/code>&lt;/a> to kill any process executing with that name, i.e., itself.&lt;/p>
&lt;figure>
 &lt;a href="./images/suicidal.png">
 &lt;img src="./images/suicidal.png" alt="Screenshot of the output of ./suicidal.sh">
 &lt;/a>&lt;figcaption>Output of &lt;code>./suicidal.sh&lt;/code>&lt;/figcaption>&lt;/figure>&lt;p>Try as I might, I cannot think of a single situation in which this would be useful;
one might think that this could potentially have some use in killing a script if some error is encountered, but there&amp;rsquo;s nothing that this approach achieves that the &lt;code>exit&lt;/code> command wouldn&amp;rsquo;t.
Perhaps, in a very contrived situation, you might have a script that must only ever have one instance of itself running at a time, and if it is detected that there are multiple instances running, they will all need to be killed to prevent some catastrophic scenario, and in that situation this snippet would be useful;
however, I would suggest that a mutex or semaphore of some kind would likely be a far better solution to prevent this situation from arising.&lt;/p>
&lt;h3 id="trippysh">&lt;code>trippy.sh&lt;/code>&lt;/h3>
&lt;p>&lt;a href="https://github.com/0x4A0D4A/inutility_scripts/blob/main/src/trippy.sh">&lt;code>trippy.sh&lt;/code>&lt;/a> is, in my opinion, by far the least useful script in this repository: it simply writes random data to the user&amp;rsquo;s display in an infinite loop, creating visuals that a hip and cool youngster such as myself&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup> might describe as &amp;ldquo;trippy&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="cp">#!/bin/sh 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># Script to display random data to the screen. To be ran from a TTY, as a user who has write privileges to the fb0 device&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="k">while&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="nb">true&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> cat /dev/urandom &amp;gt; /dev/fb0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It makes use of the &lt;code>/dev/urandom&lt;/code> device as a source of random data which, unlike its sister &lt;code>/dev/random&lt;/code>, never blocks, even if entropy is low, which makes it more suitable for our purposes&lt;sup id="fnref:5">&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref">5&lt;/a>&lt;/sup>.
It also makes use of the &lt;strong>frame buffer device&lt;/strong> &lt;code>/dev/fb0&lt;/code>, which provides an abstraction for the graphics hardware, allowing software to write data to be displayed on a graphics device in a hardware-independent manner;
this is the interface used by the X11 graphics server to control the user&amp;rsquo;s display.&lt;/p>
&lt;p>Running the script requires write privileges to the &lt;code>/dev/fb0&lt;/code> device (typically held only by the root user) and that there is no GUI session that is overriding the frame buffer, like an X11 server.
Therefore, the script cannot be executed from an ordinary terminal window within a graphical session and must instead by run from the TTY.
The GIF below is a screen recording of the script being executed on an Alpine Linux virtual machine in QEMU:&lt;/p>
&lt;figure>
 &lt;a href="./images/trippy.gif">
 &lt;img src="./images/trippy.gif" alt="Animated GIF of the output of ./trippy.sh">
 &lt;/a>&lt;figcaption>Output of ./trippy.sh&lt;/figcaption>&lt;/figure>&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://www.man7.org/linux/man-pages//man1/bash.1.html">&lt;code>bash(1)&lt;/code>&lt;/a> § &lt;span style="font-variant: small-caps">arguments&lt;/span>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>&lt;a href="https://www.man7.org/linux/man-pages//man1/bash.1.html">&lt;code>bash(1)&lt;/code>&lt;/a> § Shell Variables&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>The etymology of the term is forgotten: see &lt;a href="https://en.wikipedia.org/wiki/Inode#Etymology">inode - Wikipedia § Etymology&lt;/a>&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>&amp;ldquo;Hip and cool&amp;rdquo; youngsters are famously known to refer to themselves as such.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5">
&lt;p>On modern systems, the behaviour of &lt;code>/dev/random&lt;/code> and &lt;code>/dev/urandom&lt;/code> are virtually indistinguishable. Historically, &lt;code>/dev/random&lt;/code> would block if there was not sufficient entropy in the system to generate sufficiently random data, whereas &lt;code>/dev/urandom&lt;/code> would re-use its internal state along with a &lt;strong>cryptographically secure pseudo-random number generator (CSPRNG)&lt;/strong> to avoid blocking. In modern systems, however, &lt;code>/dev/random&lt;/code> usually blocks at startup until sufficient entropy has been gathered, and then unblocks permanently, making its behaviour past boot indistinguishable from &lt;code>/dev/urandom&lt;/code>.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>The Perils of `sudo vim`</title><link>https://ohaodha.ie/blog/the-perils-of-sudo-vim/</link><pubDate>Fri, 11 Jul 2025 13:14:53 +0100</pubDate><guid>https://ohaodha.ie/blog/the-perils-of-sudo-vim/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>On a UNIX-like system, one often has the need to edit a write-restricted file and thus to escalate one&amp;rsquo;s privileges to do so;
the obvious approach that might initially occur to you is to use &lt;code>sudo vim&lt;/code> or &lt;code>sudo $EDITOR&lt;/code> to edit the file with superuser privileges, but this is inadvisable for a number of reasons, the majority of which boil down to the &lt;a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege">principle of least privilege&lt;/a>: root privileges are needed only for writing to the file &amp;mdash; there is no need to run the entire editor process as &lt;code>root&lt;/code>.&lt;/p>
&lt;p>Throughout this blog post, I make repeated reference to Vim as it is the most popular terminal text editor, but the points discussed in this blog post are true for any text editor, terminal or otherwise, if being used to edit write-protected files.&lt;/p>
&lt;h2 id="the-risks">The Risks&lt;/h2>
&lt;p>On a single-user system, such as a personal computer, running your text editor as &lt;code>root&lt;/code> isn&amp;rsquo;t a massive problem, and is &lt;em>probably&lt;/em> fine&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> 99.99% of the time;
however, you&amp;rsquo;ll find that running &lt;code>sudo vim&lt;/code> will execute Vim with the default configuration and none of your personalisation specified in your &lt;code>~/&lt;/code> directory, as these configurations are not associated with your &lt;code>root&lt;/code> user.
You can either simply do without these configurations (which I would strongly recommend), or copy your configuration files into your &lt;code>root&lt;/code> user directory (which I would strongly discourage):
if you have any plugins installed that execute in the background as you use your editor, these will be executed as &lt;code>root&lt;/code> also, meaning that you&amp;rsquo;re putting a lot of faith in the authors of these plugins to assume that it&amp;rsquo;s safe to execute their programs as &lt;code>root&lt;/code> on your computer.
The risk here is not just that there may be some malicious code hidden in the plugin:
it&amp;rsquo;s equally possible that a simple bug or mistake in the program, that would ordinarily be of little consequence, could cause catastrophic damage to your system as the error executed with root privileges.&lt;/p>
&lt;p>The real risk, however, associated with &lt;code>sudo vim&lt;/code> is on a multi-user system:
&lt;code>sudo&lt;/code> configuration, in keeping with the principle of least privilege, allows a system administrator to specify certain commands that a user may run with root privileges, while preventing them from running other commands as &lt;code>root&lt;/code>;
therefore, if a user just needs to edit a few write-protected files for configuration purposes, it might be tempting to give them &lt;code>sudo&lt;/code> privileges with which to run the &lt;code>vim&lt;/code> command, but this introduces a massive privilege escalation vulnerability.
Now that you&amp;rsquo;ve given the user the ability to execute &lt;code>vim&lt;/code> as &lt;code>root&lt;/code>, any other process that they spawn from within &lt;code>vim&lt;/code> will also be running as &lt;code>root&lt;/code>:
if they simply enter the Vim command &lt;code>:term&lt;/code> to spawn an interactive shell within Vim, they will then have full, unrestricted access to a &lt;code>root&lt;/code> shell on your system, something which cybersecurity experts describe as &amp;ldquo;very bad&amp;rdquo;.&lt;/p>
&lt;figure>
 &lt;a href="./images/term.png">
 &lt;img src="./images/term.png" alt="The :term command being used to spawn a shell as root within Vim">
 &lt;/a>&lt;figcaption>The &lt;code>:term&lt;/code> command being used to spawn a shell as &lt;code>root&lt;/code> within Vim&lt;/figcaption>&lt;/figure>&lt;h2 id="the-alternative-sudoedit">The Alternative: &lt;code>sudoedit&lt;/code>&lt;/h2>
&lt;p>Fortunately, there is a very simple solution to these problems that I&amp;rsquo;ve raised: &lt;a href="https://www.man7.org/linux/man-pages/man8/sudoedit.8.html">&lt;code>sudoedit&lt;/code>&lt;/a>.
Instead of running your text editor as &lt;code>root&lt;/code>, &lt;code>sudoedit&lt;/code> copies the file that you want to edit to a temporary location and opens that file with your default text editor (usually specified by &lt;code>$EDITOR&lt;/code> or &lt;code>$VISUAL&lt;/code>), with your normal user privileges, and once you have quit and are finished editing, copies the modified file to overwrite the original.
Thus, you can edit write-protected files with all your text editor configurations continuing to work as usual, and you prevent any privilege escalation of the processes running within that editor, and you prevent the users of that editor from executing arbitrary commands as &lt;code>root&lt;/code>.&lt;/p>
&lt;p>Something to be careful of, however, when using &lt;code>sudoedit some_file&lt;/code> (or its alias &lt;code>sudo -e some_file&lt;/code>), is that writing the contents of your buffer (such as entering &lt;code>:w&lt;/code> in Vim) will only write the contents to the temporary file location, and will not apply them to the actual file you wish to edit;
to apply the changes, you must write your changes &lt;em>and&lt;/em> quit the editor (&lt;code>:wq&lt;/code> or &lt;code>ZQ&lt;/code> in Vim);
for example, it has happened to me in the past that I&amp;rsquo;ve been using &lt;code>sudoedit /etc/hosts&lt;/code> and expecting my changes to apply in real-time, but when I&amp;rsquo;ve tested my new configuration, nothing has changed, because I did not close the editor once I was finished making my changes &amp;mdash; my changes had been saved to the temporary file with &lt;code>:w&lt;/code>, but had not been applied to the actual &lt;code>/etc/hosts&lt;/code> file.&lt;/p>
&lt;h2 id="what-if-i-use-doas">What if I use &lt;code>doas&lt;/code>?&lt;/h2>
&lt;p>As a former &lt;a href="https://man.openbsd.org/doas">&lt;code>doas&lt;/code>&lt;/a> contrarian myself (and likely future &lt;code>doas&lt;/code> contrarian when I eventually abandon Arch Linux), I&amp;rsquo;m sympathetic to the plight of the &lt;code>doas&lt;/code> users who, in the true spirit of Free &amp;amp; Open Source Software, make their lives needlessly far more difficult for symbolic purposes, in this case, in the battle against &amp;ldquo;bloat&amp;rdquo;.
&lt;code>doas&lt;/code>, being truly minimalistic, has no such alternative to &lt;code>sudoedit&lt;/code>, but you still shouldn&amp;rsquo;t run &lt;code>doas vim&lt;/code>:
instead, you should perform the same steps that &lt;code>sudoedit&lt;/code> performs yourself, and copy the original file to a temporary location, edit that temporary file, and overwrite the original with the temporary file when you&amp;rsquo;re finished.
There are shell scripts which automate this for you online (&lt;a href="https://github.com/TinfoilSubmarine/doasedit">&lt;code>github.com/TinfoilSubmarine/doasedit&lt;/code>&lt;/a>), but I would recommend against using such options, or making your own:
the reduced attack surface given by the minimalism of &lt;code>doas&lt;/code> is rendered null &amp;amp; void if you start increasing the attack surface with random scripts you find online, and you&amp;rsquo;d be far better off just using &lt;code>sudo&lt;/code> at that point.&lt;/p>
&lt;h2 id="addendum">Addendum&lt;/h2>
&lt;p>After publishing this post, the thought occurred to me that I had neglected to mention what I consider to be the easiest workaround for when you&amp;rsquo;ve opened a file and edited it without root privileges in Vim, and now you cannot save it because it&amp;rsquo;s write-protected.
My advice on the matter may be obvious to some, but I&amp;rsquo;ve found myself taking needlessly complicated workarounds for this issue in the past, and have witnessed others doing similar, so I thought it&amp;rsquo;s best that I share how to quickly solve this problem.&lt;/p>
&lt;figure>
 &lt;a href="./images/readonly.png">
 &lt;img src="./images/readonly.png" alt="Error displayed when a file is readonly in Vim">
 &lt;/a>&lt;figcaption>Error displayed when a file is &lt;code>readonly&lt;/code> in Vim&lt;/figcaption>&lt;/figure>&lt;p>Obvious though it may seem, the best approach to solving this issue, especially if you have just finished making lengthy &amp;amp; complex changes to the file, is to simply enter &lt;code>:w some_temp_file&lt;/code>, where &lt;code>some_temp_file&lt;/code> is just a filepath that you know you have write privileges for, and then quit and run &lt;code>sudo cp some_temp_file original_file&lt;/code> to overwrite the original target file with your new changes.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>I would not be surprised if the words &amp;ldquo;probably fine&amp;rdquo; have preceded every major cybersecurity incident of the past 20 years.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Building an Affiliation Network for Blog Posts &amp; Tags in Hugo</title><link>https://ohaodha.ie/blog/building-an-affiliation-network-for-blog-posts--tags-in-hugo/</link><pubDate>Tue, 10 Jun 2025 21:32:55 +0100</pubDate><guid>https://ohaodha.ie/blog/building-an-affiliation-network-for-blog-posts--tags-in-hugo/</guid><description>&lt;style>
 svg text {
 fill: currentColor;
 }
&lt;/style>
&lt;figure>
 &lt;svg>&lt;/svg>
 &lt;figcaption>An interactive affiliation network built for blog posts &amp; tags on this website&lt;/figcaption>
&lt;/figure>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Hugo websites typically consist of blog posts, which are organised into taxonomies with the use of &lt;em>tags&lt;/em>.
In a typical Hugo website, these tags are listed for each blog post, and a tags page can be used to navigate the blog posts belonging to each tag (although I suspect that tagging blog posts is one of those features that developers implement because they feel that they should, despite the end user probably never using said feature);
clearly, I am one of those developers who has implemented tagging on their blog website despite not being sure that anyone actually uses it, just on the off-chance that someone might one day want to take a look at everything I&amp;rsquo;ve written on a given topic.&lt;/p>
&lt;p>However, I wondered if there was a better way by which I could display these relationships between blog posts and tags in a way that was easier to navigate or simply more visually interesting than listing tags &amp;amp; blog posts, and the knowledge graph in the note-taking application &lt;a href="https://obsidian.md/">Obsidian&lt;/a> immediately came to mind: a visualisation of the interconnections between individual notes in the user&amp;rsquo;s &amp;ldquo;vault&amp;rdquo;, with each node representing a single note and each edge representing a link between that note and another.
While I suspect that this graph visualisation is not actually useful for navigation compared to traditional search methods, it certainly serves its other purpose of &lt;em>looking cool&lt;/em>.&lt;/p>
&lt;figure>
 &lt;a href="./images/Obsidian_desktop_demo_workflow.jpg">
 &lt;img src="./images/Obsidian_desktop_demo_workflow.jpg" alt="Obsidian &amp;amp; its graph view">
 &lt;/a>&lt;figcaption>Obsidian &amp;amp; its graph view &lt;a href="https://commons.wikimedia.org/wiki/File:Obsidian_desktop_demo_workflow.jpg">[WikiMedia Commons]&lt;/a>&lt;/figcaption>&lt;/figure>&lt;p>Such a graph is not particularly suitable for my application as this website doesn&amp;rsquo;t have a great deal of linking in-between posts; in fact, at time of writing, I don&amp;rsquo;t believe there are any cross-links between posts on this website.
Instead, posts are tagged, and so the nature of the graph constructed on the data is very different:
the Obsidian graph view is a &lt;strong>simple&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> directed graph&lt;/strong>, with edges indicating the direction of the link from one note to another.
In contrast, to construct a meaningful graph of connections between posts on this website, an &lt;strong>affiliation network&lt;/strong> would be the best choice: a simple&lt;sup id="fnref1:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, undirected, &lt;em>bipartite&lt;/em> graph wherein there are two distinct types of nodes representing two distinct kinds of entities, typically individuals &amp;amp; groups, and in this case, blog posts &amp;amp; tags.
A &lt;strong>bipartite graph&lt;/strong> is a graph $G=(X,E)$ consisting of a node set $X$ and an edge set &lt;em>E&lt;/em>, where $X$ can be divided into two disjoint subsets $X_1$ &amp;amp; $X_2$ such that:&lt;/p>
&lt;ul>
&lt;li>$X_1 \cap X_2 = \emptyset$: that is, the two subsets are completely disjoint and share no nodes between them.&lt;/li>
&lt;li>$X_1 \cup X_2 = X$: that is, each node in $X$ is in either $X_1$ or $X_2$ &amp;mdash; there are no missing nodes.&lt;/li>
&lt;li>$X_1$ and $X_2$ are both &lt;strong>independent sets&lt;/strong>: no pair of nodes in either $X_1$ or $X_2$ are connected by an edge &amp;mdash; any edges within the graph connect a node in $X_1$ to a node in $X_2$ (and vice-versa, as the edges are undirected), never a node in $X_1$ to another node in $X_1$, or a node in $X_2$ to another node in $X_2$.&lt;/li>
&lt;/ul>
&lt;h2 id="building-the-graph">Building the Graph&lt;/h2>
&lt;p>Since this affiliation network will be part of the website and displayed on the &lt;a href="https://ohaodha.ie/tags">Tags&lt;/a> page going forward, it will almost certainly be updated repeatedly as time goes on, so the version of the code referred to here will be the first working version of the code published, and will not refer to subsequent versions.
Furthermore, I imagine the code may need to be tweaked or refactored as the size of the graph grows, so the graph data used for this post will be static from the time of writing:
the data displayed in the graph at the top of this page is not updated with subsequent builds of the website, unlike the graph displayed on the &lt;a href="https://ohaodha.ie/tags">Tags&lt;/a> page, which is updated with each build.&lt;/p>
&lt;p>To display an affiliation graph of the relationships between tags &amp;amp; blog posts on this website, it was first necessary to obtain a representation of this graph in a machine-readable format.
When a website is built in Hugo, HTML files are produced, with the HTML file for each blog post typically containing the tags associated with that blog post, a HTML file for each tag being generated which links to all the blog posts which belong to that taxonomy, and a tags page being generated which lists all the tags used on the website.
I chose to use a Python script using the &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/">Beautiful Soup&lt;/a> HTML parsing library to extract the tags from each blog post on the website:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">list_blog_posts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns a list of all the blog post slugs generated by Hugo in the build process, i.e.,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="s2"> not the titles of blog posts but the string used to identify the blog post in its URL.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_directory (str): the path to the /public/blog sub-directory of the Hugo project.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="s2"> list: a list of the blog post slugs in the blog_directory, in str form.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span> &lt;span class="n">directory&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">directory&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">listdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isdir&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">directory&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extract_title&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="s2"> Extracts the title of the given blog post from its HTML.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_post (str): the blog post slug in the blog_directory.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_directory (str): the path to the /public/blog sub-directory of the Hugo project.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="s2"> str: the title of the blog post.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">blog_post&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;/index.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl"> &lt;span class="n">soup&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BeautifulSoup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;html.parser&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="n">title_element&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">soup&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;blogpost-title&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">title_element&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get_text&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">41&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">extract_tags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">42&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">43&lt;/span>&lt;span class="cl">&lt;span class="s2"> Extracts the tags of the given blog post from its HTML.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">44&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">45&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">46&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_post (str): the blog post slug in the blog_directory.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">47&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_directory (str): the path to the /public/blog sub-directory of the Hugo project.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">48&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">49&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">50&lt;/span>&lt;span class="cl">&lt;span class="s2"> list: a list of str tags.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">51&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">52&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">53&lt;/span>&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">blog_post&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;/index.html&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">54&lt;/span>&lt;span class="cl"> &lt;span class="n">soup&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BeautifulSoup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;html.parser&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">55&lt;/span>&lt;span class="cl"> &lt;span class="n">tags_div&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">soup&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;div&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;tags&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">56&lt;/span>&lt;span class="cl"> &lt;span class="n">tag_links&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tags_div&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find_all&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">57&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">child&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">lower&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">child&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">contents&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">tag_links&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I then used the &lt;code>networkx&lt;/code> package to build the graph in Python, which can then be written to a JSON file using &lt;code>json.dump()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">60&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">build_affiliation_network&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_posts&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">61&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">62&lt;/span>&lt;span class="cl">&lt;span class="s2"> Builds an affiliation network consisting of a bipartite node set of blog post
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="s2"> nodes &amp;amp; tag nodes, with an edge between two nodes if the blog post has that tag.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">&lt;span class="s2"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_posts (list): a list of the blog post slugs in the blog_directory, in str form.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">67&lt;/span>&lt;span class="cl">&lt;span class="s2"> blog_directory (str): the path to the /public/blog sub-directory of the Hugo project.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">68&lt;/span>&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">69&lt;/span>&lt;span class="cl">&lt;span class="s2"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">70&lt;/span>&lt;span class="cl">&lt;span class="s2"> networkx.classes.graph.Graph: a networkx graph object representing the affiliation networka
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">71&lt;/span>&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">72&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">73&lt;/span>&lt;span class="cl"> &lt;span class="n">graph&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Graph&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">74&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">75&lt;/span>&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">blog_post&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">blog_posts&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">76&lt;/span>&lt;span class="cl"> &lt;span class="n">title&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extract_title&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">77&lt;/span>&lt;span class="cl"> &lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">78&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">79&lt;/span>&lt;span class="cl"> &lt;span class="n">tags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">extract_tags&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">80&lt;/span>&lt;span class="cl"> &lt;span class="n">graph&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_edges_from&lt;/span>&lt;span class="p">([&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">blog_post&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tag&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">tag&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">tags&lt;/span> &lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">81&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">82&lt;/span>&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">graph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">83&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">84&lt;/span>&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">85&lt;/span>&lt;span class="cl"> &lt;span class="n">blog_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./public/blog/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">86&lt;/span>&lt;span class="cl"> &lt;span class="n">blog_posts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">list_blog_posts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_directory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">87&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">88&lt;/span>&lt;span class="cl"> &lt;span class="n">affiliation_network&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">build_affiliation_network&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">blog_posts&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blog_directory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">89&lt;/span>&lt;span class="cl"> &lt;span class="n">graph_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">node_link_data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">affiliation_network&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">90&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">91&lt;/span>&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./public/blog/graph.json&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">92&lt;/span>&lt;span class="cl"> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">graph_data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A (prettified) sample of the output JSON is as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;directed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;multigraph&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;graph&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;nodes&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;article&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;What Does unlink Actually Do?&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;what-does-unlink-actually-do&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;linux&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;shell&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;links&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;article&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;what-does-unlink-actually-do&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;what-does-unlink-actually-do&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;linux&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;source&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;what-does-unlink-actually-do&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;shell&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The blog post nodes can be implicitly distinguished from the tags nodes on the basis that the blog post nodes will have an associated &lt;code>title&lt;/code> attribute whereas the tag nodes will not;
a more robust &amp;amp; generalisable solution could be to tag each node with the values &lt;code>&amp;quot;blogpost&amp;quot;&lt;/code> or &lt;code>&amp;quot;tag&amp;quot;&lt;/code> as appropriate, but is not necessary for our purposes.&lt;/p>
&lt;p>The Python script to build this affiliation network and create the associated JSON file is run automatically as part of the GitHub Action that I use to build &amp;amp; deploy this website, using the following additional steps in the configuration of the &lt;code>build&lt;/code> job:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">63&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install Python dependencies&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">64&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pip install -r scripts/requirements.txt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">65&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Generate affiliation network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">66&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python3 scripts/graph.py&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="displaying-the-graph">Displaying the Graph&lt;/h2>
&lt;p>I chose to use &lt;a href="https://d3js.org/">D3.js&lt;/a> to create the graph, chosen for its flexibility &amp;amp; interactive nature, using the &lt;a href="https://d3js.org/d3-force">&lt;code>d3-force&lt;/code>&lt;/a> layout and largely based upon the &lt;a href="https://observablehq.com/@d3/disjoint-force-directed-graph/2">disjoint force-directed graph&lt;/a> example available in the documentation.&lt;/p>
&lt;p>The JavaScript for the graph itself is included directly in the &lt;code>content/tags/_index.md&lt;/code> Hugo file for this website (and also here within this page).
The nodes of the graph are colour-coded, with &lt;span style="color: #B0CFFF">■&lt;/span>&lt;code>#B0CFFF&lt;/code> blue representing blog post nodes and &lt;span style="color: #FFA7CE">■&lt;/span>&lt;code>#FFA7CE&lt;/code> pink representing tag nodes.
Each node is labelled, with blog post nodes displaying the title of the blog post which they represent in a serif font, and tag nodes displaying the name of the tag prepended with a &lt;code>#&lt;/code> symbol in a monospace font;
the labels for both types of node are clickable links either to the blog post itself or the page listing all the posts associated with the tag.&lt;/p>
&lt;p>This project has been my first foray into data visualisation with D3.js, and while I am pleased with the result, it&amp;rsquo;s nonetheless quite likely that my code contains a number of rookie mistakes, so if you find any, please do let me know!
The HTML &amp;amp; JavaScript for the visualisation itself can be seen below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">svg&lt;/span> &lt;span class="nt">text&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="n">fill&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">currentColor&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">style&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;module&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">as&lt;/span> &lt;span class="nx">d3&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;https://cdn.jsdelivr.net/npm/d3@7/+esm&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 11&lt;/span>&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">drawChart&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 12&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./files/graph.json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 13&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 15&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">width&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 16&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">height&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">600&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 17&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">color&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">scaleOrdinal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">schemeCategory10&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 19&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">links&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">links&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 20&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">nodes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="p">...&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 21&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 22&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">simulation&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceSimulation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 23&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">force&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;link&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceLink&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">links&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">distance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 24&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">force&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;charge&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceManyBody&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">strength&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 25&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">force&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;x&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceX&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 26&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">force&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;y&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceY&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 27&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 28&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">svg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;svg&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 29&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;width&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">width&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 30&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;height&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">height&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 31&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;viewBox&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">width&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="nx">height&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">width&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">height&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 32&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;style&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;max-width: 100%; height: auto;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 33&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 34&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">link&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 35&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;stroke&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;#999&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 36&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;stroke-opacity&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 37&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;line&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 38&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">links&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 39&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;line&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 40&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;stroke-width&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sqrt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 41&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 42&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">node&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 43&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;stroke&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;#fff&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 44&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;stroke-width&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 45&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;circle&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 46&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 47&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;circle&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 48&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mi">12&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 49&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;fill&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;#B0CFFF&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;#FFA7CE&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 50&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">call&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">drag&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 51&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;start&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragstarted&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 52&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;drag&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragged&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 53&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;end&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">dragended&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 54&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 55&lt;/span>&lt;span class="cl"> &lt;span class="nx">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 56&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 57&lt;/span>&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;g&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 58&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;text-anchor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;middle&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 59&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">selectAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 60&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">nodes&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 61&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 62&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;xlink:href&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="sb">`/blog/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="sb">`/tags/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 63&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;_blank&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// Open in new tab
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 64&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">.&lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 65&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;font-family&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;inherit&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;monospace&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 66&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;font-size&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="mi">16&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 67&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">title&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;#&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 68&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 69&lt;/span>&lt;span class="cl"> &lt;span class="nx">simulation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 70&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">force&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;labelCollision&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forceCollide&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 71&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">radius&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 72&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">strength&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.5&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 73&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 74&lt;/span>&lt;span class="cl"> &lt;span class="nx">simulation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;tick&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 75&lt;/span>&lt;span class="cl"> &lt;span class="nx">link&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 76&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;x1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">source&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 77&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;y1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">source&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 78&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;x2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 79&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;y2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 80&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 81&lt;/span>&lt;span class="cl"> &lt;span class="nx">node&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 82&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 83&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cy&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 84&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 85&lt;/span>&lt;span class="cl"> &lt;span class="nx">label&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 86&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;x&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 87&lt;/span>&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">attr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;y&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">d&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 88&lt;/span>&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 89&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 90&lt;/span>&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">dragstarted&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 91&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">active&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">simulation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">alphaTarget&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">restart&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 92&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 93&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 94&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 95&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 96&lt;/span>&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">dragged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 97&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 98&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 99&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">100&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">101&lt;/span>&lt;span class="cl"> &lt;span class="kd">function&lt;/span> &lt;span class="nx">dragended&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">102&lt;/span>&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">active&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nx">simulation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">alphaTarget&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">103&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">104&lt;/span>&lt;span class="cl"> &lt;span class="nx">event&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">subject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">fy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">105&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">106&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">107&lt;/span>&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">querySelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;svg&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">replaceWith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">svg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">node&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">108&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">109&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">110&lt;/span>&lt;span class="cl">&lt;span class="nx">drawChart&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">111&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";

async function drawChart() {
 const response = await fetch("./files/graph.json");
 const data = await response.json();

 const width = 1000;
 const height = 600;
 const color = d3.scaleOrdinal(d3.schemeCategory10);

 const links = data.links.map(d => ({ ...d }));
 const nodes = data.nodes.map(d => ({ ...d }));

 const simulation = d3.forceSimulation(nodes)
 .force("link", d3.forceLink(links).id(d => d.id).distance(100))
 .force("charge", d3.forceManyBody().strength(-500))
 .force("x", d3.forceX())
 .force("y", d3.forceY());

 const svg = d3.create("svg")
 .attr("width", width)
 .attr("height", height)
 .attr("viewBox", [-width / 2, -height / 2, width, height])
 .attr("style", "max-width: 100%; height: auto;");

 const link = svg.append("g")
 .attr("stroke", "#999")
 .attr("stroke-opacity", 0.6)
 .selectAll("line")
 .data(links)
 .join("line")
 .attr("stroke-width", d => Math.sqrt(d.value || 1));

 const node = svg.append("g")
 .attr("stroke", "#fff")
 .attr("stroke-width", 1.5)
 .selectAll("circle")
 .data(nodes)
 .join("circle")
 .attr("r", d => d.title ? 12 : 8)
 .attr("fill", d => d.title ? "#B0CFFF" : "#FFA7CE")
 .call(d3.drag()
 .on("start", dragstarted)
 .on("drag", dragged)
 .on("end", dragended));

 node.append("title").text(d => d.id);

 const label = svg.append("g")
 .attr("text-anchor", "middle")
 .selectAll("text")
 .data(nodes)
 .join("a")
 .attr("xlink:href", d => d.title ? `/blog/${d.id}` : `/tags/${d.id}`)
 .attr("target", "_blank") // Open in new tab
 .append("text")
 .attr("font-family", d => d.title ? "inherit" : "monospace")
 .attr("font-size", d => d.title ? 16 : 12)
 .text(d => d.title ? d.title : "#" + d.id);

 simulation
 .force("labelCollision", d3.forceCollide()
 .radius(20)
 .strength(0.5));

 simulation.on("tick", () => {
 link
 .attr("x1", d => d.source.x)
 .attr("y1", d => d.source.y)
 .attr("x2", d => d.target.x)
 .attr("y2", d => d.target.y);

 node
 .attr("cx", d => d.x)
 .attr("cy", d => d.y);

 label
 .attr("x", d => d.x)
 .attr("y", d => d.y - 12);
 });

 function dragstarted(event) {
 if (!event.active) simulation.alphaTarget(0.3).restart();
 event.subject.fx = event.subject.x;
 event.subject.fy = event.subject.y;
 }

 function dragged(event) {
 event.subject.fx = event.x;
 event.subject.fy = event.y;
 }

 function dragended(event) {
 if (!event.active) simulation.alphaTarget(0);
 event.subject.fx = null;
 event.subject.fy = null;
 }

 document.querySelector("svg").replaceWith(svg.node());
}

drawChart();
&lt;/script>


&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Here, the word &amp;ldquo;simple&amp;rdquo; doesn&amp;rsquo;t refer to the complexity of the graph or the difficulty of constructing such a graph, but is a technical term that means that the graph cannot contain edges from a node to itself, and there cannot be multiple edges between the same pair of nodes; of course, in Obsidian it is possible to have several links between a pair of notes, but these are represented in the graph with a single edge, and similarly self-referential links are not displayed.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>¶, §, &amp; 🔗</title><link>https://ohaodha.ie/blog/---/</link><pubDate>Wed, 09 Apr 2025 22:28:58 +0100</pubDate><guid>https://ohaodha.ie/blog/---/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Anchor links are hyperlinks that link to a different section within the current webpage or document, rather than to an external webpage, thus allowing for links to be shared to specific sections of a webpage and for a table of contents with clickable links to be created for a webpage.
Anchor links are probably most commonly found in online technical documentation, but can be seen in many other webpages, including this one:
hovering over a heading within this blog post will make a clickable anchor link appear, the value of which can be copied for sharing or other uses.
Ordinarily, I would not use any additional sub-headings for a blog post of such short length, but I have shoe-horned some in here just for the sake of demonstration.&lt;/p>
&lt;p>However, one may notice that the symbol that I use to represent the anchor links is somewhat unusual:
this website uses § to denote anchor links, whereas it&amp;rsquo;s more common to see ¶ or 🔗 used.
In this blog post, I wish to argue for why I think that the § is in fact the best symbol to use for anchor links, why I think that the ¶ is frankly incorrect to use for anchor links despite its popularity, and I&amp;rsquo;ll also briefly discuss the use of 🔗, although I can&amp;rsquo;t claim to really have a strong opinion on its usage.&lt;/p>
&lt;h2 id="symbols">Symbols&lt;/h2>
&lt;h3 id="heading">¶&lt;/h3>
&lt;p>¶ (referred to as the &lt;em>pilcrow&lt;/em>, &lt;em>paragraph mark/sign/symbol&lt;/em>, &lt;em>paraph&lt;/em>, or &lt;em>blind P&lt;/em>) is a glyph traditionally used to identify a &lt;strong>paragraph&lt;/strong>.
It is also commonly used to denote anchor links, around equally as often as 🔗 in my experience, although in more formal contexts.
For example, the ¶ can be seen being used to represent anchor links in GNU documentation, such as in the &lt;a href="https://gcc.gnu.org/onlinedocs/">GCC Online Documentation&lt;/a>.
However, I would submit that this is an incorrect usage of the ¶:
the purpose of the ¶ is to refer to or denote paragraphs within a body of text, whereas anchor links are typically done on a per-heading basis.
If the anchor link were being used to link to a specific &lt;em>paragraph&lt;/em>, then I would say that the ¶ is the correct symbol to use, but more often than not the anchor link is linking to a &lt;em>section&lt;/em>, and therefore the paragraph mark is inappropriate.&lt;/p>
&lt;h3 id="heading-1">§&lt;/h3>
&lt;p>§ (referred to as the &lt;em>silcrow&lt;/em>, &lt;em>section mark/sign/symbol&lt;/em>, or &lt;em>double-S&lt;/em>) is a glyph traditionally used to identify a &lt;strong>section&lt;/strong>.
It is most commonly seen in legal contexts being used to refer to a section of law.
An example might look something like the following:&lt;/p>
&lt;blockquote>
&lt;p>under §12 of the Licensing Act of 1872, it is a criminal offence under Irish law to be drunk while in charge of a cow&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.&lt;/p>
&lt;/blockquote>
&lt;p>This is the symbol that is used for anchor links on this website, and it is my opinion that it is far more suitable than the ¶.
Anchor links are used for sections, and therefore the section sign should be used, not the paragraph sign.
Moreover, if you use the ¶ to reference sections of your webpage, how could one reference a specific paragraph of your webpage?
Using the § allows for both sections and paragraphs to be referred to, with an identifiable symbol for each.&lt;/p>
&lt;p>I am not aware of any other websites beyond my own that use the § for anchor links, but I&amp;rsquo;m sure they exist, and hopefully more websites will start to make use of the § in the future, as it&amp;rsquo;s both more typographically correct and allows for more flexible creation of anchor links, as you can save the ¶ for referencing specific paragraphs if needs be.&lt;/p>
&lt;h3 id="heading-2">🔗&lt;/h3>
&lt;p>The 🔗 emoji can be seen increasingly often online to denote anchor links, such as in the &lt;a href="https://docs.github.com/en/get-started/start-your-journey/about-github-and-git">GitHub Documentation&lt;/a>.
While I prefer the more traditional choice of §, the use of 🔗 makes sense for webpages that want to appear modern or semi-formal, and is likely more readily understood by the uninitiated, and is therefore a perfectly fine choice in my opinion.
Unlike the ¶, I have no objections to the usage of the 🔗, but I do prefer the § on the whole.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Genuinely real, and still technically in effect: &lt;a href="https://www.irishstatutebook.ie/eli/1872/act/94/section/12/enacted/en/html">https://www.irishstatutebook.ie/eli/1872/act/94/section/12/enacted/en/html&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>What Does `unlink` Actually Do?</title><link>https://ohaodha.ie/blog/what-does-unlink-actually-do/</link><pubDate>Tue, 08 Apr 2025 00:57:13 +0100</pubDate><guid>https://ohaodha.ie/blog/what-does-unlink-actually-do/</guid><description>&lt;p>Although I now forget the context, a friend once sent me a snippet of shell code that he was using to remove &lt;a href="https://www.man7.org/linux/man-pages/man1/ln.1.html">symbolic links&lt;/a> to files on his computer, using a shell command that I had never seen before: &lt;a href="https://www.man7.org/linux/man-pages/man1/unlink.1.html">&lt;code>unlink&lt;/code>&lt;/a>.
I, like him (and presumably like you if this is your first exposure to the command), assumed that this command is used to remove links to files on a UNIX-like system.
This is quite an understandable assumption, as the name of the command quite literally implies that it undoes links, but more importantly because &lt;em>it works&lt;/em>;
if you run &lt;code>unlink path_to_some_link&lt;/code> on a link to a file (symbolic or otherwise), the link will be removed.
It seems to me that quite a lot of people use &lt;code>unlink&lt;/code> to remove symbolic links to files, and it works perfectly well despite the command not being the link-remover that it appears to be at first glance.&lt;/p>
&lt;p>In reality, &lt;code>unlink&lt;/code> is not a link-remover (in that sense):
instead, it is an interface to your system&amp;rsquo;s &lt;a href="https://www.gnu.org/software/libc/manual/html_node/Deleting-Files.html#Deleting-Files">&lt;code>unlink&lt;/code> function&lt;/a>, which is how files are deleted &amp;ldquo;under the hood&amp;rdquo; on UNIX-like systems.
This is why it works perfectly well for deleting links on your computer: it deletes &lt;em>any&lt;/em> file it&amp;rsquo;s directed to, regardless of whether or not it&amp;rsquo;s a link to another file.
Strictly speaking, the &lt;code>unlink&lt;/code> command (like the &lt;code>rm&lt;/code> command) removes a name for a file in the filesystem, and if that name is the last remaining name for the file in that filesystem, the file will be deleted entirely;
if there are other names for the file in the filesystem (if, say, a hard link has been created), then the file will remain in the filesystem until all of its names are removed or &lt;code>unlink&lt;/code>ed.&lt;/p>
&lt;p>The &lt;code>unlink&lt;/code> syscall is how the &lt;code>rm&lt;/code> command works under the hood:
in this sense, the &lt;code>rm&lt;/code> and &lt;code>unlink&lt;/code> commands do exactly the same thing, just with slightly different interfaces.
So, while it&amp;rsquo;s not wrong to delete links to files using the &lt;code>unlink&lt;/code> command, it &lt;em>is&lt;/em> wrong to think that &lt;code>unlink&lt;/code> is necessary to delete links to files or that it&amp;rsquo;s the only correct way to delete links to files.
The only difference between the two commands is the interface: &lt;code>rm&lt;/code> has more options and can delete multiple files at a time, whereas &lt;code>unlink&lt;/code> just deletes a single file.&lt;/p></description></item><item><title>Tor as a Tool for Testing Fonts</title><link>https://ohaodha.ie/blog/tor-as-a-tool-for-testing-fonts/</link><pubDate>Thu, 13 Feb 2025 00:00:00 +0000</pubDate><guid>https://ohaodha.ie/blog/tor-as-a-tool-for-testing-fonts/</guid><description>&lt;p>A concern that may occur to you when you are designing a website is whether or not the font that you are using is &amp;ldquo;web-safe&amp;rdquo;, meaning that the user will be able to render it on their end when they load your webpage because they already have that font on their device.
Web-safe fonts include the typical well-known fonts, like Times New Roman, Arial, etc.
If you are using non-web-safe fonts on your website, typically you will either define a fallback font in the CSS that is used if the user does not have the font in question and/or supply the user with the font using some kind of web font delivery service like &lt;a href="https://fonts.google.com/">Google Fonts&lt;/a> or &lt;a href="https://www.fontsquirrel.com/">Font Squirrel&lt;/a> or by packaging the font directly with the website.&lt;/p>
&lt;p>I am, at present, using two different non-web-safe fonts for this website that I must supply the user with:&lt;/p>
&lt;ul>
&lt;li>&lt;span style="font-family: EB Garamond">EB Garamond&lt;/span>, which I supply using a font delivery service;&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/li>
&lt;li>&lt;span style="font-family: Gadelica">Gadelica&lt;/span>, which I supply the font files for directly bundled with the website.&lt;/li>
&lt;/ul>
&lt;p>Testing whether or not these fonts are being distributed correctly can be difficult, as I already have these fonts installed on my system, so they will render correctly even if the website is not distributing them correctly to the user.
There are a couple different ways to test this, such as using a different device, but one way that I&amp;rsquo;ve found to be particularly convenient is to use the &lt;a href="https://www.torproject.org/download/">Tor Browser&lt;/a>.&lt;/p>
&lt;p>The Tor Browser is a browser that intends to offer a level of anonymity and security when browsing the Internet.
The main way in which it provides this is by routing the user&amp;rsquo;s traffic through a number of different nodes to obfuscate where the user&amp;rsquo;s traffic is coming from, but it also employs a number of anti-browser-fingerprinting techniques to prevent the user&amp;rsquo;s computer from being recognised, including managing the window dimensions and &lt;em>sandboxing the user&amp;rsquo;s fonts&lt;/em>;
because a computer could be potentially identified by the unique combination of fonts installed on the machine, a malicious website could &lt;em>fingerprint&lt;/em> or identify a user based on the fonts available to them.
Therefore, the Tor Browser comes with a number of generic pre-bundled fonts and does not have access to any of your system fonts, making it harder to fingerprint you &lt;em>and&lt;/em>, incidentally, working as a handy tool for checking how web-safe a font is.&lt;/p>
&lt;p>If you open your website in the Tor Browser, and the fonts render as expected, you know the fonts are being supplied correctly; if the fonts do not render correctly, you not that they are not being supplied correctly.
I personally use this test all the time when setting up a website, as it&amp;rsquo;s one of the handiest and simplest ways I know to test such a thing.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>This is no longer true as of 2025-04-12: EB Garamond is now also directly bundled with the site, both in an effort to de-Google and as the original EB Garamond has superior font features to that offered on Google Fonts.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>To Whom (or Who?) It May Concern</title><link>https://ohaodha.ie/blog/to-whom-or-who-it-may-concern/</link><pubDate>Fri, 27 Dec 2024 03:06:46 +0000</pubDate><guid>https://ohaodha.ie/blog/to-whom-or-who-it-may-concern/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>First of all, I&amp;rsquo;m not trying to tell you how to speak; how you speak is entirely up to yourself.
Second of all, I&amp;rsquo;m not advising you on how to be pedantic and &amp;ldquo;correct&amp;rdquo; people who didn&amp;rsquo;t ask for correction over minor grammatical matters;
if you understood their meaning, there&amp;rsquo;s no need to be rude in a sad attempt to one-up them.
The purpose of this blog post is firstly to explain when it is correct to use the word &lt;em>whom&lt;/em> instead of &lt;em>who&lt;/em> for those looking to inject a little formal precision into their speech, and why sometimes &lt;em>whom&lt;/em> sounds wrong even when it ought to be correct.&lt;/p>
&lt;h2 id="who-versus-whom">&amp;ldquo;Who&amp;rdquo; versus &amp;ldquo;Whom&amp;rdquo;&lt;/h2>
&lt;p>I won&amp;rsquo;t spend too much metaphorical ink here going into detail on the difference between &lt;em>who&lt;/em> and &lt;em>whom&lt;/em>, as this has been explained countless times by countless people before me, and you could likely find a better definition by googling.
In modern usage, for better or for worse, &lt;em>who&lt;/em> is invariably considered to be correct under all circumstances, so it&amp;rsquo;s a good bet to stick with if you&amp;rsquo;re unsure, and you look sillier using &lt;em>whom&lt;/em> when you should&amp;rsquo;ve used &lt;em>who&lt;/em> than using &lt;em>who&lt;/em> when you should use &lt;em>whom&lt;/em>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.
Furthermore, &lt;em>whom&lt;/em> can be jarring or viewed as pretentious by some audiences, so you ought to consider the context of your usage carefully.&lt;/p>
&lt;p>The difference between the words comes down to the fact that &lt;em>who&lt;/em> is the interrogative pronoun for the &lt;strong>subject&lt;/strong> of a sentence and &lt;em>whom&lt;/em> is the interrogative pronoun for the &lt;strong>object&lt;/strong> of a sentence:&lt;/p>
&lt;ul>
&lt;li>The &lt;strong>object&lt;/strong> of a sentence is the actor or doer in the sentence; it is the person &lt;strong>who&lt;/strong> performs an action in the sentence. The interrogative pronoun for the object of a sentence is &lt;em>who&lt;/em>.&lt;/li>
&lt;li>The &lt;strong>subject&lt;/strong> of a sentence is the recipient of the object&amp;rsquo;s action; it is the person to &lt;strong>whom&lt;/strong> the action in the sentence is performed. The interrogative pronoun for the subject of a sentence is &lt;em>whom&lt;/em>.&lt;/li>
&lt;/ul>
&lt;p>In my opinion, the easiest way to decide which is correct to use in a sentence is by asking yourself whether the interrogative pronoun acts as a stand-in for the word &lt;em>he&lt;/em> or the word &lt;em>him&lt;/em>: if it&amp;rsquo;s a stand-in for &lt;em>he&lt;/em>, you should use &lt;em>who&lt;/em> and if it&amp;rsquo;s a stand-in for &lt;em>him&lt;/em> you should use &lt;em>whom&lt;/em>.
I remember this as &lt;em>who&lt;/em> sounds most like &lt;em>he&lt;/em> and &lt;em>whom&lt;/em> sounds most like &lt;em>him&lt;/em>.&lt;/p>
&lt;h2 id="ive-swapped-who-for-whom-why-does-it-still-sound-wrong">I&amp;rsquo;ve Swapped &lt;em>Who&lt;/em> for &lt;em>Whom&lt;/em>: Why Does it Still Sound Wrong?&lt;/h2>
&lt;p>Something that confused me greatly when I first learned the difference between &lt;em>who&lt;/em> and &lt;em>whom&lt;/em> was the fact that some sentences sounded even less correct after swapping out &lt;em>who&lt;/em> for &lt;em>whom&lt;/em> than they did before.
I initially chalked this up to the word sounding jarring or unnatural due to its declining popularity, but this couldn&amp;rsquo;t have been the case because &lt;em>whom&lt;/em> usually sounded more natural to me.
Examples of sentences which sounded less correct to me after swapping &lt;em>who&lt;/em> for &lt;em>whom&lt;/em> include:&lt;/p>
&lt;ul>
&lt;li>&lt;em>Who did you give the gift to?&lt;/em> → &lt;em>Whom did you give the gift to?&lt;/em>&lt;/li>
&lt;li>&lt;em>Who did you meet with?&lt;/em> → &lt;em>Whom did you meet with?&lt;/em>&lt;/li>
&lt;li>&lt;em>Who did you do that for?&lt;/em> → &lt;em>Whom did you do that for?&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>I eventually realised why these sentences sounded so wrong to me despite seemingly being more technically correct: they end with a &lt;strong>preposition&lt;/strong>.&lt;/p>
&lt;h3 id="dangling-prepositions">Dangling Prepositions&lt;/h3>
&lt;p>Ending a sentence with a preposition (a word that indicates the relationship between a noun and another part of the sentence, such as &lt;em>to&lt;/em>, &lt;em>with&lt;/em>, &lt;em>for&lt;/em>) is typically discouraged in formal English.
While it is not generally considered to be incorrect to end a sentence with a preposition, a sentence typically sounds more &amp;ldquo;proper&amp;rdquo; if dangling prepositions are avoided.
For example, contrast the sentences &amp;ldquo;&lt;em>What did you do that for?&lt;/em>&amp;rdquo; and &amp;ldquo;&lt;em>For what reason did you do that?&lt;/em>&amp;rdquo;: both sentences are perfectly valid, but the latter has a more formal air to it.
From around the 18&lt;sup>th&lt;/sup> century through to the 20&lt;sup>th&lt;/sup> century, the belief was generally held that it is always incorrect to end a sentence with a preposition in English, which greatly exacerbated the effect of dangling prepositions sounding less proper;
in the present day, however, you would be hard-pressed to find an academic who holds this belief, and it&amp;rsquo;s generally considered absolutely fine to end sentences with prepositions whenever one sees fit.&lt;/p>
&lt;p>I believe that the reason why those sentences in which &lt;em>who&lt;/em> was replaced with &lt;em>whom&lt;/em> suddenly sounded more incorrect above is the mixing of levels of formality: on the one hand, the speaker is being unusually formal using the word &lt;em>whom&lt;/em> but on the other hand they are not speaking in the traditionally formal way of avoiding dangling prepositions, resulting in the sentence falling into a sort of &amp;ldquo;uncanny valley&amp;rdquo; that makes it sound even less correct.
Therefore, I would recommend that you avoid dangling prepositions in the event that you decide to make use of the word &lt;em>whom&lt;/em>, as it can make the whole sentence sound off.
The previously given example sentences can be modified to sound more correct by re-arranging them to avoid dangling prepositions:&lt;/p>
&lt;ul>
&lt;li>&lt;em>Who did you give the gift to?&lt;/em> → &lt;em>Whom did you give the gift to?&lt;/em> → &lt;em>To whom did you give the gift?&lt;/em>&lt;/li>
&lt;li>&lt;em>Who did you meet with?&lt;/em> → &lt;em>Whom did you meet with?&lt;/em> → &lt;em>With whom did you meet?&lt;/em>&lt;/li>
&lt;li>&lt;em>Who did you do that for?&lt;/em> → &lt;em>Whom did you do that for?&lt;/em> → &lt;em>For whom did you do that?&lt;/em>&lt;/li>
&lt;/ul>
&lt;h2 id="afterword">Afterword&lt;/h2>
&lt;p>I originally thought that it would be amusing to title this blogpost using a regular expression to represent the optional presence of the letter &lt;em>m&lt;/em> in the sentence &amp;ldquo;To Whom It May Concern&amp;rdquo;, that is: &amp;ldquo;To &lt;code>/Whom?/&lt;/code> it May Concern&amp;rdquo;.
I decided against this, however, primarily because I didn&amp;rsquo;t think it looked as cool as I imagined it would when the idea first occurred to me, as it&amp;rsquo;s really quite a boring regular expression.
Furthermore, the relative simplicity of the regular expression made it so that it was not immediately recognisable as &amp;ldquo;code&amp;rdquo; to a one unfamiliar with regular expressions, and thus could lead to confusion: it may just appear that I included a question mark to indicate confusion, or perhaps someone may have imagined that the text with the forward slashes denoted a pronunciation written in the International Phonetic Alphabet, and the question mark to indicate a glottal stop.
Nevertheless, even though I thought that idea unsuitable for the title of the post, I thought that it was just barely interesting enough to make it worth mentioning in the afterword of this post.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>The only exception to my staunch belief that it is never acceptable in polite conversation to correct someone else&amp;rsquo;s grammar without their prior approval is when someone incorrectly uses the word &lt;em>whom&lt;/em>. If you&amp;rsquo;re using the word &lt;em>whom&lt;/em>, you&amp;rsquo;re probably taking things a bit too seriously and have high ideas about yourself, so a bit of correction when you use the word wrong to bring you down a peg or two will do you no harm.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Moaning About the Typography of Irish Text on Train Station Signs</title><link>https://ohaodha.ie/blog/moaning-about-the-typography-of-irish-text-on-train-station-signs/</link><pubDate>Tue, 12 Nov 2024 00:00:00 +0000</pubDate><guid>https://ohaodha.ie/blog/moaning-about-the-typography-of-irish-text-on-train-station-signs/</guid><description>&lt;p>If you&amp;rsquo;ve ever gotten an Iarnród Éireann train from anywhere west of Portarlington to Dublin, you&amp;rsquo;ll have passed through a railway station named &amp;ldquo;Sallins &amp;amp; Naas&amp;rdquo;.
It gets its unusual name from the fact that despite being located in a small-ish town called Sallins, it is a short distance from &amp;amp; serves the much larger town of Naas, which is the largest town in County Kildare&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.
If you&amp;rsquo;re like me, that is, have an interest in Gaelic typography and have nothing better to do with your time than get mildly annoyed over trivial matters like typography, you&amp;rsquo;ll find the station name-board particularly offensive to the eye:&lt;/p>
&lt;figure>
 &lt;a href="./images/sallinsnaas.jpg">
 &lt;img src="./images/sallinsnaas.jpg" alt="Sallins &amp;amp; Naas station name-board">
 &lt;/a>&lt;figcaption>Sallins &amp;amp; Naas station name-board&lt;/figcaption>&lt;/figure>&lt;p>Horrifying, isn&amp;rsquo;t it?&lt;/p>
&lt;p>I shan&amp;rsquo;t complain about the ugly font &amp;amp; colours, as this seems to be a standard choice across Iarnróid Éireann stations, and I can forgive the weirdly cramped &lt;em>síneadh fada&lt;/em>, distasteful as it may be.
The presence of &lt;a href="https://en.wikipedia.org/wiki/Tittle">tittles&lt;/a> on the &lt;em>I&lt;/em>s is where I really begin to take issue: traditionally, Gaelic orthography excludes the tittle, and so too did Irish road signs up until the utterly tasteless decision was made to start dotting the &lt;em>I&lt;/em>s&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>.
However, my primary issue is with the use of the ampersand in the Irish text: the Irish alphabet does not include the ampersand, and &amp;ldquo;&lt;span class="gaelic">⁊&lt;/span>&amp;rdquo; is used instead.
This symbol is called the &lt;em>Tironian Et&lt;/em> in English or more simply the &lt;em>agus&lt;/em> (literally &amp;ldquo;and&amp;rdquo;) in Irish.
While my complaint about the presence of tittles could be reasonably dismissed by saying that it&amp;rsquo;s simply an alternative typeface rendering and is consistent with the Roman type used to write Irish as opposed to the Gaelic type, the inclusion of an ampersand cannot be dismissed so easily;
including the ampersand is to insert a foreign character that has no historical basis in Irish writing, displacing an ancient character that is firmly rooted in Irish writing history.
It&amp;rsquo;s not as though this character is esoteric or unknown: it was included on Irish road signs for decades, although it seems to be slowly being phased out by sign-makers who have no regard for the Irish language or culture.&lt;/p>
&lt;p>This is particularly heartbreaking to see if you&amp;rsquo;ve seen what the sign looked like in the past:&lt;/p>
&lt;figure>
 &lt;a href="./images/Sallins_station_name-board_-_geograph.org.uk_-_2432955.jpg">
 &lt;img src="./images/Sallins_station_name-board_-_geograph.org.uk_-_2432955.jpg" alt="1974 Photograph of the Sallins station name-board">
 &lt;/a>&lt;figcaption>1974 Photograph of the Sallins station name-board [Source: &lt;a href="https://www.geograph.org.uk/profile/61944">The Carlisle Kid&lt;/a>]&lt;/figcaption>&lt;/figure>&lt;p>This is what Irish-language signs should be: high-contrast, easily-legible colours, clear visual distinction between the Irish &amp;amp; English text by using clearly distinct typefaces, using a traditional Cló Gaelach with dotless &lt;em>I&lt;/em>s and &lt;em>poncanna séimhithe&lt;/em> instead of using the letter &lt;em>H&lt;/em> to denote lenition.
It&amp;rsquo;s simply more practical, more aesthetically pleasing, and more culturally &amp;amp; linguistically enriching.
It should now be easier than ever to produce signs like this, with modern printing &amp;amp; graphic design, and computers effortlessly supporting innumerable scripts &amp;amp; typefaces, but somehow we&amp;rsquo;re moving backwards.&lt;/p>
&lt;figure>
 &lt;a href="./images/meme.png">
 &lt;img src="./images/meme.png" alt="Meme depicting Patrick Bateman&amp;rsquo;s reaction to the old Sallins name-board: &amp;ldquo;Féach ar an dathú fínealta neasbhán sin. An easpa oiríunach poncanna ar na h-íonna. Ó mo Dhia, tá poncanna séimhithe aige fiú.&amp;rdquo;">
 &lt;/a>&lt;figcaption>Meme depicting Patrick Bateman&amp;rsquo;s reaction to the old Sallins name-board&lt;/figcaption>&lt;/figure>&lt;p>You may, quite reasonably, ask &amp;ldquo;why does it matter how the Irish text is written?&amp;rdquo;.
My response is simple: what is the point of including the Irish text?
If it&amp;rsquo;s to preserve the Irish language &amp;amp; culture, then why would you not do it properly?
The traditional Irish writing system is as much a part of the Irish language as any other aspect of its orthography.
Why is one important but not the other?
Why do we give precedence to the English typeface instead of the Irish?
In this day &amp;amp; age, there&amp;rsquo;s no technical restriction on the fonts that can be used on a sign.
If the Irish language matters enough to be included on the sign, then it matters enough to write it properly.&lt;/p>
&lt;p>I&amp;rsquo;ll leave you with another minor complaint before ending off this moanfest:
if you return eastwards on the train from Sallins, you&amp;rsquo;ll pass through Athlone Station, where in the car park you&amp;rsquo;ll see a sign misspelling the name of the town in Irish as &lt;em>Átha Luain&lt;/em>&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>.
Irish signage still has a long way to go.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>The station was originally simply named &amp;ldquo;Sallins&amp;rdquo; from its opening in 1846 to its closure in 1963, before re-opening as &amp;ldquo;Sallins &amp;amp; Naas&amp;rdquo; in 1994. I am personally of the opinion that the former name makes more sense.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Beyond just being another careless blow dealt to the Irish language &amp;amp; culture, the addition of the tittle to Irish text on road signs serves to make the Irish text less visually distinct from the English than it formerly was, and in my opinion, makes the entire sign less aesthetically pleasing.
This change occurred at some point between the publication of the &lt;a href="https://dttassupportoffice.sharepoint.com/sites/DTTASSupportOffice/Traffic%20Signs%20ManualTTM/Forms/AllItems.aspx?id=%2Fsites%2FDTTASSupportOffice%2FTraffic%20Signs%20ManualTTM%2F01.%20Traffic%20Signs%20Manual%201996%2F2%20-%20Directional%20Information%20Signs.pdf&amp;amp;parent=%2Fsites%2FDTTASSupportOffice%2FTraffic%20Signs%20ManualTTM%2F01.%20Traffic%20Signs%20Manual%201996&amp;amp;p=true&amp;amp;ga=1">1996 version of the Department of Transport Traffic Signs Manual § 2 Directional Information Signs National, Regional and Local Roads&lt;/a>
and the publication of the &lt;a href="https://dttassupportoffice.sharepoint.com/sites/DTTASSupportOffice/Traffic%20Signs%20ManualTTM/Forms/AllItems.aspx?id=%2Fsites%2FDTTASSupportOffice%2FTraffic%20Signs%20ManualTTM%2FWebsite%2FLive%20documents%2FCurrent%20Chapters%20%28Feb%202024%29%2FChapter%202%20-%20Master%20%28November%202021%29.pdf&amp;amp;parent=%2Fsites%2FDTTASSupportOffice%2FTraffic%20Signs%20ManualTTM%2FWebsite%2FLive%20documents%2FCurrent%20Chapters%20%28Feb%202024%29&amp;amp;p=true&amp;amp;ga=1">2021 version of the Department of Transport Traffic Signs Manual § 2 Directional Information Signs National, Regional and Local Roads&lt;/a>, with dotless &lt;em>I&lt;/em>s appearing in the former but not the latter.
Unfortunately, at the time of writing, the Department of Transport has not made the interim versions of the Traffic Sign Manual available online, but I would estimate that the change probably occurred in the 2010 TSM, as &lt;a href="https://www.sabre-roads.org.uk/forum/viewtopic.php?t=14547">this thread from 2007 on SABRE&lt;/a> refers to a version of the road signs with dotless &lt;em>I&lt;/em>s as the most recent version, which is the latest reference I can find to &lt;em>all&lt;/em> Irish road signs featuring dotless &lt;em>I&lt;/em>s.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>This is an easy enough spelling mistake to make for someone who is not a native speaker of the Irish language.
&amp;ldquo;Athlone Town&amp;rdquo; is &lt;em>Baile Átha Luain&lt;/em> in Irish, which can be literally translated as &amp;ldquo;The town of the ford of Luan&amp;rdquo;.
This &amp;ldquo;of&amp;rdquo; relationship between two words in Irish is represented by putting the word which owns the other in the &lt;em>genitive form&lt;/em>.
The word &amp;ldquo;town&amp;rdquo; (&lt;em>baile&lt;/em>) is shown to be &amp;ldquo;of&amp;rdquo; the ford in question by changing the spelling of the word for &amp;ldquo;ford&amp;rdquo; from the nominative &lt;em>Áth&lt;/em> to the genitive &lt;em>Átha&lt;/em>.
The same is done for the name Luan: to show that it is the town of the ford &lt;strong>of Luan&lt;/strong>, the spelling of his name is changed to the genitive &lt;em>Luain&lt;/em>.
&lt;br> &lt;br>
However, if you just want to write &amp;ldquo;Athlone&amp;rdquo; and not &amp;ldquo;Athlone Town&amp;rdquo;, then it is simply &lt;em>Áth Luain&lt;/em> and not &lt;em>Átha Luain&lt;/em>.
Writing &lt;em>Átha Luain&lt;/em> is like writing &amp;ldquo;of the ford of Luan&amp;rdquo; instead of &amp;ldquo;the ford of Luan&amp;rdquo; in English: if you remove the &lt;em>baile&lt;/em> from the name, then you need to revert the spelling of the word &amp;ldquo;ford&amp;rdquo; back to its nominative form from the genitive, as the spelling &lt;em>Átha&lt;/em>.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Evolution of My Approach to Note-Taking</title><link>https://ohaodha.ie/blog/evolution-of-my-approach-to-note-taking/</link><pubDate>Mon, 24 Jun 2024 20:02:57 +0100</pubDate><guid>https://ohaodha.ie/blog/evolution-of-my-approach-to-note-taking/</guid><description>&lt;h2 id="first-year-of-university">First Year of University&lt;/h2>
&lt;p>In my first year of university, I took pen &amp;amp; paper notes, using a designated refill pad and binder per class.
Pages were stapled together by topic or chapter, and then inserted into a plastic pocket in the binder.
These notes were comprehensive, meaning that any information that was in the lecture slides or said by the lecturer was included in my notes.&lt;/p>
&lt;p>This technique presented many difficulties: if the lecturer moved on to the next slide before I was finished writing, I would have to look at the copy of the lecture slides posted on
Blackboard to complete my notes, meaning that I would either have to finish taking the notes on my own time after the lecture or have my
laptop or phone with a copy of the lecture slides open in front of me in the lecture.
I typically opted for the latter, and had the lecture slides open on my laptop during the lecture and took notes from them at my own pace,
listening to the lecturer speak in the background.
In retrospect, this meant that I often wasn&amp;rsquo;t paying as much attention to the lecture as I could&amp;rsquo;ve been, and I probably would have been
as well off had I not attended the lecture and just reviewed the slides from home.&lt;/p>
&lt;p>One of the key advantages of this approach however, was that it was quite good for getting the
information into my brain: the act of physically writing out each piece of information made
recalling that information later much easier, and I generally didn&amp;rsquo;t have to do very much revision — usually just at the end of the semester before exams.
This technique was so effective, in fact, that this remains my pre-exam study technique to this day;
I simply write out all the information on a topic repeatedly.&lt;/p>
&lt;p>It goes without saying here that I did not have any sort of version control or backup system as all my notes were on paper, which horrifies me in retrospect.
Furthermore, for reasons I now cannot recall, I would delete all the files related to an assignment once I had submitted it (perhaps to free up
SSD space?) which is quite regrettable, as it means that the Git archive of all my university materials that I maintain will now be forever incomplete.&lt;/p>
&lt;p>At this time I still used Microsoft Word documents for any assignment that required writing, but
I did use $\mathrm{\LaTeX}$ once for a mathematics group project (which was really my introduction to
$\mathrm{\LaTeX}$), using the online $\mathrm{\LaTeX}$ editor &lt;a href="https://www.overleaf.com/">Overleaf&lt;/a>.
For any sort of coding, I would use either the Microsoft IDE
&lt;a href="https://visualstudio.microsoft.com/">Visual Studio&lt;/a> (for C programming) or the Microsoft text
editor &lt;a href="https://code.visualstudio.com/">Visual Studio Code&lt;/a> for everything else, such as web development in HTML &amp;amp; JavaScript.&lt;/p>
&lt;p>I thankfully did have the good sense to digitally archive the lecture slides for each of my classes, but because I was still using
Windows as my laptop OS at the time and was unaware of CLI tools such as &lt;a href="https://pdfgrep.org/">&lt;code>pdfgrep&lt;/code>&lt;/a> to quickly &amp;amp; recursively search through
PDF documents, I merged all the lecture slides for each class into one mega-document per class to facilitate easy browsing and searching.
This has the benefit of keeping everything neat and easily findable, but it was prone to me missing a set of slides and messing up the entire
document or a lecturer updating the lecture slides and me having to re-merge the entire document again.&lt;/p>
&lt;p>One of the most damning indictments of this system of note taking is that I simply never looked back over my handwritten notes, except perhaps
for my mathematics classes.
They generally didn&amp;rsquo;t add any value that wasn&amp;rsquo;t in the lecture slides (as they were, for the most part, just copies of the slides) and had
the additional disadvantage of not being searchable or indexable, and being cumbersome to carry around.&lt;/p>
&lt;p>The notes I have from this academic year are far too voluminous for me to digitise, and so are just sitting in a box somewhere.
I never looked back on them at the time I was taking these classes, and probably never will.&lt;/p>
&lt;p>Towards the end of first year, I did begin to experiment with some alternative, digital note-taking methods.
I used the flashcard application &lt;a href="https://apps.ankiweb.net/">Anki&lt;/a> to revise for a class quiz on Computing Systems, to great success, having
previously used Anki to study for my Leaving Certificate exams in secondary school.
I also began experimenting with various applications for note-taking, including Markdown-based applications such as
&lt;a href="https://obsidian.md/">Obsidian&lt;/a>, &lt;a href="https://joplinapp.org/">Joplin&lt;/a>,
and even plaintext in &lt;a href="https://www.vim.org/">Vim&lt;/a> &amp;amp; &lt;a href="https://helix-editor.com/">Helix&lt;/a> as I started to become interested
in using CLI &amp;amp; TUI programs on my &lt;a href="https://ubuntu.com/">Ubuntu GNU/Linux&lt;/a> desktop, mostly just because
I thought they looked cool, discovering their great utility &amp;amp; convenience only as a side-effect.&lt;/p>
&lt;h2 id="second-year-of-university">Second Year of University&lt;/h2>
&lt;h3 id="semester-1">Semester 1&lt;/h3>
&lt;p>Before starting the first semester of my second year of university, I had decided that I&amp;rsquo;d had
enough of using Windows on my laptop and decided to try something new with
&lt;a href="https://manjaro.org/">Manjaro GNU/Linux&lt;/a> as my primary operating system, having previously been
using &lt;a href="https://ubuntu.com/">Ubuntu GNU/Linux&lt;/a> as my desktop operating system for some years.
With this change I also decided to try something new for my note-taking approach, and started
using the Markdown-based notes application &lt;a href="https://logseq.com/">Logseq&lt;/a>.
I ultimately decided on using Logseq because it was free &amp;amp; open source, had an easy to use &amp;amp;
pleasant UI, was capable of using $\mathrm{\LaTeX}$ typesetting for equations, and had a handy flashcard feature wherein a section of a note could be turned into a flashcard with spaced repetition for
revision, not dissimilar to Anki.
It also allowed me to embed PDF documents in the Markdown file of a note and view it in a split
pane next to the note itself, which was handy for archiving &amp;amp; linking the lecture slides, and
the split view was very helpful since I was using the
&lt;a href="https://www.gnome.org/">Gnome Desktop Environment&lt;/a> and hadn&amp;rsquo;t yet moved to a tiling window manager.&lt;/p>
&lt;p>At the start of the semester, I very briefly experimented with taking my notes by writing
flashcards directly during the lectures using Anki but I found this too impractical and
abandoned it quickly, so the flashcard feature of Logseq was very attractive.&lt;/p>
&lt;p>I took notes for all my subjects in Logseq using Markdown, including my mathematics subjects,
which was surprisingly unproblematic for studying Graph Theory as the inline equation typesetting
worked just fine, and I could just screenshot any graphs that I wanted to include in my notes from the lecture slides.
I even did some mathematics homework entirely within Logseq, and took notes from tutorials in it as well.
However, this became too difficult to use for Combinatorics, as I found it too slow for writing out the workings &amp;amp;
solutions to equations, so I reverted to using pen &amp;amp; paper for that subject alone.&lt;/p>
&lt;figure>
 &lt;a href="images/Graph Theory Notes in Logseq.png">
 &lt;img src="images/Graph Theory Notes in Logseq.png" alt="Graph Theory Notes in Logseq">
 &lt;/a>
 &lt;figcaption>Graph Theory Notes in Logseq&lt;/figcaption>
&lt;/figure>
&lt;p>Despite being faster &amp;amp; more searchable than pen &amp;amp; paper note-taking, Logseq note-taking suffered
from many of the same problems as pen &amp;amp; paper notes:
with the exception of revising with the flashcard feature, I never really looked back on my
Logseq notes, as they were at their most accurate just a copy of the content of the lecture slides, and at worst could be missing information.
I was also still not really paying attention during the lectures, instead just focusing on taking notes from my copy of the lecture slides, and often had to take time after lectures to finish my notes.
The link-based &amp;amp; tagging nature of Logseq made it easy to write notes, but mean that there was
not logical or easily navigable directory or file structure, and so the notes could only really be made sense of when viewed with the Logseq application itself.
Furthermore, I was still unfamiliar with command-line utilities like &lt;code>grep&lt;/code> which could&amp;rsquo;ve made searching through
the Markdown files much easier using something like &lt;code>grep -ri&lt;/code>, despite their unstructured arrangement.&lt;/p>
&lt;p>I also struggled greatly and ultimately failed to stay on top of my Logseq flashcards — there were
just too many of them for me to manage, and they didn&amp;rsquo;t distinguish critical information from non-
critical, so a large amount of time was spent revising information that I was never going to need
to recall.
This is largely why I no longer use flashcards for revision, except sometimes for very focused &amp;amp;
narrow topics.
I find flashcards more appropriate for a short-term, pre-exam revision tool rater than a long-term
continuous use.&lt;/p>
&lt;p>I still had no version control system at this time, but I began to keep my assignment files even
after I had submitted the assignment, a step in the right direction at least.
I continued to use merged PDFs to archive lecture slides, and these merged PDFs were invariably
what I looked back on for study or for assignments.&lt;/p>
&lt;p>Having abandoned Windows at the start of the semester, I began to use $\mathrm{\LaTeX}$ in Overleaf
to write assignments, before switching to using it locally in Vim
as I was missing out on some rather useful features of $\mathrm{\LaTeX}$ by only
using an online editor, such as the ability to include code blocks in a document by providing the
path to the source code file.
Around this time, I also experimented with pre-rolled &lt;a href="https://neovim.io/">Neovim&lt;/a> configurations such as
&lt;a href="https://www.lunarvim.org/">LunarVim&lt;/a> and also briefly with &lt;a href="https://github.com/doomemacs/doomemacs">Doom Emacs&lt;/a>, a pre-rolled Emacs configuration with Vi-like keybindings.
I also briefly experimented with &lt;a href="https://orgmode.org/">Org Mode&lt;/a> in Emacs, a markup language not
dissimilar to Markdown but far more powerful, but I gave up on this primarily because Emacs just
took far to long to start (even when I kept it running in the background constantly as a daemon)
and because configuration in Emacs via Elisp was too cumbersome for my liking, made much more
difficult by the fact that I did not know any Lisp at the time.&lt;/p>
&lt;p>My programming was done either in Neovim or &lt;a href="https://www.jetbrains.com/idea/">Intellij IDEA&lt;/a> for Java
development, and the Intellij family of IDEs remains my favourite full-blown IDE to this day for
any kind of development when I&amp;rsquo;m not using Vim, although it is sadly not FOSS so I generally don&amp;rsquo;t
use them on my personal machines if it can be avoided.&lt;/p>
&lt;h3 id="semester-2">Semester 2&lt;/h3>
&lt;p>Before the start of my second semester of second year, I decided to switch from Manjaro GNU/Linux
to &lt;a href="https://voidlinux.org/">Void GNU/Linux&lt;/a>, largely due to its increased stability from Arch-based
distributions like Manjaro while still maintaining a rolling-release model, its minimalism, and
its admittedly cool name.
With this, I also made the switch to using a tiling window manager, namely
&lt;a href="https://github.com/conformal/spectrwm">spectrwm&lt;/a>, a dynamic tiling window manager for
&lt;a href="https://www.x.org/wiki/">X11&lt;/a> with a built-in panel.&lt;/p>
&lt;p>My second year of university was when I began to start taking lecture notes in Vim with
$\mathrm{\LaTeX}$.
Like before, these notes were intended to be comprehensive and to be a superset of the content in
the lecture slides.
My goal was to create a single PDF document per subject that was like a custom comprehensive
textbook on the subject.
This technique was largely successful, as my lecture notes were convenient, transportable,
indexable, &amp;amp; searchable.
However, as in previous years, I generally didn&amp;rsquo;t really look back over my lecture notes.
The only class for which I routinely looked back over my notes was a class on Networks &amp;amp;
Data Communications, and that was only because the lecture slides for this class did not have
searchable text in them, only an image of text, so it was easier to search through my notes.
I generally preferred the lecture slides as the &amp;ldquo;canonical source&amp;rdquo; of information, as there
was always the possibility that a mistake had slipped into my notes or that they were incomplete.&lt;/p>
&lt;figure>
 &lt;a href="images/Network &amp; Data Communications Notes in LaTeX.png">
 &lt;img src="images/Network &amp; Data Communications Notes in LaTeX.png" alt="Network &amp; Data Communications Notes in LaTeX">
 &lt;/a>
 &lt;figcaption>Network &amp; Data Communications Notes in $\mathrm{\LaTeX}$&lt;/figcaption>
&lt;/figure>
&lt;p>I only discovered the Vim plugin &lt;a href="https://github.com/lervag/vimtex">VimTex&lt;/a> late in the semester,
and so I did without automatic $\mathrm{\LaTeX}$ compilation, instead using &lt;code>CTRL+z&lt;/code> to suspend Vim
and give me access to a shell, and then running a command like
&lt;code>pdflatex Notes.tex &amp;amp;&amp;amp; fg&lt;/code> to compile the document and return Neovim to the foreground.
I used the &lt;code>&amp;amp;&amp;amp;&lt;/code> operator to prevent Neovim from returning to the foreground if the compilation failed,
allowing me to read the error messages before manually returning Neovim to the foreground to fix them.
I used the PDF viewer &lt;a href="https://pwmt.org/projects/zathura/">Zathura&lt;/a>, which automatically refreshes when the
PDF document is recompiled.&lt;/p>
&lt;p>Despite still not using any sort of version control system for my university materials beyond group
projects, I did begin to properly archive all my assignments, lecture materials, and notes, meaning
that this semester is the first semester of university that I have a complete archive of.&lt;/p>
&lt;h2 id="third-year-of-university">Third Year of University&lt;/h2>
&lt;p>To my undying shame, it was not until the tail end of the first semester of third year that I began to use Git
to archive my university materials, but at least I eventually did begin to use version control.&lt;/p>
&lt;p>For the most part, my approach to note-taking in third year was not very different to the approach I was
employing at the end of second year.
I continued to take comprehensive notes, with one large, textbook-like document per class, with just some
minor typesetting improvements, and I settled on two $\mathrm{\LaTeX}$ document formats: one for short assignments,
and one for notes &amp;amp; long report-based assignments.&lt;/p>
&lt;figure>
 &lt;a href="images/Shorter &amp; Longer Assignment LaTeX Templates.png">
 &lt;img src="images/Shorter &amp; Longer Assignment LaTeX Templates.png" alt="Shorter &amp; Longer Assignment LaTeX Templates">
 &lt;/a>
 &lt;figcaption>Shorter (left) &amp; Longer (right) Assignment $\mathrm{\LaTeX}$ Templates&lt;/figcaption>
&lt;/figure>
&lt;p>I began to use &lt;a href="https://github.com/baskerville/bspwm">bspwm&lt;/a> instead of spectrwm as my window manager, as I
favoured the flexibility of a manual tiling window manager over a dynamic one (meaning that windows are
positioned according to the user&amp;rsquo;s selection rather than in an automatic pre-defined layout), and I started to
use &lt;a href="https://polybar.github.io/">polybar&lt;/a> as my desktop panel.
I began to make extensive use of CLI tools such as &lt;code>pdfgrep&lt;/code> &amp;amp; &lt;a href="https://github.com/phiresky/ripgrep-all">&lt;code>rga&lt;/code>&lt;/a>
to quickly search through lecture slides &amp;amp; notes, and ended my practice of merging the lecture slides into one
mega-document, instead just keeping them together in the one directory per subject.
I also began to use &lt;a href="https://www.luatex.org/">LuaLaTeX&lt;/a> as my $\mathrm{\LaTeX}$ compiler rather than
&lt;code>pdflatex&lt;/code> as it has better font support and I like the idea of doing Lua scripting within my $\mathrm{\LaTeX}$
documents, although I&amp;rsquo;ve never encountered a good reason to do so.&lt;/p>
&lt;p>Most importantly perhaps, I made the heavy-hearted decision to switch from my beloved Void GNU/Linux to
&lt;a href="https://archlinux.org/">Arch GNU/Linux&lt;/a>, which is not massively dissimilar to Void and is the distribution
upon which Manjaro is based.
I made this switch as I needed to use &lt;a href="https://unity.com/">Unity3D&lt;/a> for a games development class that I was
forced to take, and it was not available in the Void repositories.
They did offer both &lt;code>.deb&lt;/code> &amp;amp; &lt;code>.rpm&lt;/code> Linux binaries which I perhaps could&amp;rsquo;ve converted to
&lt;a href="https://docs.voidlinux.org/xbps/index.html">&lt;code>xbps&lt;/code>&lt;/a>, but this option seemed unreliable.
I knew I was likely to need more non-free software for university in the future, and so the
&lt;a href="https://aur.archlinux.org/">Arch User Repository&lt;/a> was appealing, as it had a large number of packages, for
just about any program you could imagine.
I don&amp;rsquo;t particularly like Arch as operating system, as I find the install process needlessly cumbersome in
the name of &amp;ldquo;minimalism&amp;rdquo;, despite more minimal operating systems like Void having an easier install process via
a TUI, and I resent how often packages break on Arch due to an update as it is a &amp;ldquo;bleeding edge&amp;rdquo; rolling-release
distribution.
However, the AUR is just so unbelievably useful &amp;amp; convenient that I&amp;rsquo;m willing to put up with these inconveniences,
at least for the remainder of my university days.&lt;/p>
&lt;p>Towards the end of the semester, I began to give up on my $\mathrm{\LaTeX}$ notes for some subjects.
I realised that for my Database Systems class that I was better off just listening to the lecturer and following their
examples than trying to transcribe the entirety of the lecture slides to my notes.
For the last few weeks of term, I just sat in the lecture hall and didn&amp;rsquo;t take any notes, just focusing on the lecture content.&lt;/p>
&lt;h2 id="the-future">The Future&lt;/h2>
&lt;p>This coming September I will begin my fourth &amp;amp; final year of university, and so I want to make the optimal note-taking system
based off what I&amp;rsquo;ve learnt from the various systems that I&amp;rsquo;ve employed throughout the years.&lt;/p>
&lt;h3 id="a-return-to-pen--paper">A Return to Pen &amp;amp; Paper?&lt;/h3>
&lt;p>I think that one of the greatest issues with my note-taking methods up to this point is that they&amp;rsquo;ve invariably prevented
me from actually paying attention during the lecture: I&amp;rsquo;m so busy taking notes (essentially just transcribing the lecture
slides) that I don&amp;rsquo;t take the time to properly listen to and understand what the lecturer is saying.
To prevent this, one idea I have is to stop transcribing information that&amp;rsquo;s already in the slides into my notes while I&amp;rsquo;m sitting
in the lecture hall; instead, I would take note only of things that are not in the slides, such as extra information that
the lecturer provides, or examples.&lt;/p>
&lt;p>However, one of the greatest strengths of my note-taking methods up to this point is that they&amp;rsquo;ve forced me to actually go
through each part of the lecture materials and transcribe them, which makes recalling them much easier later.
I generally need to do very little revision, thanks largely to the fact that I carefully comb through the entire lecture
material at least once.&lt;/p>
&lt;p>My best idea for a note-taking scheme at the moment that addresses the issue of me being distracted in lectures without
nullifying the benefits that I receive from carefully transcribing the entirety of the lecture slides is to focus on the
lecture during the lecture, noting down only what is not in the slides and then at some later time after the lecture
transcribe the entirety of the lecture slides, plus my handwritten notes, into a $\mathrm{\LaTeX}$ document.
Because I won&amp;rsquo;t be pressured to keep up with the pace of the lecture while writing my $\mathrm{\LaTeX}$ document, I&amp;rsquo;ll be able to take
the time to format &amp;amp; typeset things nicely, and create or source high-quality diagrams.&lt;/p>
&lt;p>I&amp;rsquo;ve recently started to take interest in fountain pens, so I think I may use one for note-taking as I find them
smoother &amp;amp; more fluid to write with than the conventional ballpoint pen.
Because my lecture notes would be so sparse in this system, likely rarely exceeding a page per lecture and often being entirely empty,
I could use just one single notebook that will contain notes for all my classes.
I would also continue my paradigm of one comprehensive, textbook-like $\mathrm{\LaTeX}$ document per subject, as this has worked well for me
in the past.&lt;/p>
&lt;p>My primary concern with this proposed system is that it would require a large amount of time dedicated to note-taking outside
of lectures, introducing both the risk that I exhaust much of my free time to accomplish this and the risk that I fall behind
on note-taking because of the time commitment involved.
Furthermore, my experience for many of my classes has been that the lecture slides generally include every piece of relevant
information, and there is generally little added, if anything, in lectures that that&amp;rsquo;s not in the slides.
I know of at least one person who did not attend any lectures at all in third year, instead just opting to look at the lecture
slides, to apparently reasonable success.&lt;/p>
&lt;h3 id="continuing-my-existing-method">Continuing My Existing Method&lt;/h3>
&lt;p>I have at this point largely perfected my $\mathrm{\LaTeX}$ note-taking method to my taste, so I am reluctant to give it up when it
is tried &amp;amp; true, and only has one significant flaw (being distracted during lectures).
I think that it would likely be a mistake to forgo this technique that has been so successful for me, especially considering
that for the majority of my classes I am unlikely to miss out on something that is said in the lecture but not included in the
lecture slides, and so I think I will most likely continue with this approach into fourth year.&lt;/p>
&lt;p>If however, I encounter another class like my Database Systems class, where there is significantly more to be gained from what
the lecturer is saying than from the lecture slides, I will take minimal pen &amp;amp; paper notes as detailed in the above section, and
pay as much attention as possible.
These minimal notes, along with the content of the lecture slides, will then be transferred into a $\mathrm{\LaTeX}$ document as for my
other classes at some later time, either in the evening or on the weekends, which will have the added benefit of forcing me to
revise the lecture content a little while after the lecture itself.&lt;/p></description></item><item><title>Cool qutebrowser Customisations</title><link>https://ohaodha.ie/blog/cool-qutebrowser-customisations/</link><pubDate>Fri, 19 Apr 2024 20:03:27 +0100</pubDate><guid>https://ohaodha.ie/blog/cool-qutebrowser-customisations/</guid><description>&lt;h2 id="what-is-qutebrowser">What is qutebrowser?&lt;/h2>
&lt;p>Qutebrowser&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> is a keyboard-focused, minimal web browser written in Python.
It makes use of Vim-like keybindings, and is surprisingly fast &amp;amp; memory-efficient considering the language in which it is
written: it
generally starts up faster than the &lt;a href="https://brave.com/">Brave&lt;/a> browser on my machine, and consumes less memory than
&lt;a href="https://www.mozilla.org/en-US/firefox/">Firefox&lt;/a> (which I&amp;rsquo;ve had memory leak issues with in the past).&lt;/p>
&lt;p>It has a minimal graphical user interface, and some primitive built-in adblocking which tends to work quite well, with
the exception of YouTube advertisements.
It is configured via plaintext Python files, making it highly &amp;amp; easily configurable.
It also has the built-in capability to run JavaScript userscripts, making its behaviour even more flexible &amp;amp;
customisable.&lt;/p>
&lt;h2 id="configurations-in-configpy">Configurations in &lt;code>config.py&lt;/code>&lt;/h2>
&lt;p>I&amp;rsquo;ll skip over the &lt;del>boring&lt;/del> common configurations that have been covered to death by others before me here, such as
turning on dark mode or launching the current URL in &lt;code>mpv&lt;/code> and skip to configurations that I think have something interesting to them.&lt;/p>
&lt;h3 id="video-speed-controls">Video Speed Controls&lt;/h3>
&lt;p>Most video platforms only allow you to speed up a video to $2\times$ speed, which I often find to be not as fast as
I would like.
I rarely watch a video on lower than $2\times$ speed, unless the timing of the video is important, such as music or
comedy, and often want to watch videos on $3\times$ or $4\times$ speed.&lt;/p>
&lt;p>The following lines of code, when added to the qutebrowser &lt;code>config.py&lt;/code> configuration file, make the up &amp;amp; down arrow keys
into video speed controllers which increase the speed of videos on the page by quarter-increments at a time, and allows
speeds in excess of the typical limit $2\times$ to be achieved:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># control video speed&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;Up&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;jseval document.querySelector(&amp;#39;video&amp;#39;).playbackRate += 0.25;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;Down&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;jseval document.querySelector(&amp;#39;video&amp;#39;).playbackRate -= 0.25;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="clear-notifications-messages">Clear Notifications (Messages)&lt;/h3>
&lt;p>The above speed controls generate messages at the bottom of the screen that show what speed has been set, which I find
useful, but if I hit the button several times they can build up and take a while to disappear, so the following
keybinding clears them:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;clear-messages&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="find-missing-webpages">Find Missing Webpages&lt;/h3>
&lt;p>If a webpage I visit is missing or has been changed, I hit the key combination &lt;code>,w&lt;/code> to find it on the Wayback Machine:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># search for the current URL in archive.org&amp;#39;s wayback machine&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;open http://web.archive.org/&lt;/span>&lt;span class="si">{url}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="load-the-current-page-in-firefox">Load the Current Page in Firefox&lt;/h3>
&lt;p>If a webpage I visit doesn&amp;rsquo;t work properly in qutebrowser (as is sometimes, but rarely, the case) I can open the tab in
Firefox:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># keybind to open current url in firefox &lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;Ctrl+Alt+t&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;spawn -d firefox &lt;/span>&lt;span class="si">{url}&lt;/span>&lt;span class="s2"> ;; tab-close&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="userscripts">Userscripts&lt;/h2>
&lt;h3 id="redirectors">Redirectors&lt;/h3>
&lt;p>I use a number of alternative frontends for various social media sites to avoid having to make an account, and have a
number of scripts that redirect each social media page to the alternative frontend.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">// @name reddit to libreddit redirector
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?reddit\.com\/.*$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1">// @run-at document-start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">alt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;libreddit.kavin.rocks&amp;#34;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/(www.)?reddit.com/gi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">alt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;redirected reddit.com to &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">alt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">// @name tiktok to proxitok redirector
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?tiktok\.com\/.*$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">// @run-at document-start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/(www\.)?tiktok.com/gi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;proxitok.pabloferreiro.es&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;redirected tiktok.com to proxitok.pabloferreiro.es&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">// @name twitter to nitter redirector
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?twitter\.com\/.*$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">// @run-at document-start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/(www.)?twitter.com/gi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;nitter.poast.org&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;redirected twitter.com to nitter.poast.org&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">// @name youtube to invidious redirector
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?youtube\.com\/.*$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">// @run-at document-start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">href&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/(www\.)?youtube.com/gi&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;yewtu.be&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;redirected youtube.com to yewtu.be&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="smart-video-speed-reset">Smart Video Speed Reset&lt;/h3>
&lt;p>As I mentioned above, I typically have my video speeds set to $2\times$ or above except for videos in which the
timing is important, such as music or comedy.
The following userscript detects the category of a YouTube video and resets the speed to normal speed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">// @name normal speed music
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1">// @description make music &amp;amp; comedy youtube videos normal speed (for timing purposes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?y\.com\.sb\/watch\?v=.+$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1">// @include /^https?:\/\/(www\.)?yewtu\.be\/watch\?v=.+$/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1">// @run-at document-idle
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;genre&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">innerHTML&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Music&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;genre&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">innerHTML&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Comedy&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">querySelector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;video&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">playbackRate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the above script is written to work on Invidious YouTube frontends rather than YouTube itself, although it could easily
be adapted for YouTube by just adding it to the &lt;code>@include&lt;/code> section.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>&lt;a href="https://qutebrowser.org/">https://qutebrowser.org/&lt;/a>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Fretboard Memorisation with Modular Arithmetic</title><link>https://ohaodha.ie/blog/fretboard-memorisation-with-modular-arithmetic/</link><pubDate>Fri, 01 Mar 2024 23:36:19 +0000</pubDate><guid>https://ohaodha.ie/blog/fretboard-memorisation-with-modular-arithmetic/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Memorising (or &amp;ldquo;learning&amp;rdquo;) the fretboard refers to when an individual learns which musical notes correspond to what
positions on the fretboard of their instrument, a skill that is useful for playing just about any instrument that
has frets.
In particular, &amp;ldquo;learning the fretboard&amp;rdquo; is a topic frequently discussed in guitar circles for its importance in
improvisation &amp;amp; soloing.
In general, the common approaches to learning the fretboard seem to consist of a combination of just committing it to
memory and using some tricks to help with that memorisation; however, I have a number of problems with this approach:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>It&amp;rsquo;s too much effort:&lt;/strong> I am far too lazy to memorise the note that corresponds to up to $24 \times 6 = 144$
positions on a 24-fret guitar (or $24 \times 5 = 120$ positions if you take account of the two $E$ strings
being identical except for their pitch).&lt;/li>
&lt;li>&lt;strong>It doesn&amp;rsquo;t translate to other tunings:&lt;/strong> it&amp;rsquo;s all well &amp;amp; good memorising the note that corresponds to each
position in standard tuning, but your effort is rendered useless when you switch to a different tuning or a
different instrument.&lt;/li>
&lt;/ol>
&lt;h3 id="aside-what-is-modular-arithmetic">Aside: What is Modular Arithmetic?&lt;/h3>
&lt;p>While I would encourage anyone not already familiar with modular arithmetic to read up on it in detail themselves, I&amp;rsquo;ll
give a brief explanation here so that anyone can understand what I&amp;rsquo;m talking about (a good overview can be found in the
book &lt;em>Discrete Mathematics &amp;ndash; An Open Introduction&lt;/em> by Oscar Levin
&lt;a href="https://discrete.openmathbooks.org/dmoi3/sec_addtops-numbth.html#iyd">here&lt;/a>).
&lt;strong>Modular arithmetic&lt;/strong> refers to number systems in which the values don&amp;rsquo;t get higher and higher towards infinity, but
rather wrap around and start again from 0 once a certain maximum number called the &lt;strong>modulus&lt;/strong> is reached.&lt;/p>
&lt;p>Perhaps the most famous example of modular arithmetic is the 24-hour clock.
The 24-hour clock starts at 0, and goes up to 23 before starting at 0 again.
There is no way to exceed 23 on a 24-hour clock.
In this sense, the 24-hour clock is an arithmetic system &lt;strong>modulo 24&lt;/strong>.
If an event start at 23:00 and takes 2 hours, we don&amp;rsquo;t say that it finishes at &amp;ldquo;25:00&amp;rdquo;: instead, we say it finishes at
01:00 as $23 + 2 = 25 \equiv 1 \text{ mod } 24$ (25 is &lt;em>congruent&lt;/em> to 1 modulo 24).&lt;/p>
&lt;p>Modular arithmetic can also be thought of as dividing any given number by the modulus and getting the remainder.
For example, $30 \text{ mod } 24 \equiv 6 $ because $30 \div 24 = 1 \text{ remainder } 6.$&lt;/p>
&lt;p>Incidentally, many resources refer to the 12-hour clock as a classic example of a system of modular arithmetic, but I think
that this is a poor example as the 12-hour clock starts at 1 and ends at 12, meaning that it&amp;rsquo;s not quite modulo 12
(which starts at 0 and ends at 11) and also not quite modulo 13 (which start at 0 and ends at 12).&lt;/p>
&lt;h2 id="my-system">My System&lt;/h2>
&lt;p>I realised when I was teaching myself guitar that the chromatic scale can be thought of as a system of modular
arithmetic modulo 12: there are 12 notes ($A$ through $G^\#$, including the sharps/flats).
If you assign each note a number starting with $A$ at 0 and ending with $G^\#$ at 11, you get a system of
modular arithmetic that corresponds perfectly to the frets on the $A$-string.
If you don&amp;rsquo;t fret the string and pluck it, you get $A$, if you fret the string at the first fret and play it, you
get $A^\#$ (or $B^\flat$), et cetera.
If you fret the string at the 12&lt;sup>th&lt;/sup> fret, you get $A$ again, and if you fret the string at the
13&lt;sup>th&lt;/sup> fret, you get $A^\#$ again, in accordance with $12 \text{ mod } 12 \equiv 0$ and $13 \text{
mod } 12 \equiv 1.$
Hence, we already have a system for memorising all the notes on the $A$-string: it&amp;rsquo;s just arithmetic modulo 12.
All we need to do is memorise a value for each note in the chromatic scale.
Then, to find out what note a certain fret corresponds to, we can just find its value modulo 12, and to find a certain
note on the string, we can just look at the frets at multiples of the value of the note.&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>$A$&lt;/th>
 &lt;th>$A^\#$ / $B^\flat$&lt;/th>
 &lt;th>$B$&lt;/th>
 &lt;th>$C$&lt;/th>
 &lt;th>$C^\#$ / $D^\flat$&lt;/th>
 &lt;th>$D$&lt;/th>
 &lt;th>$D^\#$ / $E^\flat$&lt;/th>
 &lt;th>$E$&lt;/th>
 &lt;th>$F$&lt;/th>
 &lt;th>$F^\#$ / $G^\flat$&lt;/th>
 &lt;th>$G$&lt;/th>
 &lt;th>$G^\#$ / $A^\flat$&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>$0$&lt;/td>
 &lt;td>$1$&lt;/td>
 &lt;td>$2$&lt;/td>
 &lt;td>$3$&lt;/td>
 &lt;td>$4$&lt;/td>
 &lt;td>$5$&lt;/td>
 &lt;td>$6$&lt;/td>
 &lt;td>$7$&lt;/td>
 &lt;td>$8$&lt;/td>
 &lt;td>$9$&lt;/td>
 &lt;td>$10$&lt;/td>
 &lt;td>$11$&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>This same system can actually be generalised to all the other strings on the guitar with little adaptation.
The only difference is that we need to remember at what number on the scale each string starts and add this to the
number of each fret on that string to get the integer value corresponding to the note at that fret.
Take for example one of the $E$ strings: this string is tuned to the note $E$ (which has value 7).
To find out what note is at the 5&lt;sup>th&lt;/sup> fret of the $E$ string, we use the same approach as with the $A$
string, but add the value that the string starts at to the fret number before getting its value modulo 12:
$7 + 5 = 12 \text{ modulo } 12 \equiv 0$, indicating that the note at the 5&lt;sup>th&lt;/sup> fret of the $E$ string
is an $A.$&lt;/p>
$$
v \equiv r + f \text{ modulo } 12
$$&lt;p>
where $v$ is the value of the note at that fret, $r$ is the &amp;ldquo;root value&amp;rdquo; of the string (i.e., the value of the
note to which the string is tuned), and $f$ is the fret number.&lt;/p>
&lt;p>This might appear quite complicated at first glance due to the formulae, but it&amp;rsquo;s actually quite simple once you&amp;rsquo;ve
memorised the values.
This system of fretboard memorisation can be generalised to any tuning, any number of strings, and any number of frets.
Furthermore, I would submit that this is actually also the most correct system, as it gets straight to the reasons &lt;em>why&lt;/em>
a fret is a certain value: simply because of the number of increments it is from the note that the string is tuned to.&lt;/p>
&lt;h3 id="advantages-of-my-system">Advantages of My System&lt;/h3>
&lt;ul>
&lt;li>Requires only the memorisation of 12 key-value pairs, one for each of the musical notes, in contrast to the up to
144 positions that have to be memorised for the more conventional approach.&lt;/li>
&lt;li>It&amp;rsquo;s tuning-agnostic: you don&amp;rsquo;t have to learn anything new to have the fretboard memorised in different tunings, you
just need to know the &amp;ldquo;root value&amp;rdquo; that each string is tuned to.&lt;/li>
&lt;li>The number of strings don&amp;rsquo;t matter: you don&amp;rsquo;t have to learn anything new to have the fretboard memorised for a 6, 7,
8-string guitar or even a different instrument so long as it has frets at semi-tone intervals.&lt;/li>
&lt;li>It could be adapted to a microtonal instrument so long as it has frets and that the intervals between the
micro-tones is consistent, although this would require memorising an entirely new set of key value pairs.&lt;/li>
&lt;/ul>
&lt;h3 id="drawbacks-of-my-system">Drawbacks of My System&lt;/h3>
&lt;ul>
&lt;li>It requires pre-existing knowledge of modular arithmetic and the ability to quickly do mental calculations modulo 12.
While this may seem overly-complicated to someone not familiar with modular arithmetic, modular arithmetic is
actually quite simple once you get your head around it, and is worth learning for its applications in mathematics
such as in encryption.&lt;/li>
&lt;li>It still requires that you memorise 12 key-value pairs, one for each of the musical notes, although this is
considerably less memorisation that the conventional approach.&lt;/li>
&lt;li>It is not applicable to fretless instruments, such as fretless guitars, cellos, violins, etc.&lt;/li>
&lt;/ul>
&lt;p>While I&amp;rsquo;m sure that I&amp;rsquo;m not the first person in music history to think of this strategy, I have never seen it mentioned
anywhere online or in print, so I&amp;rsquo;m sharing it here in the hope that it will be useful for others as it was for me.&lt;/p></description></item><item><title>How to Download Videos with No “Download” Button from Canvas</title><link>https://ohaodha.ie/blog/how-to-download-videos-with-no-download-button-from-canvas/</link><pubDate>Mon, 26 Feb 2024 16:58:00 +0000</pubDate><guid>https://ohaodha.ie/blog/how-to-download-videos-with-no-download-button-from-canvas/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Normally, when videos such as lecture recordings are posted to Canvas, there is a &amp;ldquo;Download &lt;code>file.mp4&lt;/code>&amp;rdquo; or similar
link available on the page, or at the very least you can right-click on the playing video and select &amp;ldquo;Save media&amp;rdquo;.
Sometimes, however, there is no such link available, which is quite annoying for people like me who want to hoard
lecture content.
Below is the workaround that I&amp;rsquo;ve found to work for downloading such videos from Canvas.
It&amp;rsquo;s not quite as streamlined as I would like it to be, so suggestions and/or feedback would be appreciated, but it
works for me.&lt;/p>
&lt;p>The instructions should be browser and operating system independent, but note that I have only tested them on Arch Linux
using qutebrowser &amp;amp; Firefox.
The instructions also assume that the file on Canvas is an &lt;code>mp4&lt;/code> file, an assumption that I have not been able to verify
the reliability of: I have never encountered a non-&lt;code>mp4&lt;/code> video on Canvas so it&amp;rsquo;s possible that it stores videos
exclusively as &lt;code>mp4&lt;/code> files, but it&amp;rsquo;s also equally possible that I&amp;rsquo;ve simply coincidentally only ever encountered &lt;code>mp4&lt;/code>
files and that other video formats are supported.&lt;/p>
&lt;h2 id="steps">Steps&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>Open Canvas in your web browser and navigate to the video file that you want to download.
Before going any further, I suggest you double-check that there is no download button and that you
can&amp;rsquo;t right-click on the vide and select &amp;ldquo;Save media&amp;rdquo; to save yourself the trouble of the following steps.&lt;/p>
&lt;figure>
 &lt;img src="images/canvas_video_with_download_options.png" alt="Canvas Video with Download Options">
 &lt;figcaption>Canvas Video with Download Options&lt;/figcaption>
&lt;/figure>
&lt;figure>
 &lt;img src="images/canvas_video_without_download_options.png" alt="Canvas Video without Download Options">
 &lt;figcaption>Canvas Video without Download Options&lt;/figcaption>
&lt;/figure>
&lt;/li>
&lt;li>
&lt;p>Open your browser&amp;rsquo;s Inspector window by right-clicking anywhere on the page and selecting &amp;ldquo;Inspect&amp;rdquo; and navigate to
the &amp;ldquo;Network&amp;rdquo; tab of the Inspector window.&lt;/p>
&lt;figure>
 &lt;img src="images/network_tab_of_inspector_window.png" alt="Network Tab of the Inspector Window">
 &lt;figcaption>Network Tab of the Inspector Window&lt;/figcaption>
&lt;/figure>
&lt;/li>
&lt;li>
&lt;p>Enter the term &lt;code>mp4&lt;/code> into the &amp;ldquo;Filter&amp;rdquo; box (called &amp;ldquo;Filter URLs&amp;rdquo; on Firefox) to only display network results
relating to the video itself.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Click on the video to start playing it.
A number of network requests will be displayed in the Inspector window; we are interested in the last one.&lt;/p>
&lt;figure>
 &lt;img src="images/filter_mp4_network_tab_of_inspector_window.png" alt="Network Requests Containing the String 'mp4'">
 &lt;figcaption>Network Requests Containing the String &lt;code>mp4&lt;/code>&lt;/figcaption>
&lt;/figure>
&lt;/li>
&lt;li>
&lt;p>Right-click on the last result displayed in the Inspector window, and hover the mouse over the &amp;ldquo;Copy&amp;rdquo; option in the
drop-down menu (labelled &amp;ldquo;Copy Value&amp;rdquo; on Firefox).
This is where the steps will diverge depending on your operating system.
If you are on a UNIX-like system such as GNU/Linux or MacOS, click the &amp;ldquo;Copy as cURL&amp;rdquo; option in the sub-menu.
If you are on Windows, click the &amp;ldquo;Copy as PowerShell&amp;rdquo; option.&lt;/p>
&lt;figure>
 &lt;img src="images/copy_network_request_as_curl.png" alt="Copy Network Request as cURL">
 &lt;figcaption>Copy Network Request as cURL&lt;/figcaption>
&lt;/figure>
&lt;/li>
&lt;li>
&lt;p>Open a terminal emulator (or PowerShell on a Windows machine) and paste the command you copied into the command
prompt.
If you are using &lt;code>cURL&lt;/code>, append &lt;code>-o file_name.mp4&lt;/code> (or &lt;code>--output file_name.mp4&lt;/code>) to the command before hitting
return, substituting &lt;code>file_name.mp4&lt;/code> for the name that you wish to give the file.
The download directory can be specified by prepending the path to that directory to the file name, otherwise it
will default to your home directory.&lt;/p>
&lt;figure>
 &lt;img src="images/run_curl_command_canvas.png" alt="Run the cURL Command">
 &lt;figcaption>Run the cURL Command&lt;/figcaption>
&lt;/figure>
&lt;/li>
&lt;/ol></description></item></channel></rss>