<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://michaelchadwick.info/feed.xml" rel="self" type="application/atom+xml" /><link href="https://michaelchadwick.info/" rel="alternate" type="text/html" /><updated>2026-04-22T16:36:47-07:00</updated><id>https://michaelchadwick.info/feed.xml</id><title type="html">MC.Info</title><subtitle>code, audio, and life developments by michael chadwick</subtitle><author><name>Michael Chadwick</name></author><entry><title type="html">Making SoundLister Work Offline</title><link href="https://michaelchadwick.info/blog/2026/04/03/making-soundlister-work-offline/" rel="alternate" type="text/html" title="Making SoundLister Work Offline" /><published>2026-04-03T17:01:00-07:00</published><updated>2026-04-03T17:01:00-07:00</updated><id>https://michaelchadwick.info/blog/2026/04/03/making-soundlister-work-offline</id><content type="html" xml:base="https://michaelchadwick.info/blog/2026/04/03/making-soundlister-work-offline/"><![CDATA[<p>SoundLister lets you listen to arbitrary audio using a web-based player and playlist, but if you lose network then it fails to be useful. That is, until now.</p>

<!--more-->

<h2 id="intro-easing-you-into-the-topic">INTRO: Easing You Into the Topic</h2>

<p>While <a href="https://deckdle.neb.host">Deckdle</a> is probably my most successful and continuously used web app, I think my most useful one is <a href="https://soundlister.neb.host">SoundLister</a>, mainly because whenever I’m working on new music it gets used heavily. Adding temp tracks to something like iTunes or even iCloud Files is just not as simple or efficient, and have drawbacks I don’t want (they are not finished so they don’t yet belong in my music collection, but I still need a playlist of them to listen and scrub and loop over while I’m doing listening tests). Thus, many years ago I decided to make a web app so I could drop audio files in a directory on a server and listen to them on any Internet-enabled device. It works like a charm and more details can be found at <a href="/blog/2023/01/09/soundlister-is-live/">my initial unveiling blog post</a> (note: despite what it says then, iOS eventually fixed the ‘screen off, so next track does not play bug’!).</p>

<h2 id="verse-telling-you-a-story">VERSE: Telling You a Story</h2>

<p>The only real problem with SoundLister is a problem any web app that is delivered over a network has: if the network disappears, you can’t get to the web app. This could happen if you were on a flight or driving through a low signal area or because all wireless towers and satellites blew up instantaneously. I don’t know, man, crazy stuff happens sometimes!</p>

<p>However, there is a way to somewhat get around this: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Workers</a>.</p>

<aside class="right">
  I'd lightly used <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">Web Workers</a> on my other project <a href="https://neb.host/apps/gemwarrior">Gem Warrior</a>, mainly to see if I could. The site <a href="https://github.com/michaelchadwick/gemwarrior-web/blob/master/assets/js/app/worker.js">caches some text files</a> representing your avatar's status (sitting, standing, etc.) and uses the Web Worker to do the grunt work. Of course, loading text files is <strong>not</strong> CPU-intensive at all, so a Web Worker was overkill, but it got my mind in the door.
</aside>

<p>Service Workers act as a proxy, essentially doing a full cache of whatever you tell it to cache, and so in the future if your app requests files, like CSS or image or MP3, then it checks the cache first before going to the network. Thus, if you install and register one, you go from the initial “Web App Live From the Network” to a subsequent “Web App From the Cache”, negating the need to re-download anything, and making your “web app” a “local app” for all intents and purposes. It’s kind of magical!</p>

<p>The basic code needed is the <code class="language-plaintext highlighter-rouge">sw.js</code> file itself, which handles the event life cycle of the Service Worker itself, and a <code class="language-plaintext highlighter-rouge">navigator.serviceWorker.register</code> method that registers the Service Worker to do its thing.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.js</span>
<span class="kd">const</span> <span class="nx">SL_SERVICE_WORKER_PATH</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/sw.js</span><span class="dl">'</span>

<span class="nx">SoundLister</span><span class="p">.</span><span class="nx">registerServiceWorker</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="dl">'</span><span class="s1">serviceWorker</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">try</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">registration</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span>
        <span class="nx">SL_SERVICE_WORKER_PATH</span><span class="p">,</span> <span class="p">{</span>
          <span class="na">scope</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span>
        <span class="p">}</span>
      <span class="p">)</span>

      <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_logStatus</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker registered</span><span class="dl">'</span><span class="p">,</span> <span class="nx">registration</span><span class="p">)</span>

      <span class="k">if </span><span class="p">(</span><span class="nx">registration</span><span class="p">.</span><span class="nx">installing</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_logStatus</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service worker installing</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">}</span> <span class="k">else</span> <span class="k">if </span><span class="p">(</span><span class="nx">registration</span><span class="p">.</span><span class="nx">waiting</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_logStatus</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service worker installed</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">}</span> <span class="k">else</span> <span class="k">if </span><span class="p">(</span><span class="nx">registration</span><span class="p">.</span><span class="nx">active</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_logStatus</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service worker active</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">}</span>
    <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Service Worker failed to register</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// sw.js</span>
<span class="kd">const</span> <span class="nx">SL_CACHE_STATIC_KEY</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">soundlist-cache-static-v1</span><span class="dl">'</span>

<span class="nb">self</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">install</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">event</span><span class="p">.</span><span class="nf">waitUntil</span><span class="p">(</span>
    <span class="nx">caches</span>
      <span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="nx">SL_CACHE_STATIC_KEY</span><span class="p">)</span>
      <span class="p">.</span><span class="nf">then</span><span class="p">((</span><span class="nx">cache</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">staticAssets</span> <span class="o">=</span> <span class="p">[</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/index.html</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/assets/css/app.css</span><span class="dl">'</span><span class="p">]</span>
        <span class="k">return</span> <span class="nx">cache</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="nx">staticAssets</span><span class="p">)</span>
      <span class="p">})</span>

      <span class="c1">// ...we will come back to this part...</span>

  <span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>

<p>What a Service Worker is <em>not</em> is something that provides an intuitive way to cache not only static files <strong>BUT ALSO</strong> a dynamic list of audio files that can change depending on the instance or day.</p>

<h2 id="chorus-the-part-you-will-remember">CHORUS: The Part You Will Remember</h2>

<p>PHP to the rescue!</p>

<p>That is to say, of course, that I was already using a PHP script to scan a directory and build a nice array of all the subdirs and their files so that I could use it build the playlist interface.</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>
<span class="c1">// to echo out and return to JS for further processing</span>
<span class="nv">$files</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>

<span class="nv">$iterator</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RecursiveIteratorIterator</span><span class="p">(</span>
  <span class="k">new</span> <span class="nc">RecursiveDirectoryIterator</span><span class="p">(</span>
    <span class="s1">'../audio/'</span><span class="p">,</span>
    <span class="nc">FilesystemIterator</span><span class="o">::</span><span class="no">SKIP_DOTS</span>
  <span class="p">)</span>
<span class="p">);</span>

<span class="nv">$curDir</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>

<span class="k">foreach</span> <span class="p">(</span><span class="nv">$iterator</span> <span class="k">as</span> <span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">in_array</span><span class="p">(</span><span class="nb">strtolower</span><span class="p">(</span><span class="nv">$file</span><span class="o">-&gt;</span><span class="nf">getExtension</span><span class="p">()),</span> <span class="nv">$exts</span><span class="p">))</span> <span class="p">{</span>
    <span class="nv">$path</span><span class="p">[</span><span class="s1">'basename'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$file</span><span class="o">-&gt;</span><span class="nf">getBasename</span><span class="p">();</span>
    <span class="nv">$path</span><span class="p">[</span><span class="s1">'filename'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$file</span><span class="o">-&gt;</span><span class="n">pathname</span><span class="p">;</span>

    <span class="c1">// etc.</span>

    <span class="nv">$files</span><span class="p">[</span><span class="nv">$dirIndex</span><span class="p">][]</span> <span class="o">=</span> <span class="nv">$path</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">echo</span> <span class="nb">json_encode</span><span class="p">(</span><span class="nv">$files</span><span class="p">);</span>
<span class="cp">?&gt;</span>
</code></pre></div></div>

<p>Once the script returns its value Javascript, one is left with a <code class="language-plaintext highlighter-rouge">const response = fetch(SL_PHP_SCRIPT)</code> call that then feeds into a <code class="language-plaintext highlighter-rouge">const data = response.json()</code> call that is then processed into the UI.</p>

<p>However, the Service Worker <code class="language-plaintext highlighter-rouge">install</code> method cannot have any arguments, so in order to load things dynamically into its cache when it is installed you need to have a manifest of sorts to tell it what to cache once you <em>do</em> know. Thus, I had to do an extra step in my PHP script, like so:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>
<span class="c1">// to echo out and return to JS for further processing</span>
<span class="nv">$files</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
<span class="c1">// to save to JSON for service worker manifest</span>
<span class="c1">// [</span>
<span class="c1">//   '/audio/subdir1/song1.mp3',</span>
<span class="c1">//   '/audio/subdir1/song2.mp3',</span>
<span class="c1">//   '/audio/subdir2/song1.mp3'</span>
<span class="c1">// ]</span>
<span class="nv">$filePaths</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>

<span class="nv">$iterator</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RecursiveIteratorIterator</span><span class="p">(</span>
  <span class="k">new</span> <span class="nc">RecursiveDirectoryIterator</span><span class="p">(</span>
    <span class="s1">'../audio'</span><span class="p">,</span>
    <span class="nc">FilesystemIterator</span><span class="o">::</span><span class="no">SKIP_DOTS</span>
  <span class="p">)</span>
<span class="p">);</span>

<span class="nv">$curDir</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>

<span class="k">foreach</span> <span class="p">(</span><span class="nv">$iterator</span> <span class="k">as</span> <span class="nv">$file</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">in_array</span><span class="p">(</span><span class="nb">strtolower</span><span class="p">(</span><span class="nv">$file</span><span class="o">-&gt;</span><span class="nf">getExtension</span><span class="p">()),</span> <span class="nv">$exts</span><span class="p">))</span> <span class="p">{</span>
    <span class="nv">$path</span><span class="p">[</span><span class="s1">'basename'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$file</span><span class="o">-&gt;</span><span class="nf">getBasename</span><span class="p">();</span>
    <span class="nv">$path</span><span class="p">[</span><span class="s1">'filename'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$file</span><span class="o">-&gt;</span><span class="n">pathname</span><span class="p">;</span>

    <span class="c1">// etc.</span>

    <span class="nv">$files</span><span class="p">[</span><span class="nv">$dirIndex</span><span class="p">][]</span> <span class="o">=</span> <span class="nv">$path</span><span class="p">;</span>
    <span class="nv">$filePaths</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'../audio'</span> <span class="mf">.</span> <span class="s1">'/'</span> <span class="mf">.</span> <span class="nv">$path</span><span class="p">[</span><span class="s1">'subdirPath'</span><span class="p">]</span> <span class="mf">.</span> <span class="s1">'/'</span> <span class="mf">.</span> <span class="nv">$path</span><span class="p">[</span><span class="s1">'basename'</span><span class="p">];</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// for Service Worker</span>
<span class="nv">$jsonString</span> <span class="o">=</span> <span class="nb">json_encode</span><span class="p">(</span>
  <span class="nv">$filePaths</span><span class="p">,</span>
  <span class="no">JSON_UNESCAPED_SLASHES</span> <span class="o">|</span> <span class="no">JSON_PRETTY_PRINT</span>
<span class="p">);</span>
<span class="c1">// make sure web server user has write access to the directory this is in!</span>
<span class="nv">$fp</span> <span class="o">=</span> <span class="nb">fopen</span><span class="p">(</span><span class="s1">'../json/audio_manifest.json'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">);</span>
<span class="nb">fwrite</span><span class="p">(</span><span class="nv">$fp</span><span class="p">,</span> <span class="nv">$jsonString</span><span class="p">);</span>
<span class="nb">fclose</span><span class="p">(</span><span class="nv">$fp</span><span class="p">);</span>

<span class="c1">// for immediate return to JS</span>
<span class="k">echo</span> <span class="nb">json_encode</span><span class="p">(</span><span class="nv">$files</span><span class="p">);</span>
<span class="cp">?&gt;</span>
</code></pre></div></div>

<p>Two notes:</p>

<ol>
  <li>The <code class="language-plaintext highlighter-rouge">JSON_UNESCAPED_SLASHES</code> flag was necessary so PHP wouldn’t write <code class="language-plaintext highlighter-rouge">\/audio\/subdir1\/song1.mp3</code> in its output, and the <code class="language-plaintext highlighter-rouge">JSON_PRETTY_PRINT</code> flag makes its output more human-readable (not necessary, but nice).</li>
  <li>The comment about web server user access was important because I was just getting 500 errors from my script until I realized the group that owned it did not have the ability to create the <code class="language-plaintext highlighter-rouge">json</code> file I was asking it to create.</li>
</ol>

<h2 id="bridge-slight-tangent-for-variety">BRIDGE: Slight Tangent for Variety</h2>

<p>As much as I want anyone reading this to listen to <em>my</em> music and fall in love with <strong>all of it</strong> and listen to it <strong><em>all the time</em></strong> and reach out to me about it, there are other people making music that is equally good (but really probably way more good), and you should check it out.</p>

<p>While writing this post I’ve been jamming out to Angine de Poitrine <a href="https://anginedepoitrine.bandcamp.com/album/vol-1">Vol. 1</a> and <a href="https://anginedepoitrine.bandcamp.com/album/vol-ii">Vol. II</a> and this stuff RULES SO HARD.</p>

<h2 id="chorus-the-part-you-remembered">CHORUS: The Part You Remembered</h2>

<p>PHP to the rescue!</p>

<p>Not that saying ‘PHP is cool’ has ever been, well, <em>cool</em>, I’d still like to shout it out because when I need to drop to the <em>backend</em> in order to do things frontend Javascript won’t do I still default to PHP. Python is really popular, too, and it seems to be getting more popular as LLM and AI agent programming becomes the norm, but it’s not in my gut like PHP.</p>

<p>My first ever real web app was your typical *AMP (Mac/Win/Linux, Apache, MySQL, PHP) type of project, and it really laid the groundwork for how I think to make a web application to this day. They released <strong>8.5</strong> back in November 2025 and <strong>8.6</strong> is already in development, so support remains strong even if it’s not hip to use.</p>

<h2 id="outro-transition-out-of-here">OUTRO: Transition Out of Here</h2>

<p>As a real test of this new Service Worker, I took a drive and loaded up SoundLister on my phone (with network on, obvs). Once it was done, I turned on Airplane Mode and refreshed the site. Lo and behold, it loaded and played audio like it was a local app with no need for network activity! It felt like real magic, honestly. There was a hiccup at one point where a track stopped playing out of nowhere, but a refresh of the site fixed things.</p>

<p>Since this blog post is so musically-themed, I will leave you with the outro of an actual song in the works:</p>
<audio controls="">
  <source src="/assets/audio/posts/undu-outro.mp3" type="audio/mpeg" />
No &lt;audio&gt; support in your browser found.
</audio>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="offline" /><category term="service-worker" /><category term="soundlister" /><category term="webdev" /><summary type="html"><![CDATA[SoundLister lets you listen to arbitrary audio using a web-based player and playlist, but if you lose network then it fails to be useful. That is, until now.]]></summary></entry><entry><title type="html">YouTube Video ID Allowance</title><link href="https://michaelchadwick.info/blog/2026/02/02/youtube-video-id-allowance/" rel="alternate" type="text/html" title="YouTube Video ID Allowance" /><published>2026-02-02T01:22:00-08:00</published><updated>2026-02-02T01:22:00-08:00</updated><id>https://michaelchadwick.info/blog/2026/02/02/youtube-video-id-allowance</id><content type="html" xml:base="https://michaelchadwick.info/blog/2026/02/02/youtube-video-id-allowance/"><![CDATA[<p>All current existing YouTube videos can be filtered by a single 11-character id. Does that really seem like enough?</p>

<!--more-->

<p>Have you ever wondered how the many, many, MANY YouTube videos that apparently exist can really be filtered by such a small id? It doesnt seem nearly enough, especially when we live in a world with systems that use alphanumeric ids of 16, 20, 24, and more. Of course, my grasp of big numbers is limited and deplorable, so I did a little more digging.</p>

<p>According to some brief research it seems like YouTube (as of sometime last year) has about ~15 <em>billion</em> videos, with upload rates each day in the <em>millions</em>. The <code class="language-plaintext highlighter-rouge">id</code> that each video uses is an 11-character combination of three character sets: <code class="language-plaintext highlighter-rouge">A-Z (26)</code>, <code class="language-plaintext highlighter-rouge">a-z (26)</code>, and <code class="language-plaintext highlighter-rouge">[0-9] (10)</code>, which makes the number of choices for each character <code class="language-plaintext highlighter-rouge">62</code>. Remembering my high school math lead me to a permutation to figure out:</p>

<p><sub>n</sub>P<sub>r</sub> = n! / (n - r)! -&gt; <sub>62</sub>P<sub>11</sub> = 62! / (62 - 11)!</p>

<p>This results in about <em>20 quintillion</em> (i.e. 20 with 18 zeroes) possibilities for a YouTube video ID. The current data set is nowhere near hitting that number. Thus, an 11-character ID really can easily filter all those videos.</p>

<p>This thought experiment has now concluded, but go read this <a href="https://www.bbc.com/future/article/20250213-youtube-at-20-a-computer-that-drunk-dials-online-videos-reveals-statistics-that-google-doesnt-want-you-to-know">BBC article</a> which gets into it more.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="numbers" /><category term="thought" /><category term="youtube" /><summary type="html"><![CDATA[All current existing YouTube videos can be filtered by a single 11-character id. Does that really seem like enough?]]></summary></entry><entry><title type="html">Anti-Social Mediation</title><link href="https://michaelchadwick.info/blog/2026/01/31/antisocial-mediation/" rel="alternate" type="text/html" title="Anti-Social Mediation" /><published>2026-01-31T00:00:00-08:00</published><updated>2026-01-31T00:00:00-08:00</updated><id>https://michaelchadwick.info/blog/2026/01/31/antisocial-mediation</id><content type="html" xml:base="https://michaelchadwick.info/blog/2026/01/31/antisocial-mediation/"><![CDATA[<p>After many years of sharing my life in a public broadcast kind of way, a server upgrade kills my love for even the last bastion of social media I still use consistently.</p>

<!--more-->

<p>I just killed my single-user, hosted instance of <a href="https://joinmastodon.org">Mastodon</a>.</p>

<p>Perhaps a victim of the <a href="/blog/2026/01/23/the-binge-purge-tech-cycle/">binge-purge cycle</a>?</p>

<h2 id="context">CONTEXT</h2>

<p>When I first heard about Mastodon, and that it could be self-hosted, I was very interested. I’ve had my own Linux VPS (Virtual Private Server) for many years now, and I love to try out new self-hosted software. It started with web and file serving. Then, slowly but surely, I’ve been moving services that used to be on a server I had no control over to my own server which I do. I was also excited to try a social media/microblogging platform that didn’t use an algorithm, nor that was owned by a tech billionaire.</p>

<p>My server started on Debian 9(?), and moving to 10 didn’t bring any major issues, but I was also running a lot less on it. Moving to 11 also didn’t bring any major issues that I can remember. Even a recent move from 11 to 12 was no big deal, but going to 13 borked several things: my mail server, a web site using Phusion Passenger, and my self-hosted version of Mastodon. The other two are going to need more research and work at some point, but I attempted to ameliorate the latter one today. Before I even started, I was tired. Knowing how something works and being able to fix it are things I value, but there’s a cost to the knowledge and the application of it. I have a limit on how much I want to endure, and I guess I finally hit it today.</p>

<p>I’ve been using Mastodon for several years now, occasionally posting to it, and using it to keep up on other creative people who did fun stuff like make games, music, animations, and other art. I wish it were used by more people I know, but I understand why it is not. Regardless, I believe it truly is the best way to be social online today, in a public way, to a wide audience, without being subject to tech giant algorithm nonsense.</p>

<p>Unfortunately, like every other microblogging platform that has ever existed, if you don’t already have an audience, it’s not some guarantee to getting one. Thus, a lot of the time it felt like I was posting into a void, much like when I make a blog like this one. The blog is just a <em>part</em> of my personal website, however, and so failing to write new blog posts often doesn’t mean I can’t tinker with the rest of it. Mastodon, on the other hand, is just the blog, and nothing else.</p>

<p>Not being a stranger to putting things online that no one looks at, this didn’t bother me, and so I used Mastodon, anyway. Running a Mastodon <em>server</em>, however, can be frustrating like running any other kind of server, and the difficulty of upgrading a Linux OS coupled with some confusion about the proper <code class="language-plaintext highlighter-rouge">postgresql</code> version to use resulted in losing my years-old account and friends list and post history. With more effort expended, I could go to a backup, extract said data, and put more hours into trying to fix things. But over the years I’ve grown almost as much weary at the concept of this particular brand of devops as I am curious. In other words, I’m getting too old for this shit.</p>

<p>So, I made a decision: just blow away the whole thing. The server maintenance and occasional bugfixing and Google sleuthing that comes with the whole thing, espcially when I barely use it, just doesn’t add up to enough value for it to feel worth it to me right now. It means, however, that what little “social media” I actively contribute to just took a dive. My time spent on Facebook or Instagram or X/Twitter or whatever can be measured in “barely” or “non-existent”, so removing Mastodon means I’m down to just my <a href="https://michaelchadwick.info">website</a>. Just like the old days, I guess. Except in the old days, I had LiveJournal. I didn’t control it, but I posted to it often, as did my friends, and we had a community. That particular community died years ago, however, and I feel like social on the web has never been the same since.</p>

<h2 id="for-the-road">FOR THE ROAD</h2>

<p>I’ll miss the people I found on Mastodon, and maybe I’ll join some non-self-hosted instance someday, but for now…I feel a burden lifted. Mastodon was kind of a behemoth to host, and while I probably made it more difficult on myself not using Docker (it would probably have made the dependency management much less of a concern), the overall utility of it as a platform plus the exhausted frustration made it something I could see myself going without. Tootles for now.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="debian" /><category term="facebook" /><category term="instagram" /><category term="livejournal" /><category term="mastodon" /><category term="social-media" /><category term="twitter" /><summary type="html"><![CDATA[After many years of sharing my life in a public broadcast kind of way, a server upgrade kills my love for even the last bastion of social media I still use consistently.]]></summary></entry><entry><title type="html">The Binge-Purge Tech Cycle</title><link href="https://michaelchadwick.info/blog/2026/01/23/the-binge-purge-tech-cycle/" rel="alternate" type="text/html" title="The Binge-Purge Tech Cycle" /><published>2026-01-23T00:00:00-08:00</published><updated>2026-01-23T00:00:00-08:00</updated><id>https://michaelchadwick.info/blog/2026/01/23/the-binge-purge-tech-cycle</id><content type="html" xml:base="https://michaelchadwick.info/blog/2026/01/23/the-binge-purge-tech-cycle/"><![CDATA[<p>You begin life simple and slowly become more complex. Within that life there are often multiple periods of growth and reduction. This is evident quite clearly in one’s interests. My life is no different.</p>

<!--more-->

<p>You are born. You no longer get all nutrients from another life form (well, ok, you still will for a while, but the process of stopping this begins). It’s a lot brighter, a lot noisier, and a lot weirder than the small, warm, comfortable space you’ve been in for months. However, while you were gestating, you grew from a simple organism to a complex one. And now that you’re on The Outside, you’re only going to get a lot more complex.</p>

<p>…</p>

<p>I’m now in my mid-40s. I’ve experienced things. I’ve learned things. My interests have broadened. Regardless, I go through periods where I’m seriously all about adding more stuff to my life, and then periods where I want to just excise any and everything non-essential.</p>

<p>As a kid, I was into collecting comic books and trading cards of the sports and comic varieties. These collections now sit in boxes in my garage, never looked at for decades. I did some research on whether they’re worth anything, and they’re not. I would like to purge them from my life as they don’t bring any value anymore, but it’s hard to just throw them away. So…they sit.</p>

<p>…</p>

<p>Since “getting into computers” in the mid-90s, being able to learn and toy with new things, and thusly <em>binge</em> them, has become quite easy. Bingeing on something when it’s new or once-new-and-now-refreshed is often followed by a purge once the newness wears off, or the administration to handle it becomes mentally taxing.</p>

<div class="binge-purge">
  <span class="binge">BINGE</span>: I can <strong>run applications</strong> on this thing! I will try this application and that application and this application, because it's so easy and fun! Now I have a LOT of apps on my computer. App stores made the process easier, and now Github has become the place I look first for new tools and technology since I can get them and see how they're made and modify them.<br />
  <span class="purge">PURGE</span>: My hard drive space is filling up and I realize I haven't used this or that application in a long time, so let's get rid of it.<br /><br />

  <span class="binge">BINGE</span>: I can <strong>collect music</strong> to listen to! During college I "acquired" a lot of things, and then later when I was gainfully employed I bought a lot of things. Between the two, I've got a music library that's tens of thousands of songs large, good enough for ~2 months of constant listening without repeats.<br />
  <span class="purge">PURGE</span>: Despite lots of space, looking at 20,000+ (not even mentioning Spotify, Apple Music, et. al.) tracks makes choosing any one thing to listen to near-impossible, so I just keep occasionally getting new stuff, listening to it, and then repeating that cycle, and ignore the years of music I already have. Like photos, there's no real need to purge them since they don't lose value and I can always listen later.
</div>

<p>You can see where this has gone and will continue to go. New things are fun and as cheap storage space ballooned over the decades, acquiring and storing it all got easier and easier. The Internet has allowed more people access to more things and so much of it can be digitized so it’s accessible.</p>

<div class="binge-purge">
  <span class="binge">BINGE</span>: I can <strong>browse and save favorite websites</strong> in a browser on this thing! I have many different folders of bookmarks now! I also have a Read-It-Later web service I self-host that has thousands of entries, so that I can largely not look at or refer to after I save them.<br />
  <span class="purge">PURGE</span>: Why did I save this website I haven't visited in forever as a bookmark? Time to remove. Also, this directory of sites all pertain to some activity I don't do anymore. Gone.<br /><br />

  <span class="binge">BINGE</span>: I can <strong>MAKE websites</strong> and <strong>buy domains</strong> with this thing! Since I <em>know how to</em> make a website for any old idea that comes across my brain, I <em>should</em> probably do that. Oh, wow, I have a <a href="https://omni.neb.host">LOT</a> of websites now.<br />
  <span class="purge">PURGE</span>: I don't need so many domains. No one goes to these sites, and I haven't worked on them in a while, so axe 'em. They can be kept as subdomains of fewer domains! `/time-passes` Wait, no one visits these sub-domains, not even me, so...make them sub-directories so I don't have to see so many items when I do a <code>ls -la /var/www</code>.<br /><br />

  <span class="binge">BINGE</span>: I can <strong>take and hoard pictures</strong> to look at! Modern mobile devices with excellent cameras make taking photos easy and so I will do that. Now I have years and years of photos that I probably won't look at often, but I guess I'm glad I have them?<br />
  <span class="purge">PURGE</span>: The cloud storages like iCloud/Google/OneDrive/etc. and a large backup drive make this a thing I can just ignore, so no purging ever really happens, but I should probably cull them? I'm going to take more pictures, so the collection is only going to grow...
</div>

<p>This is not a new thing because of digital life, as people have hoarded material things for centuries, buying bigger homes to store more <em>stuff</em>. As I’ve gone through life, my place of residence and the potential amount of storage I have increased, and I’ve definitely spent some time filling it. However, the digital avenue allows for <em>immense scale</em> and <em>speed</em> in harboring binge-dom. If you keep on this road and never stop, then you might become a “hoarder” and, at the most extreme, become fodder for a show. If you course-correct and go the other way without stopping, you might join the “minimalist” cult. As I’ve learned over the years, however, is that long-term extremism is not healthy.</p>

<div class="binge-purge">
  <span class="binge">BINGE</span>: I can <strong>play video games</strong> on this thing! Steam and GOG and all the other marketplaces that have seemingly limitless games to offer make it very easy to build a monumental collection of games. I've got plenty and I know I haven't played them all. Looking at the possibilities to choose from brings that <a href="https://en.wikipedia.org/wiki/Resource_curse">paradox of abundance</a> into sharp relief.<br />
  <span class="purge">PURGE</span>: Like other media, I don't purge my collection, but I ignore existing games and focus on a few new things every once in a while until I beat them or grow tired of them. Then the cycle continues.<br /><br />

  <span class="binge">BINGE</span>: I can <strong>learn programming languages</strong> on this thing! Now I have a <code>~/Code</code> folder full of various projects big and small in lots of languages, just because I <em>can</em>.<br />
  <span class="purge">PURGE</span>: New projects will fill up my local machine as I work on then. Once they're done-ish and enter maintenance mode I will tend to move them off to my backup storage. I like having lots of projects to go between, so this will build up again until I get overwhelmed or lose interest, and then they will get purged to backup again.
</div>

<p>Even outside of the digital world, I go through periods of binge-purge.</p>

<div class="binge-purge">
  <span class="binge">BINGE</span>: <strong>E-bikes are amazing</strong>! A few years ago I test-rode one for the first time, and was instantly sold on the idea. I like riding a bike, but the landscape of my city is quite hilly, and I'm not very physically fit. This is the answer! I bought one, and then a while later I bought two more for the rest of my family. We will all go on bike rides together!<br />
  <span class="purge">PURGE</span>: And we did...once or twice. Then the reality sets in that I'm the most into this thing, and the other two bikes largely languish in the garage. Time to put two of three bikes on the market, and probably only go on sporadic rides.<br /><br />

  <span class="binge">BINGE</span>: <strong>3D printers are rad</strong>! The family got one for the most recent Christmas, and we spent most of the holiday listening to the <em>brrrrring</em> of the print heads moving quickly back and forth, creating lots of fun (and sometimes useful!) widgets and doohickeys.<br />
  <span class="purge">PURGE</span>: While we will print more stuff in the future, the initial buzz of it has worn off and it currently sits silent. We got a laser printer a few months ago and it had its initial hey-day, too, but I barely register it being there now.
</div>

<p>I could do an AskReddit to see if I’m the only one who has this issue, but without even trying I’m 100% sure that is false. Perhaps this binge-purge cycle is on a spectrum, like with many character traits. We all do it to some degree, and we all probably move around on the spectrum over time.</p>

<p>As a final meta-note, I once did blogging on the Internet SO VERY MUCH, sometimes blogging multiple times a day. Now, it has taken me literally days to even finish this one. And the last one I did was months ago.</p>

<p>No great conclusion to any of this, but glad I finally got it out, as it’s something that’s rolled around in my head for years. To wit: moderation in all things, including moderation.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="reflection" /><summary type="html"><![CDATA[You begin life simple and slowly become more complex. Within that life there are often multiple periods of growth and reduction. This is evident quite clearly in one's interests. My life is no different.]]></summary></entry><entry><title type="html">Defolding My Love For 2D Gamedev</title><link href="https://michaelchadwick.info/blog/2025/10/27/defolding-my-love-for-2d-gamedev/" rel="alternate" type="text/html" title="Defolding My Love For 2D Gamedev" /><published>2025-10-27T00:00:00-07:00</published><updated>2025-10-27T00:00:00-07:00</updated><id>https://michaelchadwick.info/blog/2025/10/27/defolding-my-love-for-2d-gamedev</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/10/27/defolding-my-love-for-2d-gamedev/"><![CDATA[<p>Season two of Hacking the Grepson is all about making a game. After some consideration, the decision to use Love2D and/or Defold, two different game engines, was made and a concentrated effort to tinker with both of them has begun.</p>

<!--more-->

<h2 id="context">CONTEXT</h2>

<p>For the first 100 episodes of <a href="https://hackingthegrepson.com">Hacking the Grepson</a>, the podcast about programming and related topics that my friend Matt and I do every other week, we did various topics. These topics include the following: programming languages, Wordle, software development life cycle, documentation, Factorio, technical debt, data analytics, Slay the Spire, version control, and many more.</p>

<p>You may have noticed a few proper nouns in that list, and those proper nouns are all what? GAMES. Many people become programmers to make games, and then end up working on spreadsheets or websites to pay the bills because those are generally easier and more applicable to more people. However, that itch to make a super cool game that’s fun and addictive never quite goes away.</p>

<h2 id="action">ACTION</h2>

<p>Thus! For season 2 (episode 101+) we decided to try something different and just have one topic for as many episodes as it takes: Make a Game (Together). That means we have had to decide what kind of game we are going to make, what to make it in, and then…make it and deploy it to the world.</p>

<p>After a couple of episodes (and some offline chatter), we have both gravitated to using <a href="https://love2d.org">Love2D</a> (for prototyping) and <a href="https://defold.com">Defold</a> (for the final product). I’d played with the former back in the day (<a href="https://playbalatro.com">Balatro</a> was made with it!), but never even heard of Defold until Matt brought it up (apparently it was created by an offshoot of <a href="https://avalanchestudios.com/">Avalanche Studios</a>, the makers of Just Cause). They both use <a href="https://lua.org">Lua</a>, a very popular scripting language that seems to be embedded in a lot of other things (I remember first experiencing it when investigating World of Warcraft mods waaaaay back in the day).</p>

<p>As far as which kind of game we are going to make, or which platform, or which control scheme, or which theme, or which mechanics, or <code class="language-plaintext highlighter-rouge">[insert-other-aspect-of-game]</code>…that is still being actively decided. At least we know which tool(s) we are going to use, and are both getting the hang of both. I’ve been making prototypes, going through tutorials, and testing the online documentation and wikis of both tools.</p>

<h3 id="love2d">LOVE2D</h3>

<p>Love (or LÖVE, as the creators stylize it, and as Love2D, which is how I will refer to it to make it distinct from the emotion/concept) is a <a href="https://github.com/love2d/love">free, open-source framework</a> built on Lua, which means it takes the base Lua scripting language and adds a bunch of <code class="language-plaintext highlighter-rouge">love</code> (objects/classes) to it, and allows you to compile and run it (or export it to various things). Love2D has no IDE, so you write your code in a text editor of your choice, and build whatever pre-existing assets you want to use in whichever tool you choose, referencing them in code, but with no Love2D-specific GUI tooling to manipulate or organize them.</p>

<p>Here is some example code from a 2-player game’s drawing routine:</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="nc">love</span><span class="p">.</span><span class="nf">draw</span><span class="p">()</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">setBackgroundColor</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span>

  <span class="c1">-- draw player1</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">setCanvas</span><span class="p">(</span><span class="n">screenCanvas</span><span class="p">)</span>
    <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">clear</span><span class="p">()</span>
    <span class="n">drawEntities</span><span class="p">(</span><span class="n">player1</span><span class="p">)</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">setCanvas</span><span class="p">()</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">draw</span><span class="p">(</span><span class="n">screenCanvas</span><span class="p">)</span>

  <span class="c1">-- draw player2</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">setCanvas</span><span class="p">(</span><span class="n">screenCanvas</span><span class="p">)</span>
    <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">clear</span><span class="p">()</span>
    <span class="n">drawEntities</span><span class="p">(</span><span class="n">player2</span><span class="p">)</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">setCanvas</span><span class="p">()</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">draw</span><span class="p">(</span><span class="n">screenCanvas</span><span class="p">,</span> <span class="mi">400</span><span class="p">)</span>

  <span class="c1">-- Add a line to separate the screens</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="n">line</span><span class="p">(</span><span class="n">WinWidthCenter</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">WinWidthCenter</span><span class="p">,</span> <span class="n">WinHeight</span><span class="p">)</span>

  <span class="c1">-- draw scores</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="nb">print</span><span class="p">(</span><span class="s2">"$1: "</span> <span class="o">..</span> <span class="n">player1</span><span class="p">.</span><span class="n">score</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
  <span class="n">love</span><span class="p">.</span><span class="n">graphics</span><span class="p">.</span><span class="nb">print</span><span class="p">(</span><span class="s2">"$2: "</span> <span class="o">..</span> <span class="n">player2</span><span class="p">.</span><span class="n">score</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<h3 id="defold">DEFOLD</h3>

<p>Defold is a fully-fledged game engine and IDE (write your code and build projects from the application) also built on Lua. You spend most of your time in the application, writing code, creating tilesets and animations, and organizing things inside Game Objects and Collections. You still build your pre-existing assets in whichever tool you choose, but once inside Defold there are more GUI affordances for manipulating and integrating them.</p>

<p>Here is an excerpt from an avatar’s update routine:</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="nf">update</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
  <span class="o">...</span>

  <span class="kd">local</span> <span class="n">anim</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">current_anim</span>
  <span class="kd">local</span> <span class="n">dx</span><span class="p">,</span> <span class="n">dy</span> <span class="o">=</span> <span class="n">axis_to_vec</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">input</span><span class="p">)</span>
  <span class="kd">local</span> <span class="n">moving</span> <span class="o">=</span> <span class="p">(</span><span class="n">dx</span> <span class="o">~=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">dy</span> <span class="o">~=</span> <span class="mi">0</span><span class="p">)</span>

  <span class="c1">-- Update facing direction and animation when moving</span>
  <span class="k">if</span> <span class="n">dx</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">then</span>
    <span class="n">anim</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="s2">"left"</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">facing_direction</span> <span class="o">=</span> <span class="n">vmath</span><span class="p">.</span><span class="n">vector3</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">elseif</span> <span class="n">dx</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span>
    <span class="n">anim</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="s2">"right"</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">facing_direction</span> <span class="o">=</span> <span class="n">vmath</span><span class="p">.</span><span class="n">vector3</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">elseif</span> <span class="n">dy</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">then</span>
    <span class="n">anim</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="s2">"back"</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">facing_direction</span> <span class="o">=</span> <span class="n">vmath</span><span class="p">.</span><span class="n">vector3</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">elseif</span> <span class="n">dy</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">then</span>
    <span class="n">anim</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="s2">"front"</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">facing_direction</span> <span class="o">=</span> <span class="n">vmath</span><span class="p">.</span><span class="n">vector3</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="k">else</span>
    <span class="c1">-- When stopped, switch to idle only if facing front, otherwise keep current animation</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">facing_direction</span> <span class="o">==</span> <span class="n">vmath</span><span class="p">.</span><span class="n">vector3</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="k">then</span>
      <span class="n">anim</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="s2">"idle"</span><span class="p">)</span>
    <span class="k">else</span>
      <span class="n">anim</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">current_anim</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="o">...</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="progress-thus-far">PROGRESS THUS FAR</h2>

<p>After almost two months of this experiment, and many hours put into both tools, it’s hard to say one is a clear winner. They both use Lua, so there’s no real difference there. Love2D is more of a “write code and run compiler on command line” flow, whereas Defold is an “open project, tinker, (re)-build” flow. They both run quickly and allow for quick iteration. Defold having built-in tilesource/tilemap support so you can easily build sprite-based levels is nice. However, I’m still getting tripped up on its collection/game object organization scheme. Love2D feels closer to Pico-8, which I’ve used a lot, but it’s missing the latter’s sprite/music editors (as is Defold).</p>

<p>Basically, this point in the project is still mainly learning tooling and trying to form muscle memory for the basic foundational things, so progress consists largely of going through tutorials, tweaking them, and spending a lot of time in Google/Wikis/Ollama. It’s not all fun, and often frustrating, but that’s part of the journey.</p>

<p>For now, I’m enjoying spending time messing around with various prototypes here and there and then doing a bi-weekly checkin with Matt on the podcast. It’s almost like recording a development team’s standup. I also started a “blog” of sorts which just consists of notes about each checkin, so we have a record of what we did each meeting, and can look back on progress.</p>

<h2 id="for-the-road">FOR THE ROAD</h2>

<p>Hopefully all of this coalesces into a single project that we share and iterate on, but for now it’s just the wild west of gamedev, baby.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="defold" /><category term="htg" /><category term="gamedev" /><category term="love2d" /><category term="lua" /><category term="podcast" /><summary type="html"><![CDATA[Season two of Hacking the Grepson is all about making a game. After some consideration, the decision to use Love2D and/or Defold, two different game engines, was made and a concentrated effort to tinker with both of them has begun.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/defold-love2d-gamedev.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/defold-love2d-gamedev.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Event Listener Ephiphany</title><link href="https://michaelchadwick.info/blog/2025/08/27/event-listener-epiphany/" rel="alternate" type="text/html" title="Event Listener Ephiphany" /><published>2025-08-27T00:00:00-07:00</published><updated>2025-08-27T00:00:00-07:00</updated><id>https://michaelchadwick.info/blog/2025/08/27/event-listener-epiphany</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/08/27/event-listener-epiphany/"><![CDATA[<p>Being able to use Javascript event listeners to check when certain things happen, and then trigger useful tasks when they do, is not always as straightforward as I’d think.</p>

<!--more-->

<p>Basically, I wanted <a href="https://soundlister.neb.host">SoundLister</a> to honor a keyboard function key (like the one I have on my Mac) that toggles play/pause, while also honoring the UI play/pause button. Since SL uses a regular <code class="language-plaintext highlighter-rouge">&lt;audio&gt;</code> element, pressing that key would play/pause the audio like expected, but the UI would NOT toggle like you’d expect, since the existing event listener was only checking for clicking on the main player’s icon.</p>

<p>At first I thought I needed to find an EventListener for <code class="language-plaintext highlighter-rouge">keypress</code>, as the event in question was a ‘key’ being pressed. Seems intuitive, right? That’s how I added some simple keyboard shortcuts for play/pause and skip forward/back:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">keydown</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">code</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">Space</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// fix issue with double-triggering</span>
    <span class="c1">// if space bar is activeElement</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">activeElement</span><span class="p">.</span><span class="nf">blur</span><span class="p">();</span>
    <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_updatePlayState</span><span class="p">(</span><span class="dl">'</span><span class="s1">key</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="c1">// Next Track: Shift+Cmd/Win+Right</span>
    <span class="c1">// Prev Track: Shift+Cmd/Win+Left</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">metaKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">shiftKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">code</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">ArrowRight</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">goForward</span><span class="p">();</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if </span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">metaKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">shiftKey</span> <span class="o">&amp;&amp;</span> <span class="nx">event</span><span class="p">.</span><span class="nx">code</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">ArrowLeft</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">goBack</span><span class="p">();</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>

<p>However, despite being aided by this wonderful <a href="https://www.toptal.com/developers/keycode">JavaScript Key Codes</a> site, JS doesn’t seem to <em>hear</em> function keys (F1-F19). Why? Probably some OS-specific low-level reason. I dunno. Regardless, I was looking in the wrong place: I needed to listen for when audio began to play or was paused. I was already doing this, but I needed to augment it and figure out the proper logic to keep from accidentally getting stuck in loops. This was achieved by properly seperating the various entrypoints into toggling the play/pause state of the <code class="language-plaintext highlighter-rouge">&lt;audio&gt;</code> element:</p>

<ul>
  <li>Clicking on the playlist itself</li>
  <li>Clicking on the collection dropdown and choosing a new subset of tracks</li>
  <li>Clicking on the UI play/pause icon (or using keyboard shortcuts)</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;audio&gt;</code> events that the other methods would trigger</li>
</ul>

<p>I ended up with the following:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// excerpt from events.js</span>
<span class="c1">// ...</span>
<span class="c1">// audio player UI play/pause icon</span>
<span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButton</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_updatePlayState</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// &lt;audio&gt; element has started playing</span>
<span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">play</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_updatePlayState</span><span class="p">();</span>
  <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_setTitle</span><span class="p">();</span> <span class="c1">// set &lt;title&gt; with current track name</span>
<span class="p">});</span>
<span class="c1">// &lt;audio&gt; element has been paused</span>
<span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nf">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">pause</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">_updatePlayState</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div></div>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// excerpt from main.js</span>
<span class="c1">// ...</span>
<span class="c1">// change play/pause icon and audio element depending on context</span>
<span class="nx">SoundLister</span><span class="p">.</span><span class="nx">_updatePlayState</span> <span class="o">=</span> <span class="p">(</span><span class="nx">source</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">switch </span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// clicking in the audio playlist auto-starts track</span>
    <span class="c1">// and sets play/pause icon to 'pause'</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">playlist</span><span class="dl">'</span><span class="p">:</span>
      <span class="c1">// start the audio scrubbing bar updating so refreshes every second</span>
      <span class="nf">requestAnimationFrame</span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">_whilePlaying</span><span class="p">);</span>

      <span class="c1">// change play/pause icon to 'pause'</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>

      <span class="k">break</span><span class="p">;</span>

    <span class="c1">// clicking on the collection dropdown auto-stops track</span>
    <span class="c1">// and sets play/pause icon to 'play'</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">collection</span><span class="dl">'</span><span class="p">:</span>
      <span class="c1">// stop updating the audio scrubber bar refresh</span>
      <span class="nf">cancelAnimationFrame</span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">raf</span><span class="p">);</span>

      <span class="c1">// load first track in collection</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">SoundLister</span><span class="p">.</span><span class="nf">tracks</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">href</span><span class="p">;</span>

      <span class="c1">// change play/pause icon to 'play'</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>

      <span class="k">break</span><span class="p">;</span>

    <span class="c1">// clicking directly on the play/pause icon</span>
    <span class="c1">// or using the space or next/prev keys</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">key</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">if </span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nx">paused</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nf">play</span><span class="p">();</span>

        <span class="c1">// start the audio scrubbing bar updating so refreshes every second</span>
        <span class="nf">requestAnimationFrame</span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">_whilePlaying</span><span class="p">);</span>

        <span class="c1">// change play/pause icon to 'play'</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nf">pause</span><span class="p">();</span>

        <span class="c1">// stop updating the audio scrubber bar refresh</span>
        <span class="nf">cancelAnimationFrame</span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">raf</span><span class="p">);</span>

        <span class="c1">// change play/pause icon to 'pause'</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>
      <span class="p">}</span>

      <span class="k">break</span><span class="p">;</span>

    <span class="c1">// audio play/pause events</span>
    <span class="nl">default</span><span class="p">:</span>
      <span class="k">if </span><span class="p">(</span><span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">audio</span><span class="p">.</span><span class="nx">paused</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// change play/pause icon to 'play'</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// change play/pause icon to 'pause'</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-play</span><span class="dl">'</span><span class="p">);</span>
        <span class="nx">SoundLister</span><span class="p">.</span><span class="nx">dom</span><span class="p">.</span><span class="nx">playButtonIcon</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">fa-pause</span><span class="dl">'</span><span class="p">);</span>
      <span class="p">}</span>

      <span class="k">break</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The real key was making sure that activating the play/pause UI icon didn’t create an infinite loop of audio start/stop events, causing the icon to toggle endlessly and the audio to take on a nice glitchy tremelo feature.</p>

<p>Trial and error, complication followed by simplification, and lots of refreshing of the browser finally got me to the finished, working state. Now I can hit the function key on my Mac and the status of everything syncs up nicely.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="audio" /><category term="event-listeners" /><category term="keyboard" /><category term="javascript" /><category term="js" /><category term="soundlister" /><category term="webdev" /><summary type="html"><![CDATA[Being able to use Javascript event listeners to check when certain things happen, and then trigger useful tasks when they do, is not always as straightforward as I'd think.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/event-listener-epiphany.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/event-listener-epiphany.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">New England and Parts Further Northeast</title><link href="https://michaelchadwick.info/blog/2025/07/10/new-england-and-parts-further-northeast/" rel="alternate" type="text/html" title="New England and Parts Further Northeast" /><published>2025-07-10T06:23:40-07:00</published><updated>2025-07-10T06:23:40-07:00</updated><id>https://michaelchadwick.info/blog/2025/07/10/new-england-and-parts-further-northeast</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/07/10/new-england-and-parts-further-northeast/"><![CDATA[<p>Sometimes, you just gotta get away from it all, see something new, and do something else. Thus, it is time to recount the recent travels of me, my wife, and our daughter, after 3 weeks spent in New England and Canada. Be it by car, ship, or plane, we traveled to new places, saw new things, and had new experiences. Buckle up: here is the account of said adventure.</p>

<!--more-->

<h2 id="pre-preamble">Pre-Preamble</h2>

<p>Three weeks is a long period of time. At least in the year 2025, it also means one probably took a lot of photos and videos with their mobile phone. And I did. Besides the words below, I also sifted through said photos and videos AND created a whole vacation video AND its soundtrack.</p>

<!-- markdownlint-disable-next-line -->
<style>.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style>
<div class="embed-container">    <iframe title="YouTube video player" width="640" height="390" src="//www.youtube.com/embed/zwIdDTqpQVE" frameborder="0" allowfullscreen=""></iframe></div>

<p>You can watch the video above, and then read the words. You could also just listen to the soundtrack, “Just the Halifax, Ma’am”, in the background while you read the words. OR you could <em>just</em> read the words and throw shade on my creative efforts. Very choices, many option, wow!</p>

<!-- replace with {# audeeo #}, my own custom embedded audio player, that I will build -->
<div class="audio-player">
  <audio id="just-the-halifax" src="https://music.nebyoolae.com/files/audio/_blog/NewEngland2025.mp3" preload="auto" controls="" controlslist="nodownload"></audio>
  <label for="just-the-halifax">Just the Halifax, Ma'am by Nebyoolae</label>
</div>

<h2 id="preamble-fly-new-york-city-new-york">Preamble: Fly: New York City, New York</h2>

<p><img src="/assets/images/posts/2025/ne2025/sd-to-ny.jpg" alt="Map of the US centered around New York City" /></p>

<p>In order to vacation in New England, first you must get there, and I’m usually in Southern California, so that’s…a distance. Thankfully, commercial air travel is a thing (with a little more turbulence than I liked). Podcasts in the ear (I went through a lot of the <a href="https://eggplant.show">Eggplant Show</a>’s <a href="https://eggplant.show/ep-a-year-of-ufo-50-barbuta">Year of UFO 50</a>) for hours of entertainment and New York City as a terminus did just fine.</p>

<h2 id="june-3-6-drive-edison-new-jersey">June 3-6: Drive: Edison, New Jersey</h2>

<p><img src="/assets/images/posts/2025/ne2025/new_jersey.jpg" alt="A large pizza from Sciortino's in Perth Amboy, NJ" /></p>

<p>Our plan was to have a bit of a family mini-vacation before joining up with more family for sight-seeing in the Big Apple, so we commandeered a rental vehicle and drove from JFK to New Jersey. Despite being born on the East Coast (and lived there for many years as a kid), I’d never driven there before, and I was quickly confused and nearly swallowed up whole by the highway system. Also, TOLLS. So many tolls. So many unnecessary tolls (due to confusion). It does not make me want to go back and drive there again any time soon.</p>

<p>After such heinous vehicular mayhem, we met the first of two new IRL friends in Edison. She welcomed us in the parking lot with a big hug and it set the tone nicely. Three cats, countless tortillas, and many yummy <em>carnitas</em> later, we were nice and chill in her place. It was like hanging out with a friend we had already known for a while. Besides the homecooked meal, we also had a delightful Italian dinner excursion and an Original Pancake House visit, so vittles were plentiful and delicious. We even watched Mexican cooking YouTube while we were there! Note: the hotel we stayed in was the roomiest of any during the whole vacation, as it had a separate bedroom, which is always nice.</p>

<h2 id="june-7-8-drive-toronto-ontario">June 7-8: Drive: Toronto, Ontario</h2>

<p><img src="/assets/images/posts/2025/ne2025/toronto.jpg" alt="Centre Island, just off the coast of Toronto, Ontario" /></p>

<p>After our visit in New Jersey had come to an end, we loaded up the rental car and drove through upstate New York, heading towards Toronto (with more tolls en route). The scenery was green and beautiful, obviously fed by the kind of moderate to heavy rain we had to drive through.</p>

<p>Before arriving at our destination, we stopped in at the <a href="https://www.youtube.com/@herculescandy">YouTube-famous</a> Hercules Candies to grab some sweets and talk to one of the co-owners(!) about their history. After fawning over our collective fortune at this opportunity outside the building, we reconvened in our rented ride and made our way across the international border into Ontario. The downtown hotel we stayed in was fancy pants (19th floor!), but had to pay for parking (boo!), but it was valeted (meh), but it also had nice free breakfast (yay!).</p>

<p>We were to meet another new friend, but first we did some exploring of the surrounding area by visiting Centre Island. After a short ferry across part of Lake Ontario, we set foot on one of a series of islands that were really quite lovely. It felt like being in Balboa Park, and the streets were bustling with people. Yummy lunch included a Steam Whistle ale.</p>

<p>I did some swinging with my daughter near a beach while my wife came up with a brilliant idea: quadricycles! Yes, a four-person bike with a roof (the whole thing was the size of a golf cart) is called a quadricycle and we got to move at less-than-breakneck speeds, passing around the main movement duties. It was a lot of work, a lot more than my solo bike adventures, but it was so cool to speed up together and pass other quads, laughing the whole way. We were pooped after less than an hour, though, and decided we needed food and water, so called it a day and walked back to our hotel.</p>

<p>Later in our Toronto tour, we met up with more friends who took us out to dumpling dinner, invited us to their house to play with their kitties, and watched Let’s Game It Out and car crash YouTube. They also took us out to a delightful Korean BBQ (so many dishes). Finally, they inspired a new name for a specific chat platform (What’s a PP?).</p>

<p>Our final trip in Toronto led us up the CN Tower, a large building in the downtown area. After a very speedy elevator ride up, we stepped out and had a grand view of the city, including a floor window so you could step “over” everything, hundreds of feet in the air. Scary, but cool.</p>

<p>We capped our visit off with a live viewing of the Tony Awards, replete with a 10 year-anniversary medley from the original Hamilton cast!</p>

<h2 id="june-9-10-drive-niagara-falls-ontario">June 9-10: Drive: Niagara Falls, Ontario</h2>

<p><img src="/assets/images/posts/2025/ne2025/niagara.jpg" alt="Mist and spray obscuring a boat tour navigating close to Niagara Falls" /></p>

<p>After our bustling modern city experience in Toronto, moving location to Niagara Falls was a bit less hectic. The hotel we stayed in seemed old, had really short ceilings, and gave off an “old casino hotel in Vegas” vibe. Not my favorite place to stay, but it was fine for our purposes.</p>

<p>When you go to Niagara Falls, you look at water running down the side of a cliff. You can’t get that close to it, but you <em>can</em> take a boat tour which gets you <em>pretty</em> close, so we did. Once you’re next to the falls there’s so much water spray that the provided ponchos make sense. The child seemed to really enjoy the rain-like climate, as she squealed with delight. A+++, would boat again.</p>

<p>Besides seeing falling water, there’s no much else to do in town. It all has a kind of mini-Vegas vibe, but without the Vegas. We took a nighttime ride around the city in hopes of seeing a light show (didn’t see anything), I took a morning walk to Starbucks (Maple mini pancakes!), and we ate at the hotel’s restaurant (Chocolate Cake shooter!), but that’s about it.</p>

<h2 id="june-10-11-drive-albany-new-york">June 10-11: Drive: Albany, New York</h2>

<p><img src="/assets/images/posts/2025/ne2025/albany.jpg" alt="The dining room at the Schuyler Mansion in Albany, NY" /></p>

<p>We’re in the home stretch of our mini-vacation, but still had some time to kill before meeting more family in NYC, so we took a detour to New York’s capital, Albany. If you’re a Hamilton fan, you might know that the famous Schuyler Mansion lives here, so we had to visit. Not in a great part of town, but still interesting. Not as big or as lavish as I would imagine a “mansion” to be (and I’ve seen Versailles), but apparently the Schuyler’s <em>real</em> place is north a bit in Saratoga. Our hotel was nice, but small. Never made it to the actual capital, despite hearing about something called the Million Dollar Staircase, but there’s always next time.</p>

<p>Before heading to bed to wake up in the morning for our departure to NYC, we made an exploratory mission to find Stewart’s ice cream, a popular dessert served at a chain of gas stations. Our first choice didn’t actually <em>serve</em> any ice cream, so we drove a couple miles away to a legit one, got crazy amazing ice cream for cheap ($3/big scoop), and were satisfied.</p>

<h2 id="june-11-13-drive-back-to-new-york-city-new-york">June 11-13: Drive: (Back to) New York City, New York</h2>

<p><img src="/assets/images/posts/2025/ne2025/new_york_city.jpg" alt="Very important business to attend to at the top of the Empire State Building" /></p>

<p>The long car ride through New York was now done. We were back in The City. All 8 million people crammed into a small space. The rideshare driver handled getting us to our hotel on 47th street, and I am forever in his debt for it. TRAFFIC BE CRAZY.</p>

<p>The mood of everything really changes when you leave Upstate New York (i.e. North Pennsylvania) and dive into Manhattan, especially around Times Square. Everything feels more overwhelming and rushed and tense. That being said, once we got to the 27th floor of our hotel (the bathroom mirror had a clock! the drapes were automatic!), being above it all helps. Grabbed <em>cubano</em> sandwiches from some place called Havana Central for dinner and then spent the rest of our first night chilling.</p>

<p>We spent two days in NYC proper, doing the sightseeing thing. I had been before many, many years ago, and did a similar thing. This time, we all went to Lady Liberty herself, and climbed up her insides. The route you climb up to get to the crown is a warm, claustrophobic, spiral path, and I don’t feel the need to ever do it again. It’s not even that great a view from up top. From there we jetted off to Ellis Island, but I wasn’t too excited to look around, and after lunch at their diner there seemed to be less interest in continuing being touristy so we returned to the hotel. Later that day we visited the Empire State Building, had to deal with some ticket purchase miscommunication (they were for the day <em>bought</em>, not the day <em>visited</em>, oops), elevated up to the 102nd floor, and got a new stuffy named Emma Pire who looks like…well, you can probably guess.</p>

<p>Day 2 of 2 in the Big Apple was spent once again using the subway (mainly the R; you can enter and exit from the same turnstile what) and being a tourist. Breakfast was from some place called Toasties(!) and it was fast, cheap, and good. We all spent the morning in Lower Manhattan, starting from Battery Park, and a local named Jonathan (who reminded me of a way more chill Gianmarco Soresi) showed us around, pointing out historical stuff. We lunched at Fraunces Tavern, an 18th century historic place where Washington gave his farewell address, among other colonial happenstances. Our afternoon activity was originally planned to be the 9/11 memorial, but our party of three was pretty exhausted from vacationing and decided to chill at the hotel instead. We did later step out to visit Nintendo of New York and the American Girl doll store, where my daughter got her own semi-look-alike. We finished the evening at a noisy Italian spot whose name escapes me.</p>

<h2 id="june-14-embark-cruise">June 14: Embark Cruise</h2>

<p><img src="/assets/images/posts/2025/ne2025/embark.jpg" alt="Leaving New York City on the Island Princess" /></p>

<p>Enough vacation for ya yet? No? Then let’s take a cruise!</p>

<p>Before making our way to Brooklyn’s port to embark via another slow rideshare (gotta love traffic), I grabbed Pret a Manger for the fam (Toasties closed on weekend boo). We said goodbye to my wife’s brother and his family as they were only there for the NYC, and remarked on the price of avocado toast in the hotel’s restaurant ($22!).</p>

<p>From turf to surf: we walked up the gangway to our home for the next 10 days, the <strong>Island Princess</strong> (yes, like from The Love Boat, which we watched many episodes of during (and still after!) the cruise). When you first get on a cruise, you don’t necessarily have a cabin ready, and you definitely don’t have your bags yet, so we found a place to eat lunch first. Soon after, though, we had a cabin ready, and our bags soon followed. Compared to our hotels thus far, the cabin was pretty nicely-sized, with a big bed for the adults, and a pullout couch for the younger one. It even had TWO televisions, and ONE remote that somehow controlled BOTH of them. We also had an outside balcony with chairs to sit on, which helps one feel less trapped inside the specific slice of the gigantic floating hotel you’re assigned.</p>

<p>We visited the library for some jigsaw puzzling, signed up our daughter for the Kid Zone, and then all met in the main dining room for dinner. Our daughter left early to actually go to the Kid Zone, and then as we were returning to our cabin after eating we ran into her and some new kid! She had made a friend, much to our surprise, and they would end up spending nearly the whole rest of the time on the cruise together. For our more introverted kid, this was a wonderful turn of events, both for her and for us. We were so shell-shocked we had to regroup in my wife’s parents’ cabin to discuss it!</p>

<h2 id="june-15-cruise-sea-day">June 15: Cruise: Sea Day</h2>

<p>Since our first day after leaving the port in Brooklyn was a sea day (takes a bit to get to Canada), we explored the ship and its features. Our daughter went back to the Kid Zone all on her own and we were stunned. We started using a pen and paper note system so everyone knew where we were headed. We all had these medallions attached to our watches that could be looked up, but the note-leaving had a nice old-school charm to it.</p>

<p>There are a lot of things you <em>could</em> do on a cruise besides eat or drink or lounge or a combination of the above, but after taking a few cruises I’m less excited about any one particular activity. Trivia is still fun, though, so we hit up one of those. We watched people play cornhole in the main atrium. We did laundry. We ate at the Salty Dog for lunch while our daughter ate in the main dining room with her newfound friend. The night was finished off with dinner in the main dining room and a musical bingo game in one of the lounges. Besides that, it was basically bedtime, which meant Love Boat and then podcasts with which to fall asleep.</p>

<h2 id="june-16-cruise-halifax-nova-scotia">June 16: Cruise: Halifax, Nova Scotia</h2>

<p><img src="/assets/images/posts/2025/ne2025/halifax.jpg" alt="Fifty-plus year-old lobster" /></p>

<p>Our first port, and our first time in Nova Scotia! Halifax is the second largest (or busiest?) port in North America (or the world?). We met our tour guide, Gary, and hopped in his tour van. Some highlights of our trek: the star-shaped unused fort called the Citadel (autonomous lawn mowers with names, like <strong>Prince Edweed</strong>!), Shaw’s Landing for lunch where they were oddly out of multiple things, a live lobster demo (various lobsters, sized from 1 to 10 lbs, including a 50+ year-old one), Peggys Cove with a lighthouse, cool rocks to scamper on with multiple musicians playing, and a grave site with tombs for Titanic victims. All in all, Halifax is a cool place, and our tour guide was very knowledgeable (if too verbose). The city would be worth coming back to some other time. I even saw a Fallout license plate!</p>

<p>The rest of the day was spent chilling. Decided on doing the busy buffet for dinner, and then enjoying a Bee’s Knees cocktail while listening to two violinists cover the Bee Gees. We moved to the library at some point to do puzzles but lost interest after hearing some bad work news -_-</p>

<h2 id="june-17-cruise-sydney-nova-scotia">June 17: Cruise: Sydney, Nova Scotia</h2>

<p><img src="/assets/images/posts/2025/ne2025/sydney.jpg" alt="The three little pigs, in rug form" /></p>

<p>The next day took us a little further northeast in Nova Scotia to the port town of Sydney. If I remember anything from this place it will be the ENORMOUS FIDDLE at the dock. I will also remember the amazeballs lunch we had at The Lebanese Flower. We were initially greeted by a small child sitting on the front counter, which was unexpected. She ended up being the daughter of the head waiter/restaurant owner, and even brought us menus and was generally cute while sitting or playing on a nearby chair while we ate. Also, the French Fry Wars of 2025 continued apace, as my daughter continued to order them, and we all continued to steal them. To walk off our meal, we visited an old-timey house-turned-musem called Jost House and were given some background information about who lived there over the past 100 years or so.</p>

<p>Back on the ship, we did the usual dinner, and then a gameshow and trivia for post-meal entertainment. We were also alerted about our delayed departure due to high winds. Would we miss our next port, Prince Edward Island, due to mother nature? Well, if you look at the next header, you know the answer.</p>

<h2 id="june-18-cruise-charlottetown-prince-edward-island">June 18: Cruise: Charlottetown, Prince Edward Island</h2>

<p><img src="/assets/images/posts/2025/ne2025/pei.jpg" alt="A stone buddha" /></p>

<p>Thankfully, the winds in Sydney eventually died down enough for our vessel to leave port and we got into Charlottetown, PEI. Disembarked the ship, met up with another tour guide (this time Sonia), and were whisked away in a northwesterly direction toward the home of one Lucy Maud Montgomery, better known as the author of Anne of Green Gables.</p>

<p>Prince Edward Island, much like Halifax and Sydney in Nova Scotia, is pretty and green and coastal. It is also an island, and it’s where L. M. Montgomery grew up and was inspired by when she wrote her famous novel. Her house and the grounds around it are now behind a paywall, better known as a Visitor Center, so we begrudgingly paid to get access. My wife has been a lifelong fan, and so coming here was a dream come true. We saw the house, obviously, but also the Haunted Forest, and Lover’s Lane. We got the hat with built-in braids. We event went to L. M. Montgomery Park, which has a bronze statue of her. To cool down from the intensity of all this excitement, we took a detour for ice cream. I aim to remember <a href="https://maps.app.goo.gl/x6p1HvNZHpxZNMvM8">Cricker’s Creamery</a> in North Rustico forever, because they had a life-changing chocolate-tipped vanilla cone that my wife <em>really</em> enjoyed.</p>

<p>After we spent some time in Anneland, our tour guide drove us around some more, stopping at some cool garden with weird wood furniture, and then some sand dunes at a beach! Returning back to the ship, we actually had to wait in line for a while, and we weren’t sure we were going to make it back on as it was slated to leave in the early afternoon. In the end, we were worried for no reason: high winds kept us from leaving at our scheduled time, and we ended up staying in PEI all night and not leaving until the morning. This meant that we skipped our Saint John, New Brunswick stop, and instead continued on past it, back to the US.</p>

<h2 id="june-19-20-cruise-sea-days">June 19, 20: Cruise: Sea Days</h2>

<p>The next two days were spent slowly cruising through the Atlantic Ocean, so there was not much to report on. However, each day included meals in the main dining room, doing puzzles in the library, and walking on the promenade deck.</p>

<p>My wife and I brought Uno: No Mercy to a card/game session once, and we ended up playing with some random people which was really fun! They introduced us to SkyJo, a card game that was suspiciously like <a href="https://playnine.com">Play Nine: The Card Game of Golf</a>, but was really fun to play with a huge group.</p>

<p>This was also a time during our cruise that we started to check back in a little with work, mainly due to some “Bite teltening” going on. Worrying about my job while slowly boating through the ocean was a weird disconnect.</p>

<p>We also got a special chance to do something called Chef’s Table, which meant we got a tour of the dining room galley, and sat at a special table with some other cruisers, and eat a bunch of random food. I was initially hesitant, but it ended up being really fun, and the conversation was good.</p>

<h2 id="june-21-cruise-portland-me">June 21: Cruise: Portland, ME</h2>

<p><img src="/assets/images/posts/2025/ne2025/portland_me.jpg" alt="Boats and boats and boats found in Portland, ME" /></p>

<p>Highlights of Maine included: immigration, Tony Tromboney, Love Song trivia in the lounge, and meeting some good friends who have a summer home in Portland AND a friend from San Diego who happened to be visiting! Truly unexpected, but we were able to have lunch together and catch up a little.</p>

<h2 id="june-22-cruise-boston-ma">June 22: Cruise: Boston, MA</h2>

<p><img src="/assets/images/posts/2025/ne2025/boston.jpg" alt="Shipping crates? Books? It's a building in Boston, MA" /></p>

<p>Our final stop on the cruise was Boston, Masschusetts. The warm, humid weather continued as we got off the ship and made our way to our tour guide. He was a local named Neil, and had the appropriate accent. We started our excursion in the North End, which was a downtown-y area full of cash-only, open-at-odd-times businesses, apparently due to the influx of Italians at some point. Fronts for organized crime? You be the judge.</p>

<p>We spent the next good chunk of time going past Bunker Hill (i.e. actually Breed’s Hill), many churches, a graveyard that wasn’t a graveyard, Paul Revere Square (unduly honoring a man whose story is more of a myth), and a stop at Warren Tavern for lunch. I saw a woman playing some Ben Franklin spinning instrument, a large Paul Revere statue, and a lady near a fountain who temporarily put on a tortilla/cookie costume, walked a bit, and then took it off(?!?!?).</p>

<p>Post-lunch travels included driving past Boston Commons, the place where the Boston Symphony and Boston Pops perform, Berklee College of Music, Fenway Park, and a trip through Cambridge where MIT, Boston University, and Harvard are all located. I was surprised to learn that the latter wasn’t some huge distinct building atop a hillside out in a green plain, as I imagined it. After all that deep Boston lore whizzing past us, our tour guide dropped us back at the dock and we were done with Boston…for now (I’d like to come back).</p>

<h2 id="june-23-cruise-sea-day">June 23: Cruise: Sea Day</h2>

<p>Once we were finally done with ports and tours and stuff, my mind could truly settle back on Real Life, where California, and work, and unhumid air lived. As a parting thought towards cruises, in general, though: bring stuff to use and/or leave on the cruise next time. Things to donate would be puzzles, plastic ducks, pencils (with erasers!).</p>

<h2 id="june-24-disembark-cruise-fly-home">June 24: Disembark Cruise, Fly Home</h2>

<p><img src="/assets/images/posts/2025/ne2025/disembark.jpg" alt="Even stuffed animals get thirsty sometimes" /></p>

<p>We made it: the end of vacation. Got off the ship back in Brooklyn and was accosted by Uber drivers to get us to JFK. Normally, I wouldn’t interact with that kind of communication, but we just grabbed the first person who offered and he got us to JFK in the usual NY traffic, and I am again heavily indebted. The cost of that ride was preposterous, money-wise, but worth it in the end to not have to deal with the transport ourselves.</p>

<p>JFK presented the final obstacle of our trip: the 30+ minute wait in a line that switched back and forth in a separate room before even getting to the people who check your ID. My wife got flagged for a forgotten 200ml maple syrup bottle in her bag, so even though she and our daughter went through the Pre-Check line they still only barely beat me through the regular line.</p>

<p>We all boarded our flight, took to the sky, and several hours later we touched down in San Diego. We were home. Besides seeing some suitcases in the baggage claim area that were styled with Van Gogh’s Starry Night, my main memory is stepping out into the open DRY air and breathing it in. It felt good.</p>

<p>There was some actual traffic getting home, despite it being the middle of the day, but nothing could keep my happiness at bay once we got out of the rideshare and saw our house.</p>

<h2 id="for-the-road">FOR THE ROAD</h2>

<p>Twas a truly wonderful vacation. We all got to see so many new places, and have new experiences, and I think three weeks, while long, went by at a good pace. The combined drive through NY to Canada and back had a unique flavor, while NYC itself had one of its own, and the cruise was different yet still. After all the gallivanting and living out of hotels and a suitcase, I’m really enjoying just hanging at home for a while, but also thinking about our next adventure, too.</p>

<p>Until next time!</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="canada" /><category term="cruise" /><category term="maine" /><category term="massachusetts" /><category term="new-jersey" /><category term="new-york" /><category term="nova-scotia" /><category term="pei" /><category term="prince-edward-island" /><category term="travel" /><summary type="html"><![CDATA[Sometimes, you just gotta get away from it all, see something new, and do something else. Thus, it is time to recount the recent travels of me, my wife, and our daughter, after 3 weeks spent in New England and Canada. Be it by car, ship, or plane, we traveled to new places, saw new things, and had new experiences. Buckle up: here is the account of said adventure.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/ne2025/header.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/ne2025/header.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">First Half of 2025 Has Some Sounds For You</title><link href="https://michaelchadwick.info/blog/2025/06/02/first-half-of-2025-has-some-sounds-for-you/" rel="alternate" type="text/html" title="First Half of 2025 Has Some Sounds For You" /><published>2025-06-02T07:42:00-07:00</published><updated>2025-06-02T07:42:00-07:00</updated><id>https://michaelchadwick.info/blog/2025/06/02/first-half-of-2025-has-some-sounds-for-you</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/06/02/first-half-of-2025-has-some-sounds-for-you/"><![CDATA[<p>The last six months have been productive for my musical making, and there are 3 releases (including instrumental guitar covers, odes to the lost, and gamey remixes) to check out if you’re looking for something new.</p>

<!--more-->

<h2 id="satch-20">Satch 20</h2>

<p>When I first started learning to play the guitar in ~1995, I was already steeped in the rock and/or roll scene. Your Guns ‘n Roses, Metallicas, AC/DCs, Led Zeppelins, and the like were all part of my listening sphere by the time I was a pre-teen. However, my musical preferences were really cemented in the early 90s, and bands like Pearl Jam, Soundgarden, Alice in Chains, Stone Temple Pilots, and Nirvana became the foundation for what excited and soothed me.</p>

<p>These bands are all guitar-centric, so I listened to a lot of strings vibrating through distortion pedals. Lots of the guitarists in these bands were virtuosic, but they mainly played to the song, which had vocals that were the emotional center. Playing the guitar and not singing? That wasn’t the norm.</p>

<p>However, at some point I discovered <a href="https://en.wikipedia.org/wiki/Joe_Satriani">Mr. Joseph Satriani</a>, someone who would revolutionize my interest in not only focusing on the guitar in a piece of music, but influencing what I thought a piece of music <em>could</em> be.</p>

<div class="audio-player">
  <audio id="satch20-summer-song" src="https://music.nebyoolae.com/files/audio/61_satch_20/1992-01-summer_song.mp3" preload="auto" controls="" controlslist="nodownload"></audio>
  <label for="satch20-summer-song">Summer Song from Satch 20 by Nebyoolae</label>
</div>

<p>While I might’ve heard “Summer Song” back when it first came out in the 80s, I wasn’t cognizant of what it was, or why it was amazing. The first track that truly hit me was his first single from <a href="https://en.wikipedia.org/wiki/Crystal_Planet">Crystal Planet</a>, entitled <a href="https://en.wikipedia.org/wiki/Ceremony_(Joe_Satriani_song)">“Ceremony”</a>. I guess I heard it on the radio (even though he’s not an artist that tends to be played there), and it just fascinated me with its opening wah riff, the catchy main melody, and both the middle and ending solos. When I finally bought the CD I only listened to “Ceremony” for days, not yet wanting to spoil the rest of the album. Eventually I <em>did</em> listen to the rest of it, and was blown away by all of it. It spurred me to go into his back catalog, and it kept me interested in his future releases for many years after that.</p>

<div class="audio-player">
  <audio src="https://music.nebyoolae.com/files/audio/61_satch_20/1998-01-ceremony.mp3" preload="auto" controls="" controlslist="nodownload"></audio>
  <label for="satch20-summer-song">Ceremony from Satch 20 by Nebyoolae</label>
</div>

<p>Of course, as a budding young guitarist at the the time, I looked up tabs and tried to play along, but my ability was nowhere near his. Practice, practice, and more practice allowed me to play along to the rhythms and main leads, but the solos were always out of my league.</p>

<p>It’s now 2025, though. I’ve been playing guitar for nearly 30 years. It was time to finally see if I could pull off Satriani. After spending a fair amount of time setting up backing tracks, I plowed into 17 different Satriani songs running the first 20 years (1984-2004) of his recorded output. I chose favorite songs, even if they weren’t all “the hits”. Over the years I had actually tried and recorded a few “easier” Satriani songs, and decided to include them in my collection. I also added one more track, originally written in 2008 but recorded now, that was heavily inspired by Satch.</p>

<p><a href="https://satch20.nebyoolae.com">Listen to all of Satch 20</a></p>

<p>My efforts are not as professional as I would like, and there are errors and hand-wavy attempts and patch-ins, but it’s all my guitar playing. It’s my own little Satch collection, finally done after so many years of admiration.</p>

<h2 id="pennyfarthing">Pennyfarthing</h2>

<p>2025 has been a rough year for obvious geopolitical reasons, but on a personal note it’s been rough because our family dog, Penny, passed away. She was 13.5 years old, was not doing well, and…it was time.</p>

<p>In order to mask the hurt and sadness, I did what I do in these times: make an EP of music as Fenchy.</p>

<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=205120420/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless=""><a href="https://fenchy.bandcamp.com/album/pennyfarthing-ep">pennyfarthing (ep) by Fenchy</a></iframe>

<p>None of the tracks are really <em>about</em> Penny, but the subject matter and its contemplative and fractured tone were inspired by the loss of her. Musically, I decided to sing over beats and synths instead of guitar and drumkit (like most Fenchy stuff). It’s not my favorite Fenchy release, but it needed to be done. My favorite track is “Important Job”, my attempt to sound like NIN.</p>

<h2 id="gamey-jumble">Gamey Jumble</h2>

<p>Number 5 in an ongoing series of albums that cover/remix old video game music came out the most recently, and instead of a goulash, amalgam, stew, or mixture you get a jumble this time.</p>

<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=375511405/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/transparent=true/" seamless=""><a href="https://nebyoolae.bandcamp.com/album/gamey-jumble">Gamey Jumble by Nebyoolae</a></iframe>

<p>All the track titles are in non-English languages because…reasons. It was fun to come up with ideas and then find a language that had a cool translation. Virtual metal guitar VST <a href="https://www.threebodytech.com/en/products/heavier7strings">Heavy7Strings</a> features, uh, heavily in many tracks.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="fenchy" /><category term="gamey" /><category term="guitar" /><category term="music" /><category term="nebyoolae" /><category term="releases" /><category term="satriani" /><summary type="html"><![CDATA[The last six months have been productive for my musical making, and there are 3 releases (including instrumental guitar covers, odes to the lost, and gamey remixes) to check out if you're looking for something new.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/first-half-of-2025-sounds.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/first-half-of-2025-sounds.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why Even Blog?</title><link href="https://michaelchadwick.info/blog/2025/01/28/why-even-blog/" rel="alternate" type="text/html" title="Why Even Blog?" /><published>2025-01-28T01:46:00-08:00</published><updated>2025-01-28T01:46:00-08:00</updated><id>https://michaelchadwick.info/blog/2025/01/28/why-even-blog</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/01/28/why-even-blog/"><![CDATA[<p>With social media and microblogging all the rage on the Internet these days, why even blog? I’m going to tell you, through one of the oldest Internet things I know of: being tagged to fill out a template.</p>

<!--more-->

<p>Full disclosure: I was not actually tagged to do this, as I personally know very few people who even write in the long-form style anymore, but I found <a href="https://ttntm.me/blog/blog-questions-challenge/">this</a> through the <a href="https://indieblog.page">IndieBlog</a> random function (which I highly recommend using every once in a while to find other bloggers), and it seemed like a good prompt to get some thoughts out about why I occasionally put text into a screen on a computer and save it so the whole wide world can read it.</p>

<h2 id="why-did-you-start-blogging-in-the-first-place">Why Did You Start Blogging in the First Place?</h2>

<p>My earliest memory of any kind of “write something on the Internet” was a thing called <a href="https://livejournal.com">LiveJournal</a> (although its current incarnation is nothing like it was in <a href="https://web.archive.org/web/20070109212153/http://www.livejournal.com/">early-to-mid-2000s</a>). It began in 1999, but it wasn’t until someone I knew told me about it in 2001 that I decided to try it. Adding whimsical stuff to my AOL Instant Messenger client was fun, but this was a new level. I had tried some written journals in my teens, I think, but any kind of consistent journaling didn’t take root until LJ.</p>

<p>My first post, while not on LiveJournal anymore and now just in a hidden draft form along with 1000+ old posts that need reviewing, was just an ode to Rage Against the Machine, a band in heavy listening rotation for the budding young guiarist me at the time.</p>

<h2 id="what-platform-are-you-using-to-manage-your-blog-and-why-did-you-choose-it">What Platform Are You Using to Manage Your Blog, and Why Did You Choose it?</h2>

<p>My blog is currently managed by <a href="https://jekyllrb.com">Jekyll</a>, a Static Site Generator. There are plenty of choices when it comes to <a href="https://en.wikipedia.org/wiki/Static_site_generator">SSGs</a>, but my fondness for Ruby (at the time,  and even now) probably swayed this one for me.</p>

<p>Before Jekyll, I used Wordpress (and a MySQL database on a host I controlled), and before that it was all LiveJournal (in who-knows-what database on a host I did not control). I was able to export both previous systems, thankfully, so now all my 20-something musings are at least captured in flat files should I ever decide to import them properly. Controlling my content, and in a format that’s easy to move around (it’s just a bunch of files!), is really important to me now.</p>

<p>Creating content for the walled gardens of the world is fine, but I prefer to own mine as much as possible, and “share it with the world” in whatever way that means at the time, rather than create it in silos that can then be taken down or away without any consent or notice. See also: <a href="https://indieweb.org/POSSE">POSSE</a>.</p>

<h2 id="how-do-you-write-your-posts">How Do You Write Your Posts?</h2>

<p>Programming is how I make my living, and I’m a fan of VSCode, so I just make Markdown files in it. Thanks to <a href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint">markdownlint</a> and <a href="https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced">Markdown Preview Enhanced</a> I’ve got all I need.</p>

<h2 id="when-do-you-feel-most-inspired-to-write">When Do You Feel Most Inspired to Write?</h2>

<p>Honestly? I feel most inspired when I realize I haven’t blogged in a while.</p>

<p>The conclusion of a project is sometimes enough to get me to write about the experience of working on said thing. However, the more likely culprit is this scenario: read through my RSS feeds, see all these other blogs being written, get a bit sad that my feed hasn’t updated in many moons, think of something to write about, write about it.</p>

<h2 id="do-you-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit-as-a-draft">Do You Publish Immediately After Writing, or Do You Let it Simmer a Bit as a Draft?</h2>

<p>Since I blog so infrequently, when I actually think of something to make a blog post about, it’s usually somewhat long and involved. I often take all day, poking and prodding the words, until a finished product is ready to go.</p>

<p>Regardless, when it’s done I usually read it over, check for obvious mistakes, maybe add links and tags, and then push it. Simmering generally not needed.</p>

<h2 id="whats-your-favorite-post-on-your-blog">What’s Your Favorite Post on Your Blog?</h2>

<p>Choosing a “favorite” of anything is difficult for me, so I’ll just link <a href="https://michaelchadwick.info/blog/2011/10/25/i-dont-say-anything-to-c-regions-actually-because-theyre-inanimate-constructs/">this C# regions rant</a> I made 13+ years ago (please excuse the cringey title) that I actually put on <a href="https://www.reddit.com/r/programming/comments/lorvb/i_dont_say_anything_to_c_regions_actually_because/">Reddit</a> (which is very rare for me) and <a href="https://michaelchadwick.info/blog/2016/12/06/first-finished-pico-8-game-axeracer/">this writeup on Axeracer</a>, my first Pico-8 game.</p>

<h2 id="any-future-plans-for-your-blog">Any Future Plans for Your Blog?</h2>

<p>Using Jekyll works for me, so no change sighted on the horizon for now. All my blog content is in either HTML or Markdown files, so it’s easily exported to another system, if needed.</p>

<p>My content is largely going to stay the same, focusing on audio and development, but I might make the blog itself front and center instead of tucked away in a subdirectory. I also will foolishly make the goal of “blogging more”, and 01/2025 is going well so far!, but know it probably won’t last. Still, it’s going to exist as an archive of my posts in perpetuity, regardless.</p>

<h2 id="why-do-you-write-other-than-your-blog-do-you-write-long-form-content-elsewhere">Why do you write? Other Than Your Blog, Do You Write Long-Form Content Elsewhere?</h2>

<p>I write to remember, primarily. But I also write to better understand things. When I make a blog post, it’s intentional and I try to make it interesting and accurate. Since most of my blogging these days is technical, development stuff, it takes time to formulate my thoughts and make something of substance. Social media and microblogging sites are for the quick thoughts, but my blog is for the long ones.</p>

<p>I’d love to blog about something and it come up in a search because someone else was also working on the thing I’m working on. I’ve found so many cool blogs by people doing what I can assume is a similar intention, so I want to be part of that.</p>

<p>I don’t write anything of substance anywhere else, so this is the digital extension of my brain that I hope has the best chance of becoming my legacy.</p>

<h2 id="its-your-turn-now">It’s Your Turn Now</h2>

<p>So, if anyone reads this, and has their own blog, consider copying the questions and adding your own answers! I found it interesting to think about why I do this thing you’re reading, as introspection has always been enjoyable to me.</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="blogging" /><category term="jekyll" /><category term="livejournal" /><category term="markdown" /><category term="pico8" /><summary type="html"><![CDATA[With social media and microblogging all the rage on the Internet these days, why even blog? I'm going to tell you, through one of the oldest Internet things I know of: being tagged to fill out a template.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/why-even-blog.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/why-even-blog.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Waviest of Tubular Men</title><link href="https://michaelchadwick.info/blog/2025/01/22/the-waviest-of-tubular-men/" rel="alternate" type="text/html" title="The Waviest of Tubular Men" /><published>2025-01-22T00:54:00-08:00</published><updated>2025-01-22T00:54:00-08:00</updated><id>https://michaelchadwick.info/blog/2025/01/22/the-waviest-of-tubular-men</id><content type="html" xml:base="https://michaelchadwick.info/blog/2025/01/22/the-waviest-of-tubular-men/"><![CDATA[<p>Through no particular effort of my own beyond recording and titling a song, recording a video for it, and putting it onto the Internet, I now have an entry on YouTube that (as of today) totals 4.5K (2025-11-22 update: 5.3K!) views. For my channel, that is bonkers.</p>

<!--more-->

<p>Back in October of 2024, I released a bunch of silly songs under the moniker “Just a Goof”. One of those songs was a ditty called “Wavy Tube Man”.</p>

<!-- markdownlint-disable-next-line -->
<style>.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style>
<div class="embed-container">    <iframe title="YouTube video player" width="640" height="390" src="//www.youtube.com/embed/zhseifhUSBU" frameborder="0" allowfullscreen=""></iframe></div>

<p>It was, and still is, an ode to the majesty of the waving, air-filled tube creature that is often adorning shops, trying to get your attention by moving to and fro;  trapped, but also given life, by their oxygenated internal system.</p>

<p>For me, it’s just a fun song that exults this fun-loving commercial mascot, but to YouTube, due to whatever governing forces it employs, it was destined to become a minor hit, its view count now at <del><strong>4500+</strong></del><strong>5300</strong>. It’s been up for <del>a <em>few months</em></del><em>10 months</em>. My next most popular video has…~170 views and was posted <strong>over 14 years ago</strong>. So, this is not the yoozsh for me.</p>

<p>What about Wavy Tube Man has so captivated audiences? Is it simply the title? Is it the song itself? Is it the Jiffy Lube mascot, bouncing around to the audio? I don’t know and will probably never know.</p>

<p>But it’s kinda cool :D</p>]]></content><author><name>Michael Chadwick</name></author><category term="blog" /><category term="just-a-goof" /><category term="music" /><category term="wavy-tube-man" /><category term="youtube" /><summary type="html"><![CDATA[Through no particular effort of my own beyond recording and titling a song, recording a video for it, and putting it onto the Internet, I now have an entry on YouTube that (as of today) totals 4.5K (2025-11-22 update: 5.3K!) views. For my channel, that is bonkers.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://michaelchadwick.info/posts/2025/wavy-tube-man.jpg" /><media:content medium="image" url="https://michaelchadwick.info/posts/2025/wavy-tube-man.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>