<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:webfeeds="http://webfeeds.org/rss/1.0" version="2.0">
  <channel>
    <title>blog | ege.celikci.me</title>
    <link>https://ege.celikci.me/</link>
    <atom:link href="https://ege.celikci.me/blog.xml" rel="self" type="application/rss+xml"/>
    <description>my personal website, blog and portfolio.</description>
    <lastBuildDate>Sun, 29 Mar 2026 20:59:35 GMT</lastBuildDate>
    <language>en</language>
    <generator>Lume 3.2.1</generator>
    <item>
      <title>how did I make the music page</title>
      <link>https://ege.celikci.me/blog/music-page/</link>
      <guid isPermaLink="false">https://ege.celikci.me/blog/music-page/</guid>
      <content:encoded>
        <![CDATA[<p>I’ve been wanting to write a blog post for a long time, but I couldn’t find a topic to write about. I decided to write about how I created my <a href="https://ege.celikci.me/music">favorite albums page</a> because it’s something I’m very confident in and proud of.</p>
<p>This page, which I’ve been wanting to make for a while, had a clear purpose: to make a cool list of my favorite music albums. Although I know there are sites like <a href="https://rateyourmusic.com/">Rate Your Music</a>, <a href="https://www.last.fm/">Last.fm</a>, or <a href="https://topsters.org/">Topsters</a> for this, none of them offered a solution that worked for my needs. I just wanted something simple yet beautiful, and easy to manipulate in terms of both appearance and content management. I knew the best solution was to create a website with a static site generator. This led me to start this website, which eventually grew far beyond my original vision.</p>
<p>The simplest solution I used to use (pre-this-site-era) was to process the album cover images manually using image manipulation software like <a href="https://imagemagick.org/">ImageMagick</a>, and then list them on a page.</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">magick</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> input.ext -dither FloydSteinberg -scale 290x290 -monochrome output.png</span></span></code></pre>
<p>After processing the images with that command, listing the albums shouldn’t be too difficult, right…?</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">![</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-dark:#7FB4CA">cover of the first album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">![</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-dark:#7FB4CA">cover of the second album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">![</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-dark:#7FB4CA">cover of the third album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">-</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> &#x3C;cite></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the first album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">&#x3C;/cite> by </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the first artist of first album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">artistURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">-</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> &#x3C;cite></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the second album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">&#x3C;/cite> by </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the first artist of second album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">artistURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> · </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the second artist of second album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">artistURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">-</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> &#x3C;cite></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the third album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">albumURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">&#x3C;/cite> by </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">name of the first artist of third album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">](</span><span style="color:light-dark(#6693BF, #7FB4CA);--shiki-light:#6693BF;--shiki-light-text-decoration:underline;--shiki-dark:#7FB4CA;--shiki-dark-text-decoration:underline">artistURL</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span></span></code></pre>
<p>While this isn’t the hardest thing in the world to do, as I said, if you have hundreds of albums to list, it quickly becomes a time-consuming process. Even if you don’t manually edit markdown and instead loop through data from a JSON file, things get out of control on the JSON side where you store the album information.</p>
<h2 id="this-is-where-critiquebrainz-comes-in"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#this-is-where-critiquebrainz-comes-in">#</a>this is where CritiqueBrainz comes in</h2>
<p>There is a site that can be considered an open-source alternative to Rate Your Music. It gets its data from <a href="https://musicbrainz.org/">MusicBrainz</a> and has a very useful <a href="https://critiquebrainz.readthedocs.io/api.html">API</a> that—spoiler—will solve all my problems.</p>
<p>To integrate the <a href="https://critiquebrainz.org/">CritiqueBrainz</a> API into my site, I used the <a href="https://www.11ty.dev/docs/data-js/">JavaScript data files</a> feature that Eleventy offers for <a href="https://www.11ty.dev/docs/data/">data management</a> and the <a href="https://www.11ty.dev/docs/plugins/fetch/">eleventy-fetch plugin</a>.</p>
<p>The first thing I had to do was automatically pull the 5-star reviews I gave on CritiqueBrainz. To do this, I simply make a request to the CritiqueBrainz API with <code>user_id</code> and <code>limit</code> queries. The <code>limit</code> is set to <code>50</code> since that is the maximum we can get from the API: <code>https://critiquebrainz.org/ws/1/review?user_id=4d5dbf68-7a90-4166-b15a-16e92f549758&#x26;limit=50</code></p>
<p>This gives out:</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">{</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  "</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">count</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 134</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  "</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">limit</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 50</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  "</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">offset</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  "</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">reviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> [</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    {</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      "</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">entity_id</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "d6c4be50-923a-4d14-8fe7-31f665630d6b"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      "</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">entity_type</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "release_group"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      "</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">rating</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 5</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  ]</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>Since I can only get a maximum of 50 entries per request, I had to create a loop script with an offset.</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">async</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> getFavoriteAlbumIds</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">()</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // ... setup code ...</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  let</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> offset </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  let</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> allReviews </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // Loop until no more reviews are returned</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  while</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">true</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">    const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> url </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      `https://critiquebrainz.org/ws/1/review?user_id=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">CRITIQUEBRAINZ_ID</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">&#x26;limit=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">LIMIT</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">&#x26;offset=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">offset</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold">    try</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> batch </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Fetch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">url</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        duration</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "0s"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // always fetch fresh data for reviews</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        type</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "json"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">        // ... headers ...</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      },);</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">!</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">batch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">reviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">?.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">length</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> break</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // stop if no reviews left</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      allReviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">push</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">...</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">batch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">reviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      offset </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> LIMIT</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // move the cursor forward</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">      // don't harrass the API</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> sleep</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">1000</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    }</span><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold"> catch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      console</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">error</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`[music.js] Failed to fetch: </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">message</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      break</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // Filter for only 5-star albums</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> favReviews </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> allReviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">filter</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">r</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> r</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">rating</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> ===</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 5</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> &#x26;&#x26;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> r</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">entity_type</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> ===</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "release_group"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  );</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  return</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> new</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Set</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">favReviews</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">map</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">((</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">r</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> r</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">entity_id</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">),);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>While our <code>getFavoriteAlbumIds</code> function allows us to fetch more than 50 albums by sending multiple requests at 1-second intervals, it also filters the results to keep only the <code>release_groups</code> that I gave a <code>rating</code> of 5.</p>
<p>Eleventy Fetch plays a massive role here. You might have noticed the <code>duration: "0s"</code> option in the previous script. This controls the <a href="https://www.11ty.dev/docs/plugins/fetch/#change-the-cache-duration">cache duration</a>. For the reviews list, I explicitly tell Eleventy <strong>not</strong> to cache the request because I want my latest ratings to appear immediately every time I build the site. By managing these durations smartly (and caching the static album details for longer periods), I ensure that I don’t bombard the <a href="https://musicbrainz.org/doc/MusicBrainz_API">MusicBrainz API</a> on every build.</p>
<h2 id="getting-album-data-from-musicbrainz"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#getting-album-data-from-musicbrainz">#</a>getting album data from MusicBrainz</h2>
<p>Now that I have the IDs of the albums I love, I need to find out what they actually <em>are</em>. I need the title, the artist, and the release year.</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">async</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> fetchAlbumData</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> url </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">    `https://musicbrainz.org/ws/2/release-group/</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">?inc=releases+artists&#x26;fmt=json`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold">  try</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">    await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Fetch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">url</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      duration</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "30d"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      type</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "json"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      directory</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> DATA_DIR</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">      filenameFormat</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> ()</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      fetchOptions</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        headers</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">          "User-Agent"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> USER_AGENT</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          Accept</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "application/json"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">        },</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      },</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    },);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold"> catch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    console</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">error</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      `[music.js] Failed to fetch album data for </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">: </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">message</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    );</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>The <code>fetchAlbumData</code> function takes the Release Group ID (<code>rgid</code>) we found earlier and hits the MusicBrainz API.</p>
<p>Notice the <code>duration: "30d"</code> here? This is the caching strategy I mentioned earlier. Unlike my review rating, which might change if I decide I hate an album tomorrow, the fact that X album was released in Y year is never going to change. By caching this for 30 days, I make sure I’m not harassing the API for data I already have.</p>
<h2 id="grabbing-the-cover-art"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#grabbing-the-cover-art">#</a>grabbing the cover art</h2>
<p>a wall of text is boring to look at, we need images…</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">async</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> fetchAlbumCover</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> url </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> `https://coverartarchive.org/release-group/</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">/front-500`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold">  try</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">    await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Fetch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">url</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      duration</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "30d"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      type</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "buffer"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      directory</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> COVER_DIR</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">      filenameFormat</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> ()</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      fetchOptions</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> headers</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "User-Agent"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> USER_AGENT</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> },</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> },</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    },);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-light-font-weight:bold;--shiki-dark:#FF5D62;--shiki-dark-font-weight:bold"> catch</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    console</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">error</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      `[music.js] Failed to fetch album cover for </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">: </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">e</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">message</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    );</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>The <code>fetchAlbumCover</code> function works almost exactly like the metadata fetcher, but it hits the <a href="https://coverartarchive.org/">Cover Art Archive</a>. Instead of requesting JSON, I’m grabbing the <code>buffer</code> (the raw image data) for the 500px version of the cover. Again, I cache this for 30 days because downloading hundreds of images every time I build the site would be painfully slow.</p>
<h2 id="the-ａｅｓｔｈｅｔｉｃ-part-image-processing"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#the-%EF%BD%81%EF%BD%85%EF%BD%93%EF%BD%94%EF%BD%88%EF%BD%85%EF%BD%94%EF%BD%89%EF%BD%83-part-image-processing">#</a>the ａｅｓｔｈｅｔｉｃ part, image processing</h2>
<p>The hardest part of this whole project wasn’t fetching the data, it was processing the images. I didn’t want to just slap high-res JPEGs on the page. I wanted a specific look: a 1-bit dithered aesthetic that feels a bit retro.</p>
<p>In the early versions of this script, I relied on <strong>ImageMagick</strong>. It’s the industry standard for a reason; it has a built-in flag for dithering that looks great. To make it work in my Node script, I had to use <code>child_process.spawn</code> to actually run the terminal command from within JavaScript.</p>
<p>It looked something like this:</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066">import </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">{</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> spawn</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> }</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> from</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "node:child_process"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> convertWithMagick</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">inputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> outputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  return</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> new</span><span style="color:light-dark(#597B75, #7AA89F);--shiki-light:#597B75;--shiki-dark:#7AA89F"> Promise</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">((</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">resolve</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> reject</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">    const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> proc </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> spawn</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">"magick"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> [</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      inputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      "-dither"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      "FloydSteinberg"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      "-scale"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      "290x290"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">      "-monochrome"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      outputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    ],);</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    proc</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">on</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">"exit"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">code</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">code </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">===</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> resolve</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">();</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      else</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> reject</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">new</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Error</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`magick exited with code </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">${</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">code</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">`</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,),);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    },);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  },);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>This approach worked, but it felt “heavy.” I was spawning a new system process for every single album cover. Plus, it meant that anyone who wanted to build my site (including my CI/CD pipeline) needed to install the heavy ImageMagick binary, which made the site unbuildable on <a href="https://www.netlify.com/">Netlify</a> at all. I wanted this project to be “pure” Node.js—just <code>yarn</code> and it should work out-of-the-box.</p>
<p>So, I switched to <strong><a href="https://sharp.pixelplumbing.com/">sharp</a></strong>, which is significantly faster and runs natively in Node. But there was a catch: sharp doesn’t have a built-in “Floyd-Steinberg monochrome” filter. It can handle palettes, but it doesn’t give that specific 1-bit error-diffusion look I wanted.</p>
<p>So, I had to get my hands dirty and steal the pixel math from the internet. I searched for:</p>
<blockquote>
<p>floyd-steinberg dithering in javascript</p>
</blockquote>
<p>in my search engine to steal this dithering algorithm…</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">async</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> ditherWithSharp</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">inputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> outputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> data</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> info</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> }</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> sharp</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">inputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">resize</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">290</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 290</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> fit</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> "cover"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> },)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">greyscale</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">()</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">raw</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">()</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">toBuffer</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">({</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> resolveWithObject</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066"> true</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> },);</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> info</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> height </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> info</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">height</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> inputPixels </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> new</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Uint8Array</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">data</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // Create output buffer for RGBA (4 channels)</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> outputPixels </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> new</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> Uint8Array</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">width </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> height </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 4</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  for</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">let</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> y </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> y </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">&#x3C;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> height</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> y</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">++</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">    for</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">let</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> x </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> x </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">&#x3C;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> x</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">++</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> y </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> x</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> oldPixel </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">];</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> newPixel </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> oldPixel </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">&#x3C;</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 128</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> ?</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> :</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 255</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> error </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> oldPixel </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">-</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> newPixel</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> newPixel</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">      // Distribute error</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">x </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> &#x3C;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> +=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">error </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 7</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> /</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 16</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">y </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> &#x3C;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> height</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">        if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">x </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">></span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">-</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> +=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">error </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 3</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> /</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 16</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> +=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">error </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 5</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> /</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 16</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">        if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">x </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> &#x3C;</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> inputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> +=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">error </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> /</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 16</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">      // Map to RGBA: Black pixel = Opaque Black; White pixel = Transparent</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">      const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> idx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">*</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 4</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">newPixel </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">===</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // R</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // G</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 2</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // B</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 3</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 255</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // Alpha</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      }</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> else</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 1</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 2</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outIdx </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 3</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384"> =</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 0</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">;</span><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169"> // Transparent</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">      }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    }</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> sharp</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">Buffer</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">from</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outputPixels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,),</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    raw</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> width</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      height</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> height</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      channels</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 4</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    },</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  },)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">png</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">({</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      palette</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#CC6D00, #FFA066);--shiki-light:#CC6D00;--shiki-dark:#FFA066"> true</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      colors</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 2</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      effort</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">:</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99"> 10</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    },)</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    .</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">toFile</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">outputPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>This code manually iterates over every single pixel in the image buffer. It determines if a pixel should be black or white, calculates the difference (the error) between what the pixel <em>should</em> be and what it <em>is</em>, and pushes that error onto the neighboring pixels.</p>
<p>The result? I removed the external dependency entirely. Now, the site builds faster, the image processing is self-contained within the script, and I have total control over the output.</p>
<h3 id="tying-it-all"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#tying-it-all">#</a>tying it all</h3>
<p>Finally.</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">async</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> function</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> getMusicData</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">()</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> favAlbumIds </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> getFavoriteAlbumIds</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  for</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> rgid </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">of</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> favAlbumIds</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">    // check if we have the files, if not, fetch them</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">    if</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">!</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> fileExists</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">jsonPath</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)))</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> fetchAlbumData</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">rgid</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">      await</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8"> sleep</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#B35B79, #D27E99);--shiki-light:#B35B79;--shiki-dark:#D27E99">1000</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">    }</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">    // ... fetch covers ...</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">    // ... process images ...</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // read all the JSON files back into an array</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">  let</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> albums </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> [];</span></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  for</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> (</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">const</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> file </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">of</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold"> await</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> fs</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">readdir</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">DATA_DIR</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,))</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">    // ... parse JSON ...</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    albums</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">push</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">content</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,);</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">  // sort by release date (newest to oldest)</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">  albums</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#4D699B, #7E9CD8);--shiki-light:#4D699B;--shiki-dark:#7E9CD8">sort</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">((</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">a</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> b</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,)</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> =></span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span></span>
<span class="line"><span style="color:light-dark(#716E61, #727169);--shiki-light:#716E61;--shiki-dark:#727169">    // ... date sorting logic ...</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">  },);</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-light-font-weight:bold;--shiki-dark:#957FB8;--shiki-dark-font-weight:bold">  return</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> {</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> albums</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">,</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> };</span></span>
<span class="line"><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">}</span></span></code></pre>
<p>The <code>getMusicData</code> function is the main entry point. It gets the IDs, checks if files exist (so we don’t re-download or re-process images we already have), and coordinates the dithering. Finally, it reads all that cached data into a nice array and sorts the albums by their release date.</p>
<h2 id="the-result"><a class="heading-anchor" aria-hidden tabindex="-1" href="https://ege.celikci.me/blog/music-page/#the-result">#</a>the result</h2>
<p>It is actually available on the <a href="https://ege.celikci.me/music">music</a> page!</p>
<p>All of this backend work results in a simple <code>albums</code> array that I can loop through in my template.</p>
<pre class="shiki shiki-themes kanagawa-lotus kanagawa-wave" style="background-color:light-dark(#F2ECBC, #1F1F28);--shiki-light-bg:#F2ECBC;--shiki-dark-bg:#1F1F28;color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA" tabindex="0"><code><span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">&#x3C;div class="album"></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">  {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">for</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> album </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">in</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> music</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">albums </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">|</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> shuffle %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">set</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> artistNames </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> ""</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">for</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> ac </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">in</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">"artist-credit"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">if</span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8"> not</span><span style="color:light-dark(#D9A594, #FF5D62);--shiki-light:#D9A594;--shiki-dark:#FF5D62"> loop</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">first %}{% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">set</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> artistNames </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> artistNames </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C"> " · "</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}{% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">endif</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">set</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> artistNames </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">=</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> artistNames </span><span style="color:light-dark(#77713F, #E6C384);--shiki-light:#77713F;--shiki-dark:#E6C384">+</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> ac</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">name %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">endfor</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}</span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    &#x3C;a</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      href="https://listenbrainz.org/album/%7B%7B%20album%3C/span%3E%3Cspan%20style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">id }}"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      class="album__item"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      title="{{ album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">title }} by {{ artistNames }}"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    ></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      &#x3C;div class="album__cover"></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;img</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          class="album__cover__color"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          data-src="/assets/images/covers/colored/{{ album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">id }}.png"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          alt=""</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          width="290"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          height="290"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        /></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;img</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          class="album__cover__mono"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          src="https://ege.celikci.me/assets/images/covers/monochrome/%7B%7B%20album%3C/span%3E%3Cspan%20style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">id }}.png"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          alt="{{ album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">title }} by {{ artistNames }}"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          loading="lazy"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          width="290"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          height="290"</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        /></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      &#x3C;/div></span></span>
<span class="line"></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      &#x3C;div class="album__meta"></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;span class="album__title">{{ album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">.</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">title }}&#x3C;/span></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;span class="album__artist">{{ artistNames }}&#x3C;/span></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;span class="album__year" style="opacity: 0.5"></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">          ({{ album</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">[</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">"first-release-date"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">]</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA"> |</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> dateFromISO </span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">|</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> readableDate</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">(</span><span style="color:light-dark(#6F894E, #98BB6C);--shiki-light:#6F894E;--shiki-dark:#98BB6C">"yyyy"</span><span style="color:light-dark(#4E8CA2, #9CABCA);--shiki-light:#4E8CA2;--shiki-dark:#9CABCA">)</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> }})</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">        &#x3C;/span></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">      &#x3C;/div></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">    &#x3C;/a></span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">  {% </span><span style="color:light-dark(#624C83, #957FB8);--shiki-light:#624C83;--shiki-dark:#957FB8">endfor</span><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA"> %}</span></span>
<span class="line"><span style="color:light-dark(#545464, #DCD7BA);--shiki-light:#545464;--shiki-dark:#DCD7BA">&#x3C;/div></span></span></code></pre>
<p>By using a little CSS to toggle visibility between the <code>.album__cover__mono</code> and <code>.album__cover__color</code> images, I get a static site that feels dynamic, looks unique, and, best of all, updates automatically whenever I rate a new album on CritiqueBrainz.</p>]]>
      </content:encoded>
      <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Hello, world</title>
      <link>https://ege.celikci.me/blog/hello-world/</link>
      <guid isPermaLink="false">https://ege.celikci.me/blog/hello-world/</guid>
      <content:encoded><![CDATA[<p>I created this website as a way of expressing myself.</p>]]></content:encoded>
      <pubDate>Sun, 28 Sep 2025 00:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>