<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Ash Kyd&#39;s blog</title>
  <subtitle>Australian in Europe (no longer). Webdev, sometimes speaker, gamedev, vlogger. Click here for the good stuff. 🏳️‍🌈</subtitle>
  <link href="https://ashk.au/atom.xml" rel="self"/>
  <link href="https://ashk.au/"/>
  <updated>2026-07-07T11:18:12Z</updated>
  <id>https://ashk.au/</id>
  <icon>https://ashk.au/wp-content/icons/icon-192.png</icon>
  <author>
    <name>Ash Kyd</name>
    <email>ash@kyd.com.au</email>
  </author>
  
  <entry>
    <title>The web is dead, what&#39;s next?</title>
    <link href="https://ashk.au/2026/07/07/the-web-is-dead-whats-next/"/>
    <updated>2026-07-06T20:31:27Z</updated>
    <id>https://ashk.au/2026/07/07/the-web-is-dead-whats-next/</id>
    <summary>Long live the web. If Apple strangled it, it&#39;s the tech bros finally put it out of its misery.</summary><category term="life" /><category term="web" /><category term="ai" /><category term="Blog" /><content type="html">&lt;p style=&quot;background:rgb(255, 89, 0);color:white;padding:32px;text-align:center;position:relative;font-weight:bold;font-size:22px;&quot;&gt;
Continue reading this post for free, courtesy of Ash Kyd lol. 
&lt;img class=&quot;bespoke&quot; src=&quot;https://ashk.au/favicon.ico&quot; width=&quot;32&quot; height=&quot;32&quot; style=&quot;position:absolute;left:calc(50% - 32px);top:-32px;width:64px;border-radius:1000px;border:1px solid black;box-shadow:none;background:none;padding:0;&quot; alt=&quot;site favicon styled to look like a Substack paywall&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;I tried to open a new bank account the other day. I got through the text message verification, then it popped up a note telling my my browser isn&#39;t supported. I closed the tab and gave up. I might try again later, but just it&#39;s annoying we&#39;re back to the good old days of Please Use Internet Explorer And Update Your Flash Player.&lt;/p&gt;
&lt;p&gt;Then after a lot of internal deliberation, I figured the good The Guardian do for Australian journalism outweighs the transphobic bullshit the UK parent is renowned for, and I should probably subscribe. I got most of the way through the sign up form then got stuck in captcha hell; selecting buses and zebra crossings in a loop until I gave up. They won&#39;t even take my money.&lt;/p&gt;
&lt;p&gt;I&#39;ve been using RSS a bit more in an attempt to bring back a bit of variety to my news habits. But most of the sites I visit nowadays have the bog standard newsletter/cookie/ad popups that make it unpleasant to read. We&#39;re all familiar with this. It&#39;s just how it is now. But the AI content-pocalypse has led to more paywalls, that require logins, and/or get-the-magic-link-via-email-every-time workflows, and it&#39;s just bullshit, actually.&lt;/p&gt;
&lt;p&gt;And I shit you not, I just tried to log into one of the newsletters I used to sub to, and for some reason Substack is now age gated and requires a facial scan to access. It&#39;s just bonkers.&lt;/p&gt;
&lt;hr /&gt;
&lt;img style=&quot;max-width:320px;&quot; class=&quot;aligncenter&quot; src=&quot;https://ashk.au/wp-content/substack-age-gate.webp&quot; alt=&quot;Verify your age. Due to the Online Safety Act, readers in your country must verify their age to access Substack. By clicking the button below, you consent to Persona, our vendor, collecting, using, and utilizing their service providers to process your biometric information to verify your age, age fraud, and improve Persona&#39;s platform in accordance with its Privacy Policy. Your biometric information will be deleted instantly after 7 days. Begin verifying. SECURED WITH persona.&quot; width=&quot;800&quot; height=&quot;1300&quot; /&gt;
&lt;hr /&gt;
&lt;p&gt;I have a deep mistrust of content online nowadays. I think we all do, to some degree. It&#39;s hard not to be cynical when everything is driven by the perverse incentive to generate reams of neverending algorithm fodder, regardless of whether it&#39;s good. Or even necessary. Even if what I&#39;m reading was (on the off chance) actually written by a human; from OnlyFans to Substack (monetising far right extremists lol), everything is about Content with a capital C.&lt;/p&gt;
&lt;p&gt;And yet almost none of it has actual value. Just ask Google, and it&#39;ll find 5 sources to summarise for you.&lt;/p&gt;
&lt;p&gt;I started journalling last year when Mum was dying because I figured it&#39;s a better place to pour my grief, and I kept it up because I appreciate an end-to-end encrypted vault for my stupid thoughts. I don&#39;t need to monetise this stuff, and you don&#39;t need to read it. But now I&#39;m writing more and publishing less, and it&#39;s made me reevaluate what even is the point of blogging? We&#39;re in the dead internet era where this site sees more bot traffic than human by a large margin; nobody&#39;s reading tech articles and my little blogs are better suited to a private journal. And given the AI-powered advancements in mass surveillance capabilities, the very real opsec risks of posting anything publicly at all are much more real than they ever have been.&lt;/p&gt;
&lt;p&gt;But mostly, I&#39;m just not enjoying the cumulative output of humanity. So it&#39;s hard to feel encouraged to contribute to it.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Friday</title>
    <link href="https://ashk.au/2026/03/13/sticky-fingers-friday/"/>
    <updated>2026-03-13T00:44:05Z</updated>
    <id>https://ashk.au/2026/03/13/sticky-fingers-friday/</id>
    <summary>I am exhausted today. Late night for queer tech drinks, then an early start to let the Cockroach Men in. They came an hour and a half late because they had my number wrong and couldn’t get into the complex; the dude was overwhelmed and looked like he was going to burst into tears. Later, …  Continue reading “Friday”</summary><category term="life" /><category term="Blog" /><content type="html">&lt;p&gt;I am exhausted today. Late night for &lt;a href=&quot;https://lgbtqtech.bne.social/&quot;&gt;queer tech drinks&lt;/a&gt;, then an early start to let the Cockroach Men in. They came an hour and a half late because they had my number wrong and couldn’t get into the complex; the dude was overwhelmed and looked like he was going to burst into tears.&lt;/p&gt;
&lt;p&gt;Later, the little bottle of orange juice was structurally unsound and splurted out as I cracked the top. I decanted it into a glass.&lt;/p&gt;
&lt;p&gt;It’s sticky fingers Friday.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>QR Code creator</title>
    <link href="https://ashk.au/2026/02/21/qr-code-creator/"/>
    <updated>2026-02-21T12:27:31Z</updated>
    <id>https://ashk.au/2026/02/21/qr-code-creator/</id>
    <summary>You can create normal things like links or contact cards, but also map locations that open in your maps app, phone numbers, calendar invites and even wifi autoconnect prompts.</summary><category term="javascript" /><category term="software" /><category term="Announcing" /><content type="html">&lt;p&gt;I don’t know how many times I’ve searched for a QR code creator only to land on some dodgy, slow website that’s full of ads. So I made my own.&lt;/p&gt;
&lt;p&gt;For the longest time it was a bare basic nothingburger to get the job done. But today I spent a bit of time tweaking it, expending it, learning about Preact server rendering, adding a service worker to make it installable, and turning it into something a bit more feature complete.&lt;/p&gt;
&lt;p&gt;So here it is: &lt;a href=&quot;https://qr.kyd.au/&quot;&gt;https://qr.kyd.au&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;a href=&quot;https://qr.kyd.au/&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/og-image-1024x538.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;538&quot; src=&quot;https://ashk.au/wp-content/posts/og-image-1024x538.webp&quot; alt=&quot;A Safari window showing the app against a purple background.&quot; class=&quot;wp-image-1614&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/02/og-image-1024x538.webp 1024w, https://press.ash.ms/wp-content/uploads/2026/02/og-image-300x158.webp 300w, https://press.ash.ms/wp-content/uploads/2026/02/og-image-768x403.webp 768w, https://press.ash.ms/wp-content/uploads/2026/02/og-image.webp 1200w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;I mostly use it to quickly send stuff to my phone, especially while developing. &lt;/p&gt;
&lt;p&gt;But the new version is kind of fun because it supports a bunch of different types of QR codes, some which I didn’t even realise were possible. You can create normal things like links or contact cards, but also map locations that open in your maps app, phone numbers, calendar invites and even wifi autoconnect prompts.&lt;/p&gt;
&lt;p&gt;This is mostly a thing to scratch an itch, but I think it’s a pretty useful tool so hopefully you will too.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Fit a MapLibre 3d globe to the available screen size</title>
    <link href="https://ashk.au/2026/02/12/fit-a-maplibre-3d-globe-to-the-available-screen-size/"/>
    <updated>2026-02-11T23:16:27Z</updated>
    <id>https://ashk.au/2026/02/12/fit-a-maplibre-3d-globe-to-the-available-screen-size/</id>
    <summary>It’s not easy being a developer asked to calculate zoom levels on a 3d globe using the web mercator projection. So let’s ask the robots.</summary><category term="javascript" /><category term="maps" /><category term="How-to" /><content type="html">&lt;p&gt;MapLibre introduced a globe mode, which is really cool. But one thing I had a lot of trouble with was making the globe take up all the available screen space without either overflowing or leaving a bunch of whitespace at the sides.&lt;/p&gt;
&lt;p&gt;Initially I thought I could call &lt;code&gt;fitBounds&lt;/code&gt; with the coordinates on each corner of my globe, but that only worked at latitude 0; once we started panning and zooming around the calculation would break down. This is for several reasons:&lt;/p&gt;
&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;firstly, I was calculating coordinates based on the longitude, but for the latitude I’d just stuck the two poles in, figuring the top and bottom of the map would do. But the poles get visually closer together when you change the latitude. No good.&lt;/li&gt;
&lt;li&gt;in MapLibre the zoom level (&lt;code&gt;z&lt;/code&gt;) affects the map differently depending on your latitude. So at the poles, you need to have a super zoomed out &lt;code&gt;z&lt;/code&gt; value to see the same amount of globe as you do at the equator. This means you have to calculate the zoom value every time the latitude changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This turned out to be too hard and I’d given up a few times, until I started messing around with &lt;a href=&quot;https://antigravity.google/&quot;&gt;Antigravity&lt;/a&gt; trying to tune my bad algorithm.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;Every so often the LLM manages to do something that I would not in a million years. This is something I’ve been agonising over, and with a bit of iteration the damn thing did it with plain maths and known values:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fitTheGlobe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;mapRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mapRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientWidth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// 1. Determine how big (in pixels) we want the globe diameter to be on screen.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; padding &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// visually tweak to fit&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetDiameterPx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; padding &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// 2. MapLibre&#39;s zoom logic is based on a Mercator projection, which stretches&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// the world as you move away from the equator by a factor of 1/cos(latitude).&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// To keep the globe a constant physical size, we must shrink our target&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// dimensions by cos(latitude) to counteract that internal magnification.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mapRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lat&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; latRad &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lat &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mercatorScaleCorrection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;latRad&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// 3. Calculate the necessary world circumference (in pixels) to achieve&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// our target diameter. On a sphere, Circumference = Diameter * PI.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requiredWorldCircumferencePx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; targetDiameterPx &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; mercatorScaleCorrection&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// 4. MapLibre defines Zoom 0 as a world circumference of 512px.&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Each zoom level doubles the pixel size (exponential growth: 512 * 2^z).&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// We use Math.log2 to convert that pixel growth back into a linear zoom level &#39;z&#39;.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetZoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requiredWorldCircumferencePx &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentZoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mapRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getZoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; threshold &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.01&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentZoom &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; targetZoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        mapRoot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flyTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;zoom&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; targetZoom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; animationDuration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;essential&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I prompted it to split the calculation out and document what’s happening and I think this is a pretty accurate take. The conditional at the end guards against rounding errors and jitter. The vibes feel good.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-02-12-at-9.11.01-am-1024x569@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-02-12-at-9.11.01-am-1024x569@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-02-12-at-9.11.01-am-1024x569@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-02-12-at-9.11.01-am-1024x569@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;569&quot; src=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-02-12-at-9.11.01-am-1024x569@1200.jpg&quot; alt=&quot;A 3D globe with Nasa&#39;s Blue Marble satellite imagery wrapped around it, showing Australia, bit of Asia, Antarctica, and New Zealand (weirdly, usually maps don&#39;t show New Zealand).&quot; class=&quot;wp-image-1605&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/02/Screenshot-2026-02-12-at-9.11.01-am-1024x569.jpg 1024w, https://press.ash.ms/wp-content/uploads/2026/02/Screenshot-2026-02-12-at-9.11.01-am-300x167.jpg 300w, https://press.ash.ms/wp-content/uploads/2026/02/Screenshot-2026-02-12-at-9.11.01-am-768x426.jpg 768w, https://press.ash.ms/wp-content/uploads/2026/02/Screenshot-2026-02-12-at-9.11.01-am-1536x853.jpg 1536w, https://press.ash.ms/wp-content/uploads/2026/02/Screenshot-2026-02-12-at-9.11.01-am-2048x1137.jpg 2048w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;Look, I don’t fully understand the maths, and there’s a bit more padding the bigger your screen gets. But deeply understanding this is not necessary for me to get my work done so I’m fine with that.&lt;/p&gt;
&lt;p&gt;Ultimately it would be nice to have a built-in function in MapLibre, and if I get a moment I’ll see if I can wrap my head around it enough to make a PR back upstream. In the meantime, I hope this helps.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Dealing with Missing Glyphs in MapLibre</title>
    <link href="https://ashk.au/2026/01/31/dealing-with-missing-glyphs-in-maplibre/"/>
    <updated>2026-01-31T13:12:19Z</updated>
    <id>https://ashk.au/2026/01/31/dealing-with-missing-glyphs-in-maplibre/</id>
    <summary>It’s not supposed to look like that?! Why the heck is that island in the pacific called SMOA? A technical deep dive.</summary><category term="code" /><category term="maps" /><category term="How-to" /><content type="html">&lt;p&gt;I’ve been working with MapLibre a lot recently, and every so often I come across a weird bug. This one was particularly annoying; our corporate font doesn’t have the glyphs required to render certain characters. Rolling around the map I’d spot places like Vit Nam (&lt;em&gt;Việt Nam&lt;/em&gt;), and Smoa (Sāmoa), and put it on my to-do list to deal with later.&lt;/p&gt;
&lt;p&gt;Well, now is later. And I have to find a fix.&lt;/p&gt;
&lt;p&gt;My first thought was that we were possibly using the wrong place names. My second thought was to change the field we take names from. I don’t recall what the default was, but changing it to &lt;code&gt;name:en&lt;/code&gt; was enough to get most labels rendering properly.&lt;/p&gt;
&lt;p&gt;However Samoa was stubborn; I was still getting&amp;nbsp;“&lt;em&gt;Smoa&lt;/em&gt;”, and had to do some investigation to work it out.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-investigation-whats-in-the-tile&quot;&gt;What’s in a tile?&lt;/h2&gt;
&lt;p&gt;Web maps use tiles to break up data downloads, and modern ones use the pbf (protocolbuffer format) rather than PNGs or JPEGs. PBFs give you vector data so you can style your maps on the frontend.&lt;/p&gt;
&lt;p&gt;My first step was to determine if the issue was in the data itself, or the rendering. I identified the specific pbf tile by zooming right in on Samoa and picking a random one out of the network tab, then downloaded it to inspect.&lt;/p&gt;
&lt;p&gt;Since I don’t have time for this, I got the AI to write a script to list the properties the renderer could use, using &amp;nbsp;&lt;code&gt;@mapbox/vector-tile&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;node:fs/promises&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Pbf &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pbf&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; VectorTile &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@mapbox/vector-tile&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tile.pbf&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VectorTile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pbf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Inspect the &#39;place&#39; layer where country labels live&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; layer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;place&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Find the feature by ISO code using a functional approach&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; samoa &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; layer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; layer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;feature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iso_a2 &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;WS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;samoa&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;samoa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;properties&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This filtered through the features and printed every property available on the country object, which I think will be useful for all kinds of other styling purposes. The output, truncated:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;country&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;iso_a2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sāmoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:am&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ሳሞአ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:ar&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ساموا&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:be&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Самоа&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:bg&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Самоа&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:br&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Samoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:ca&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Samoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:da&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Samoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:de&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Samoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:el&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Σαμόα&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name:en&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sāmoa&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this specific tile set,&amp;nbsp;every&amp;nbsp;common English fallback property contained the non-ASCII character&amp;nbsp;&lt;code&gt;ā&lt;/code&gt;. Since our custom brand font didn’t include a glyph for that character, MapLibre dropped it leaving us with “Smoa”.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-solution-on-the-fly-label-rewriting&quot;&gt;So like, ok now what? Search &amp;amp; replace for MapLibre labels&lt;/h2&gt;
&lt;p&gt;Since I have no control over our font &lt;em&gt;or&lt;/em&gt; tiles, I needed to rewrite the label on the fly. I hoped I could use some kind of string replace function to turn the mācron characters into the standard letter &lt;code&gt;a&lt;/code&gt;, but MapLibre expressions don’t have a&amp;nbsp;&lt;code&gt;string.replace()&lt;/code&gt;&amp;nbsp;function.&lt;/p&gt;
&lt;p&gt;Fortunately, for countries, we can write MapLibre expressions using the ISO country code (&lt;code&gt;iso_a2&lt;/code&gt;) to replace the label value:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nameFallback &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&#39;coalesce&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;name_en&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;name:en&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;name:latin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

layer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;text-field&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&#39;case&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Is this a country label?&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;==&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;country&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;match&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;iso_a2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;WS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Samoa&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;token comment&quot;&gt;// ASCII rewrite for Sāmoa&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;CI&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cote d&#39;Ivoire&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// ASCII rewrite for Côte d&#39;Ivoire&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;ST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Sao Tome and Principe&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    nameFallback              &lt;span class=&quot;token comment&quot;&gt;// Default country fallback&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  nameFallback                &lt;span class=&quot;token comment&quot;&gt;// Default for cities, towns, etc.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m using Javascript to search through an existing style.json and set values. But you could just as easily implement a short list of hard-coded countries directly in your style.json. Not a great solution, but a band-aid to get us by for now.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;As an addendum, I found the ability to introspect PBF files was a super useful debug tool, especially when writing niche styles by hand. So I turned the script into &lt;a href=&quot;https://www.npmjs.com/package/pbf-introspect&quot;&gt;a little npm package&lt;/a&gt; that you can run for any remote url:&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-01-31-at-10.57.00-pm-1024x755.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;755&quot; src=&quot;https://ashk.au/wp-content/posts/Screenshot-2026-01-31-at-10.57.00-pm-1024x755.webp&quot; alt=&quot;&quot; class=&quot;wp-image-1582&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/Screenshot-2026-01-31-at-10.57.00-pm-1024x755.webp 1024w, https://press.ash.ms/wp-content/uploads/2026/01/Screenshot-2026-01-31-at-10.57.00-pm-300x221.webp 300w, https://press.ash.ms/wp-content/uploads/2026/01/Screenshot-2026-01-31-at-10.57.00-pm-768x566.webp 768w, https://press.ash.ms/wp-content/uploads/2026/01/Screenshot-2026-01-31-at-10.57.00-pm-1536x1132.webp 1536w, https://press.ash.ms/wp-content/uploads/2026/01/Screenshot-2026-01-31-at-10.57.00-pm.webp 1902w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;It’s kind of a game changer, just run &lt;code&gt;npx pbf-introspect https://someurl&lt;/code&gt; and get all* of the properties straight back. The json output is also handy to prompt your robot coworker, if you have one.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How good is cheesy rice tho</title>
    <link href="https://ashk.au/2026/01/20/how-good-is-cheesy-rice-tho/"/>
    <updated>2026-01-20T03:11:21Z</updated>
    <id>https://ashk.au/2026/01/20/how-good-is-cheesy-rice-tho/</id>
    <summary>I’ve been loving my rice cooker. Between that and the air fryer it makes it super easy to prepare work-from-home lunches without having to do much of anything at all. It’s a little Cuckoo 6-cup pressure rice cooker, which is the perfect size for me. And, look, probably the perfect size for anyone unless you …  Continue reading “How good is cheesy rice tho”</summary><category term="food" /><category term="recipe" /><category term="Blog" /><content type="html">&lt;p&gt;I’ve been loving my rice cooker. Between that and the air fryer it makes it super easy to prepare work-from-home lunches without having to do much of anything at all.&lt;/p&gt;
&lt;p&gt;It’s a little Cuckoo 6-cup pressure rice cooker, which is the perfect size for me. And, look, probably the perfect size for anyone unless you have a massive family. I don’t know why rice cookers are always so enormous.&lt;/p&gt;
&lt;p&gt;I’ll usually throw in a bit of seasoning, some msg, assorted herbs, bit of chilli depending on my mood, then put some marinated chicken in the air fryer, pop some some steamed veg on, and voila! It’s a low effort meal, and I can keep working at my desk while it cooks. It’s healthy and tasty, and one of my go-to CBF food strategies.&lt;/p&gt;
&lt;p&gt;But today I was feeling a bit of a malaise. I didn’t just want rice and chicken, I wanted something more. Something extravagent. Something with a bit of fat to it, actually.&lt;/p&gt;
&lt;p&gt;So when the rice cooker finished I had the genius idea to grate a bit of cheese into it, and stir it through. With a bit of parsley, some chicken salt, a springle of MSG you get cheesy rice! Fucking brilliant.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Continental &amp;nbsp;Cheesy Rice&lt;/h2&gt;
&lt;p&gt;There used to be a Continental cheesy rice sachet thing I loved when I was a kid. I haven’t had it for decades so I had no idea if they still do it. The real thing was a surprise I didn’t know I needed, but I was curious so I googled it. And what the controversy!&lt;/p&gt;
&lt;figure class=&quot;wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex&quot;&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-old-1024x1024@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-old-1024x1024@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-old-1024x1024@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-old-1024x1024@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-id=&quot;1558&quot; src=&quot;https://ashk.au/wp-content/posts/cheesy-rice-old-1024x1024@1200.jpg&quot; alt=&quot;The old cheesy rice packaging was sort of tan, green and red. The packaging is composited against a wooden benchtop with badges proclaiming: &amp;quot;no artificial colours &amp;amp; flavours&amp;quot; and &amp;quot;proudly made in Australia&amp;quot;&quot; class=&quot;wp-image-1558&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old-1024x1024.jpg 1024w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old-300x300.jpg 300w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old-150x150.jpg 150w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old-768x768.jpg 768w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old-100x100.jpg 100w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-old.jpg 1200w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-new-1024x1024@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-new-1024x1024@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-new-1024x1024@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/cheesy-rice-new-1024x1024@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-id=&quot;1557&quot; src=&quot;https://ashk.au/wp-content/posts/cheesy-rice-new-1024x1024@1200.jpg&quot; alt=&quot;The new packaging is green, teal and red. It&#39;s less good. Also it&#39;s just &amp;quot;cheesy&amp;quot; rice now, not &amp;quot;tasty cheesy rice&amp;quot;.&quot; class=&quot;wp-image-1557&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new-1024x1024.jpg 1024w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new-300x300.jpg 300w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new-150x150.jpg 150w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new-768x768.jpg 768w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new-100x100.jpg 100w, https://press.ash.ms/wp-content/uploads/2026/01/cheesy-rice-new.jpg 1200w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;/figure&gt;
&lt;p&gt;The product is still &lt;a href=&quot;https://www.continental.com.au/p/cheesy-rice.html/09300830047321&quot;&gt;listed on the Continental website&lt;/a&gt;, but the product reviews section is abuzz with activity. Lucy says:&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;This is my sons favourite food! I add garlic and ginger to spice it up and we love it! We have it at least twice a week. Please tell me you will never discontinue this product! I have just found out woolworths will no longer be selling this product. I am so disappointed. It looks like Coles still sell it so they will become my new weekly shopping destination.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To which the team at Continental replied:&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Hi there, Lucy! We are glad that your family are enjoying our Cheesy rice. The team will be thrilled to hear this feedback and rest assured this will be noted for future product development. This has been deleted in Woolies and Coles, but you may still find this in IGA. Hope this helps!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Deleted in Woolies and Coles! I don’t know how Lucy will be able to rest assured about that.&lt;/p&gt;
&lt;p&gt;I can’t say I’m hugely surprised it’s disappearing, given the explosion of the more convenient pouch- and microwaveable-rice segment. But the loyal Cheesy Rice consumers are not to be underestimated. Zarli says:&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;I have been eating cheesy rice for at least 7 years and I don’t think I’ll ever stop. I have it with creamy garlic prawns , chicken wedges , any other meal , on a sandwhich , by itself. I might turn into a cheesy rice if I keep going but please never stop making cheesy rice it’s the best thing ever&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One thing is clear, the fans will be devastated when this made-in-Australia staple of ’90s pantries finally stops production. I might pop down to IGA and see if I can hunt down a packet for old time’s sake. Bit of weird food nostalgia.&lt;/p&gt;
&lt;p&gt;That said, one reviewer wasn’t happy with the product at all. Phil gave it a scathing 1 out of 5 stars, saying “Not very cheesy.” &lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“The previous packs from last year were 100% better”&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  
  <entry>
    <title>A chain of things that went wrong which I fixed with some help from my friends</title>
    <link href="https://ashk.au/2026/01/18/it-started-when-the-router-broke/"/>
    <updated>2026-01-18T06:20:02Z</updated>
    <id>https://ashk.au/2026/01/18/it-started-when-the-router-broke/</id>
    <summary>My place is dark, so I do really like having my lamps and lights come alive in the morning and turn off at night. It’s kind of a SAD lamp situation. I also have a bunch of indoor plants and a fish tank that are only alive because of these automations, so there’s really no going back now.</summary><category term="hardware" /><category term="home assistant" /><category term="home automation" /><category term="networking" /><category term="Blog" /><content type="html">&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-scaled@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-scaled@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-scaled@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-scaled@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img src=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-scaled@1200.jpg&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;&lt;hr /&gt;&lt;p&gt;When I first moved back to Australia, I splurged a little on a new Internet router. I went with Google Wifi (now Nest Wifi) because they looked really pretty and the big name was reassuring against the backdrop of crappy brands with outdated software getting hacked all the time.&lt;br /&gt;&lt;br /&gt;It turned out to be a medium sized mistake, because it didn’t work very well, the app was slow and buggy, and it just kinda sucked. But it worked fine enough for the past five years, until last week when it started glitching out.&lt;/p&gt;
&lt;p&gt;First it disconnected from the mothership in the cloud so I couldn’t login to see what was happening. Then, slowly but surely, chunks of the Internet started disappearing. I rebooted to see if that would fix anything, but the router never came back up and completely took out my internet.&lt;br /&gt;&lt;br /&gt;I swapped over to a 4G router and plugged my personal SIM card in. That ran ok for a while until I realised I could also use it to route directly into the NBN. But it was a fiddly solution so I wanted to replace it as soon as possible.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Replacing my router&lt;/h2&gt;
&lt;p&gt;I already have a separate wifi point set up, so I don’t need wifi in my router. This made things easier and cheaper. I asked around the Internet on my shortlist ended up looking like this:&lt;/p&gt;
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tp-link.com/au/business-networking/omada-sdn-router/er707-m2/v1/&quot;&gt;&lt;strong&gt;TP-Link Omada ER707-M2&lt;/strong&gt;&lt;/a&gt; – the cheapest option, thank you China. A 2.5 gbit router with little else going for it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techspecs.ui.com/unifi/cloud-gateways/ucg-max&quot;&gt;&lt;strong&gt;Ubiquiti Cloud Gateway Max&lt;/strong&gt;&lt;/a&gt; – a very pretty, more capable option for more than double the price. I was tempted by the shiny marketing, but I’m very reluctant to spend my money in the Untied States given the current social and geopolitical bullshit.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.friendlyelec.com/index.php?route=product/product&amp;amp;product_id=289&quot; data-type=&quot;link&quot; data-id=&quot;https://www.friendlyelec.com/index.php?route=product/product&amp;amp;product_id=289&quot;&gt;&lt;strong&gt;NanoPi R6S&lt;/strong&gt;&lt;/a&gt; – a single board computer, that runs OpenWRT or your own choice of Linux if you want to tinker. This looks kinda awesome, but shipping was exorbitant and the prices on AliExpress left a bad taste. Also it wouldn’t arrive until next month anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I got the Omada. Paid ten bucks for shipping via Uber and it arrived a couple of hours later, before sitting in the box until I finished work and plugged it in.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-1024x771@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-1024x771@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-1024x771@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-1024x771@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://ashk.au/wp-content/posts/PXL_20260115_014530436-2-1024x771@1200.jpg&quot; alt=&quot;The TP-Link Omada ER707-M2 sitting on a desk. It has two 2.5gbit ports, 5x 1 gbit ports, and a USB connection on the front. It&#39;s just a sleek black metal box, like an old Netgear router.&quot; class=&quot;wp-image-1543&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/PXL_20260115_014530436-2-1024x771.jpg 1024w, https://press.ash.ms/wp-content/uploads/2026/01/PXL_20260115_014530436-2-300x226.jpg 300w, https://press.ash.ms/wp-content/uploads/2026/01/PXL_20260115_014530436-2-768x578.jpg 768w, https://press.ash.ms/wp-content/uploads/2026/01/PXL_20260115_014530436-2-1536x1157.jpg 1536w, https://press.ash.ms/wp-content/uploads/2026/01/PXL_20260115_014530436-2-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;I also want to thank the &lt;a href=&quot;https://bne.social/&quot;&gt;bne.social&lt;/a&gt; community who have been donating money into the pile to keep the server going. We dipped into the kitty to replace this one, and though I feel very weird about taking people’s money, it’s a pretty necessary investment.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;TP-Link Omada ER707-M2&lt;/h2&gt;
&lt;p&gt;Unforch I don’t especially like it.&lt;/p&gt;
&lt;p&gt;Physically it’s great; it’s a solid metal chassis which harks back to the blue Netgear routers of old. Feels premium. But mentally, the software kind of sucks.&lt;/p&gt;
&lt;p&gt;I haven’t been able to get IPv6 working yet because it doesn’t support it by default. I’ve poked around and managed to get a WAN assignment, but local machines still can’t see out to the internet. The inline help is daft to the point of useless, and the docs are questionable at best.&lt;/p&gt;
&lt;p&gt;Also frustratingly while there’s an option to enable MDNS, it doesn’t seem to do anything so I can’t resolve any local hostnames. I didn’t even realise that was a router feature until I bought one that didn’t do it. It does have an option to add custom DNS entries, but I can’t get them to work either, and it seems to be a widespread issue.&lt;/p&gt;
&lt;p&gt;So it’s a bit of a weird one. To me that rules it out for home or small business use, which leaves me wondering: who is this even for? Cheap idiots like me? Ultimately it’s been a huge fuck-around and I’m somewhat regretting not waiting and going the simpler Linux route.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;The dim home: when the smarts go away&lt;/h2&gt;
&lt;p&gt;To add insult to injury, when the router died it took out my smart home with it. Suddenly none of my lights or outlets were online, which meant my cosy, low-light collection of lamps and accents were no longer usable. The fish tank wasn’t turning on, the bookshelf wasn’t illuminating, the little bulb with the patterns by the couch was dark, and the miniature salt lamp? You guessed it: out like a light. On the flip side, the lamps either side of my bed got stuck at full brightness, so come bedtime I had to reach down into the cobwebs at the back to flick them off at the wall.&lt;/p&gt;
&lt;p&gt;But perhaps the most insulting thing was I had to use the &lt;a href=&quot;https://www.mamamia.com.au/big-light-debate/&quot;&gt;big lights&lt;/a&gt;. And even worse, they were all configured at different colour temperatures. Yucko.&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“The big light is for interrogation purposes only”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All my smart stuff runs through &lt;a href=&quot;https://www.home-assistant.io/&quot;&gt;Home Assistant&lt;/a&gt; which I quite like. But most of my hardware is run over Zigbee through a hacked dongle running custom firmware that I just couldn’t get talk to me any more, which meant I couldn’t control any of my lights or switches.&lt;/p&gt;
&lt;p&gt;I was exhausted after a week at work and not really mentally in it, so I bought the &lt;a href=&quot;https://www.home-assistant.io/connect/zbt-2/&quot;&gt;Home Assistant Connect ZB-2&lt;/a&gt; zigbee/thread adapter to replace it. I was hesitant because B has a massive and vocal Home Assistant grudge after he bought into a hardware product they overpromised and completely failed to deliver on. But having seen some reviews, and read their fairly frank explanation of what this thing is and what the limitations are, I figured I’d take a punt. I paid express post so thanks to the inconveniently located weekend non-delivery schedule, it’s going to arrive next week.&lt;/p&gt;
&lt;p&gt;On Saturday, B came over and started poking around at the dongle, and we pretty quickly discovered that it’s working completely fine. It was just that the IP address had changed so Home Assistant couldn’t find it. Duh, in retrospect. But now everything works again.&lt;/p&gt;
&lt;p&gt;I also took the opportunity to pair my original first gen Lifx colour bulb that’s been sitting disconnected in my desk lamp after it randomly stopped working a year or so back. I’d tried to set it up a few times since, but when I paired it this time, the app helpfully told me why it wasn’t working: this version of Android was too new to connect to the bulb. So I pulled out my emergency iPad, charged it up, and got this ancient thing back online. I bought the light bulb in a black Friday sale back in 2014, and it’s travelled the world with me since. Kind of cool.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/image-7-1024x905.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;905&quot; src=&quot;https://ashk.au/wp-content/posts/image-7-1024x905.webp&quot; alt=&quot;The gmail page showing a mailout from Lifx offering a Black Friday Special of 20% off and a very retro looking blue &amp;quot;shop now&amp;quot; button. &amp;quot;For this weekend only, save 20% on all LIFX products. With free shipping worldwide*, you&#39;ll never buy LIFX bulbs this cheap ever again!&amp;quot; Dated 29th November 2014.&quot; class=&quot;wp-image-1545&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/image-7-1024x905.webp 1024w, https://press.ash.ms/wp-content/uploads/2026/01/image-7-300x265.webp 300w, https://press.ash.ms/wp-content/uploads/2026/01/image-7-768x679.webp 768w, https://press.ash.ms/wp-content/uploads/2026/01/image-7-1536x1357.webp 1536w, https://press.ash.ms/wp-content/uploads/2026/01/image-7.webp 1546w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;The final thing I’m happy about was setting up a little automation to make sure all my Big Lights are in sync colour temperature-wise. I was really impressed with &lt;a href=&quot;https://gemini.google.com/&quot;&gt;Gemini&lt;/a&gt;, it combined all my requirements into one automation, and I learned a bunch in the process. The Home Assistant GUI is fine enough, but having Gemini create snippets I could edit with YAML, and have them appear back in the GUI is very, very cool actually. I had no idea you could use multiple triggers, or add template logic in values.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/image-6-1024x687.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;687&quot; src=&quot;https://ashk.au/wp-content/posts/image-6-1024x687.webp&quot; alt=&quot;Light: Turn on Action./ checked checkbox &amp;quot;Color temperature in Kelvin&amp;quot; The value is a Jinja2 template that basically says when the hour is between 7 and 17 make the colour temperature cool, otherwise warm. 5278 and 4100 respectively.&quot; class=&quot;wp-image-1544&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2026/01/image-6-1024x687.webp 1024w, https://press.ash.ms/wp-content/uploads/2026/01/image-6-300x201.webp 300w, https://press.ash.ms/wp-content/uploads/2026/01/image-6-768x515.webp 768w, https://press.ash.ms/wp-content/uploads/2026/01/image-6.webp 1046w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;Ultimately: this whole thing has been a pain in the butt. I don’t want to have to deal with any of this but I have dug myself into a deep home automation hole in particular.&lt;/p&gt;
&lt;p&gt;My place is dark, so I do really like having my lamps and lights come alive in the morning and turn off at night. It’s kind of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Seasonal_affective_disorder&quot;&gt;SAD lamp&lt;/a&gt; situation. I also have a bunch of indoor plants and a fish tank that are only alive because of these automations, so there’s really no going back now.&lt;/p&gt;
&lt;p&gt;So I don’t know. Now I’m back online I will have to make peace with the situation and make things as resilient as possible. One of the ways is by disconnecting my automations from the internet completely, but I’ll never be able to disconnect them from my home network. Maybe. Let me know if you have a good zigbee switch that doesn’t burn through batteries.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to type GeoJSON objects with custom properties in TS/JS</title>
    <link href="https://ashk.au/2025/12/16/how-to-type-geojson-objects-with-custom-properties-in-ts-js/"/>
    <updated>2025-12-16T02:35:23Z</updated>
    <id>https://ashk.au/2025/12/16/how-to-type-geojson-objects-with-custom-properties-in-ts-js/</id>
    <summary>Just a quick note because I found this useful and the @types/geojson docs aren’t especially verbose.</summary><category term="javascript" /><category term="How-to" /><content type="html">&lt;p&gt;Just a quick note because I found this useful and the &lt;code&gt;@types/geojson&lt;/code&gt; docs aren’t especially verbose.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Type a GeoJSON returned from a fetch (TypeScript)&lt;/h2&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; FeatureCollection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Geometry &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;geojson&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Create a standard GeoJSON object with custom properties for each feature:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyFeatureCollection&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FeatureCollection&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;
    Geometry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Your types go here&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      height&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      color&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Fetch and apply types to our GeoJSON obj&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myGeoJSON &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/au.geo.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyFeatureCollection&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point you get autocomplete in VSCode Intellisense (or equivalent), for standard GeoJSON props, as well as your own custom properties. Pretty cool.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large invertable&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/intellisense-geojson-1024x230.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;230&quot; src=&quot;https://ashk.au/wp-content/posts/intellisense-geojson-1024x230.webp&quot; alt=&quot;In VSCode, accessing properties on a GeoJSON feature shows a dropdown with recommendations based on our types.&quot; class=&quot;wp-image-1528&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2025/12/intellisense-geojson-1024x230.webp 1024w, https://press.ash.ms/wp-content/uploads/2025/12/intellisense-geojson-300x67.webp 300w, https://press.ash.ms/wp-content/uploads/2025/12/intellisense-geojson-768x172.webp 768w, https://press.ash.ms/wp-content/uploads/2025/12/intellisense-geojson.webp 1444w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Type a GeoJSON returned from a fetch (JSDoc)&lt;/h2&gt;
&lt;p&gt;Same again, but in Javascript, NodeJS etc:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @typedef {import(&#39;geojson&#39;).FeatureCollection&amp;lt;
 *   import(&#39;geojson&#39;).Geometry,
 *   {
 *     name: string;
 *     id: string;
 *     height?: number;
 *     color?: string;
 *   }
 * &gt;} MyFeatureCollection
 */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myGeoJSON &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/au.geo.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/** @type {Promise&amp;lt;MyFeatureCollection&gt;} */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>The curious case of the complimentary car chargers</title>
    <link href="https://ashk.au/2025/12/04/bcec-charging/"/>
    <updated>2025-12-04T02:44:14Z</updated>
    <id>https://ashk.au/2025/12/04/bcec-charging/</id>
    <summary>The Brisbane convention centre has cheap parking and free car charging. For $16 in parking I can fill my car up with as much juice as I like, which is a pretty good deal, as long as I remember to do it.</summary><category term="ev" /><category term="life" /><category term="Blog" /><content type="html">&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20240815_215226574-scaled@1680.avif 1680w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20240815_215226574-scaled@1200.webp 1200w&quot; type=&quot;image/webp&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20240815_215226574-scaled@987.avif 987w&quot; type=&quot;image/avif&quot; /&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/PXL_20240815_215226574-scaled@1200.jpg 1200w&quot; type=&quot;image/jpg&quot; /&gt;
&lt;img src=&quot;https://ashk.au/wp-content/posts/PXL_20240815_215226574-scaled@1200.jpg&quot; alt=&quot;&quot; /&gt;
&lt;/picture&gt;&lt;hr /&gt;&lt;p&gt;The Brisbane Convention Centre has cheap parking, and free car charging. So for $16 a day in parking, I can fill my car up with as much juice as I like. Which is a pretty good deal.&lt;br /&gt;&lt;br /&gt;I’ve been meaning to charge the EV for a couple of weeks. It’s been sitting on 10% battery for a fair while now, but just haven’t got around to it. There’s only 8 car chargers, and they get busy during the day, so I usually drop the car in overnight, schedule it to start charging in the morning when there’s plenty of solar in the grid, and voila; free volts. But it’s a big ask. Especially when it’s late, you’re snoozy, and just want to go to bed.&lt;br /&gt;&lt;br /&gt;So in the interests of trying something new, I organise with a friend to charge it tomorrow at their apartment complex. We work out the details and I put it in my calendar, but then my brain twitches:&lt;br /&gt;&lt;br /&gt;Wait a minute.&lt;br /&gt;&lt;br /&gt;Didn’t you drop the car in to charge last week?&lt;br /&gt;&lt;br /&gt;And did you ever pick it up again?&lt;/p&gt;
&lt;p&gt;No?&lt;br /&gt;&lt;br /&gt;How much is THAT going to cost? Will it even still be there? Have they cut the cable and towed it to an undisclosed location?&lt;br /&gt;&lt;br /&gt;PANIC!&lt;br /&gt;&lt;br /&gt;With a massive pit in my stomach I set my Slack status to “lunch” and race out the door, almost forgetting my car keys. There’s no scooters around, but the bus is only a stop away, so I take that.&lt;br /&gt;&lt;br /&gt;I share my situation with the lady in the car park office, who seems nonplussed. “Just drive out” she says.&lt;br /&gt;&lt;br /&gt;So I do.&lt;br /&gt;&lt;br /&gt;And you know what? The ticket machine has forgotten about me. The QR code scans as per normal, and as the boom gates lift the screen flashes the usual message: “Charged: $16”.&lt;br /&gt;&lt;br /&gt;I double-check, and sure enough the invoice has only charged me for today. I was expecting it to run into the hundreds, but there were no consequences whatsoever for my whatever-it-was moment. Bank error in my favour, I’m off scot free. And after all that it’s kind of nice to know that the machines can have forgetful moments too.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Announcing Alchemize v3 — faster, smarter, and sleeker than ever</title>
    <link href="https://ashk.au/2025/10/30/announcing-alchemize-v3-faster-smarter-and-sleeker-than-ever/"/>
    <updated>2025-10-30T09:01:24Z</updated>
    <id>https://ashk.au/2025/10/30/announcing-alchemize-v3-faster-smarter-and-sleeker-than-ever/</id>
    <summary>Alchemize is a lightweight tool to minify and pretty-print your code snippets from around the web. v3 is now available with a refreshed interface and support for modern JS &amp; CSS features.</summary><category term="javascript" /><category term="self" /><category term="Announcing" /><content type="html">&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://alchemizeapp.com/&quot; data-type=&quot;link&quot; data-id=&quot;https://alchemizeapp.com/&quot;&gt;Alchemize&lt;/a&gt;&lt;/strong&gt; is a lightweight tool to &lt;strong&gt;minify and pretty-print your code snippets&lt;/strong&gt; from around the web. It’s perfect for developers to format messy API responses, debug broken or unreadable code, learn new JS tricks from the Terser engine, or share polished snippets with teammates.&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/og-image-optim-1024x538.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;538&quot; src=&quot;https://ashk.au/wp-content/posts/og-image-optim-1024x538.webp&quot; alt=&quot;The application has a minimal interface and shows some HTML code with buttons to minify &amp;amp; pretty-print&quot; class=&quot;wp-image-1497&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2025/10/og-image-optim-1024x538.webp 1024w, https://press.ash.ms/wp-content/uploads/2025/10/og-image-optim-300x158.webp 300w, https://press.ash.ms/wp-content/uploads/2025/10/og-image-optim-768x403.webp 768w, https://press.ash.ms/wp-content/uploads/2025/10/og-image-optim.webp 1200w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/figure&gt;
&lt;p&gt;Initially written in jQuery and after a good ten years since the last release, I thought it was time to rewrite my little code playground from scratch.&lt;/p&gt;
&lt;p&gt;The new Alchemize is based on &lt;a href=&quot;https://preactjs.com/&quot;&gt;Preact&lt;/a&gt; and &lt;a href=&quot;https://vite.dev/&quot;&gt;Vite&lt;/a&gt; for a more modern developer experience, uses the same editor component as VSCode, and natively supports light and dark mode. It’s been upgraded to mainly use Terser and Prettier under the hood, which brings updated JS &amp;amp; CSS support and should make these a lot easier to maintain going forward.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;♻️ &lt;strong&gt;Modern tech stack&lt;/strong&gt; A complete under-the-hood revamp for better performance and future-ready support.&lt;br /&gt;🛠️ &lt;strong&gt;Industry-standard tooling&lt;/strong&gt; Built on trusted technologies like &lt;em&gt;Prettier&lt;/em&gt; and &lt;em&gt;Terser&lt;/em&gt; for formatting and minification.&lt;br /&gt;✨ &lt;strong&gt;Latest JavaScript and CSS support&lt;/strong&gt; Enjoy the most up-to-date syntax features and improved styling support.&lt;br /&gt;📚 &lt;strong&gt;Better language detection&lt;/strong&gt; Alchemize now does a smarter job at figuring out what kind of code you’re working with.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;What people said about v2&lt;/h2&gt;
&lt;p&gt;Before Google killed off Chrome apps, the app had a loyal following on the Chrome Web Store where it got a bunch of great reviews which really made my day:&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“Great for checking, untangling, prettifying, and minifying JSON and other formats.”&lt;/p&gt;
&lt;cite&gt;Richard H&lt;/cite&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“Very helpful, small and effective, without the bloat of a full editor — with great error-catching capability.”&lt;/p&gt;
&lt;cite&gt;&lt;em&gt;Stefan C&lt;/em&gt;&lt;/cite&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“I’m using this more and more now. Perfect way to make my code look nice before sharing it.”&lt;/p&gt;
&lt;cite&gt;&lt;em&gt;Andrew L&lt;/em&gt;&lt;/cite&gt;&lt;/blockquote&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;🚀 Try Alchemize v3 today&lt;/h2&gt;
&lt;p&gt;The new version is live — no installation required unless you want to install it as a desktop app. Just open the app, paste your code, and experience the magic:&lt;/p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https://alchemizeapp.com/app/&quot;&gt;&lt;strong&gt;Launch Alchemize&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And if you spot anything missing or have ideas for improvement, do &lt;a href=&quot;https://ashk.au/contact&quot;&gt;get in touch&lt;/a&gt; or &lt;a href=&quot;https://github.com/ashkyd/alchemize&quot;&gt;fork the project on GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>What I’m doing for bookmarks now that Pocket is dead</title>
    <link href="https://ashk.au/2025/10/23/what-im-doing-for-bookmarks-now-that-pocket-is-dead/"/>
    <updated>2025-10-23T10:47:27Z</updated>
    <id>https://ashk.au/2025/10/23/what-im-doing-for-bookmarks-now-that-pocket-is-dead/</id>
    <summary>Pocket was a pretty great little app. It let you save articles, read them later, tag them etc. I used it because it was integrated with my Kobo ebook reader, so I could save stories and read them on that. But then Mozilla unceremoniously killed it. So now what?</summary><category term="javascript" /><category term="mastodon" /><category term="Blog" /><content type="html">&lt;p&gt;Pocket was a pretty great little app. It let you save articles, read them later, tag them etc. I used it because it was integrated with my Kobo ebook reader, so I could save stories and read them on that. But then Mozilla unceremoniously killed it.&lt;/p&gt;
&lt;p&gt;I wouldn’t have minded so much except that I’m using the API to power the &lt;a href=&quot;https://ashk.au/bookmarks/&quot;&gt;bookmarks&lt;/a&gt; section of my website. I was using it to share articles I find particularly interesting, and there’s an RSS feed for the particularly adventurous. But that’s been broken for the last little while.&lt;/p&gt;
&lt;p&gt;I migrated over to &lt;a href=&quot;https://instapaper.com/&quot;&gt;Instapaper&lt;/a&gt; since that seemed to be the reliable alternative, but their API has a manual human approval process, and I’m neither patient nor confident enough that will succeed.&lt;/p&gt;
&lt;p&gt;So I had a new idea: what if I just post to Mastodon with a specific hashtag, then use the Mastodon API to collate them all together. That way get the benefit of sharing links on socials, but it’s also much easier than having another third party app.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;Long story short I gave &lt;a href=&quot;https://kilocode.ai/&quot;&gt;Kilo Code&lt;/a&gt; some specs and it built an implementation that searches the Mastodon API for posts matching any post from me with the hashtag &lt;code&gt;#link&lt;/code&gt; (just searches for “&lt;code&gt;#link from:@ash&lt;/code&gt;“), then it grabs the metadata and hashtags from the API, and merges those into my existing bookmarks.json file that runs the whole thing.&lt;/p&gt;
&lt;p&gt;I’m not going to post code because you can probably write something more to your liking. But I thought the concept was cool, and now I’m thinking about other possibilities, like having a music recommendations playlist that pulls from my posts in the &lt;code&gt;#NowListening&lt;/code&gt; hashtag.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;I saw this post on BlueSky the other day and it tickled me slightly. And I was reminded of it because in a way, that’s exactly what I’m doing now. Using loosely marked up socials as the backend.&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;BlueSky and YouTube are down at the same time. This is because, to save money on storage costs, BlueSky just saves all our posts as comments on one kid’s middle school science fair presentation in 2011 (he lost)&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://dfeldman.org/&quot;&gt;Daniel Feldman&lt;/a&gt; @ &lt;a href=&quot;https://bsky.app/profile/dfeldman.org/post/3m3bi6q4ka22x&quot;&gt;bsky&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;
</content>
  </entry>
  
  <entry>
    <title>Announcing Deep Time – an epic story more than 65,000 years in the making</title>
    <link href="https://ashk.au/2025/10/21/deep-time/"/>
    <updated>2025-10-21T12:29:34Z</updated>
    <id>https://ashk.au/2025/10/21/deep-time/</id>
    <summary>On Sunday we launched the project we’ve been working on for the last little while, Deep Time. It’s an immersive story of the knowledge, art and ingenuity of Australia’s First Nations peoples — told like never before.</summary><category term="abc" /><category term="devlog" /><category term="self" /><category term="Announcing" /><content type="html">&lt;p&gt;On Sunday we launched the project we’ve been working on for the last little while, &lt;a href=&quot;https://www.abc.net.au/news/deeptime/&quot;&gt;Deep Time&lt;/a&gt;. It’s an immersive story of the knowledge, art and ingenuity of Australia’s First Nations peoples — told like never before.&lt;/p&gt;
&lt;p&gt;It brings together knowledge from dozens of knowledge holders in Aboriginal and Torres Strait Islander communities, incredible art, design, and if I say so myself, some pretty slick dev work. It was very, very much a team effort and I feel privileged to work with some of the most creative and accomplished colleagues across the journo, design, and dev spectrum. Check out the &lt;a href=&quot;https://www.abc.net.au/news/deeptime/about/&quot;&gt;About page&lt;/a&gt; for a full list of credits.&lt;/p&gt;
&lt;p&gt;For some technical details it’s implemented with Sveltekit and adaptor-static. Aside from the homepage most of the animation is hand-coded, including the scroll-tied transitions. &lt;a href=&quot;https://www.abc.net.au/news/deeptime/about/&quot;&gt;Josh&lt;/a&gt; did some amazing work manipulating SVGs on the fly in &lt;a href=&quot;https://www.abc.net.au/news/deeptime/tell-me-a-story/&quot;&gt;Tell Me A Story&lt;/a&gt;, and I had fun implementing a pseudorandom starfield in &lt;a href=&quot;https://www.abc.net.au/news/deeptime/time/&quot;&gt;Time&lt;/a&gt; that fades in and out as you read the story.&lt;/p&gt;
&lt;p&gt;I feel like there’s a lot of interesting dev work I could talk about, but for the moment I just wanted to share it with you because it’s been a massive effort and I’m really proud of what we achieved. I hope you check it out:&lt;/p&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;a href=&quot;https://www.abc.net.au/news/deeptime/&quot;&gt;&lt;picture&gt;
&lt;source srcset=&quot;https://ashk.au/wp-content/posts/OG-Default.Cm_O8EME-2-1024x538.webp 9999w&quot; /&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;538&quot; src=&quot;https://ashk.au/wp-content/posts/OG-Default.Cm_O8EME-2-1024x538.webp&quot; alt=&quot;Deep Time&quot; class=&quot;wp-image-1418&quot; srcset=&quot;https://press.ash.ms/wp-content/uploads/2025/10/OG-Default.Cm_O8EME-2-1024x538.webp 1024w, https://press.ash.ms/wp-content/uploads/2025/10/OG-Default.Cm_O8EME-2-300x158.webp 300w, https://press.ash.ms/wp-content/uploads/2025/10/OG-Default.Cm_O8EME-2-768x403.webp 768w, https://press.ash.ms/wp-content/uploads/2025/10/OG-Default.Cm_O8EME-2.webp 1200w&quot; sizes=&quot;auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px&quot; /&gt;
&lt;/picture&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Another construction site</title>
    <link href="https://ashk.au/2025/09/09/another-construction-site/"/>
    <updated>2025-09-08T21:15:09Z</updated>
    <id>https://ashk.au/2025/09/09/another-construction-site/</id>
    <summary>I was sure everything would work. I had a little bit of mastodon maintenance to do out of hours, so I stayed up, kicked it off at midnight, and went to bed at half past twelve. I set my alarm for 0830 and this would have been fine but for the machinery on the street …  Continue reading “Another construction site”</summary><category term="brisbane" /><category term="Blog" /><content type="html">&lt;p&gt;I was sure everything would work. I had a little bit of &lt;a href=&quot;https://bne.social/&quot;&gt;mastodon&lt;/a&gt; maintenance to do out of hours, so I stayed up, kicked it off at midnight, and went to bed at half past twelve.&lt;/p&gt;
&lt;p&gt;I set my alarm for 0830 and this would have been fine but for the machinery on the street that started making noise at 0545. Sometimes the garbage trucks rock up at this time, so I stayed in bed with my eyes closed trying to get back to sleep, but it kept going; unending bass thrumming punctuated with reversing beeps.&lt;/p&gt;
&lt;p&gt;Turns out it’s a new construction site breaking ground. They were moving a medium sized diesel crane into position, letting it idle, and occasionally rev up to do who knows what. People were going up and down on a scissor lift, beeping all the way. And you’d better believe all the noise came straight into my bedroom.&lt;/p&gt;
&lt;p&gt;Bleary eyed and feeling like a sack of shit, I called the Brisbane Council noise hotline to complain. The helpful lady confirmed “that’s not classed as construction noise,” so there’s no noise complaint to make. She suggested I keep a noise diary.&lt;/p&gt;
&lt;p&gt;I’m so tired.&lt;/p&gt;
&lt;p&gt;I rearranged my life to &lt;a href=&quot;https://ashk.au/2025/04/15/getting-the-worm-as-the-early-bird/&quot;&gt;wake up at 0600 for a while there&lt;/a&gt;, but I fell out of the habit for a few reasons. But if this is what Brisbane Council counts as reasonable, I think I need to start again because I have little other agency over my life. This city.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Pain II – the nerve root injection</title>
    <link href="https://ashk.au/2025/08/06/pain-ii-the-nerve-root-injection/"/>
    <updated>2025-08-05T16:38:12Z</updated>
    <id>https://ashk.au/2025/08/06/pain-ii-the-nerve-root-injection/</id>
    <summary>It took maybe six 6 or 7 trips into the CT before he was satisfied. He injected a contrast dye and sent me back in one last time, apparently to highlight the nerves and make sure the needle wasn’t going to smash them. Then the scary part began.</summary><category term="life" /><category term="pain" /><category term="Blog" /><content type="html">&lt;p&gt;As far as treatments went, this one scared me shitless. The doctor prescribed a CT guided nerve root injection. Essentially they stick a needle in your spine using a CT machine to make sure it doesn’t accidentally go into the nerve or anywhere else nasty, then inject a combo of anaesthetic and steroids to calm everything the fuck down.&lt;/p&gt;
&lt;p&gt;Rational brain understands this is a normal procedure, and people get a lot of benefit out of it. It’s not a silver bullet but it’s a tool to help things heal, in conjunction with physiotherapy and not being a big fucking idiot for the next little while. But anxious brain started humming along, filling itself with a background radiation of bad thoughts. The x-ray place gave me next to no information about the procedure straight up, so all I really had was secondhand info.&lt;/p&gt;
&lt;p&gt;The one thing they were adamant about was making sure I had someone drop me off and pick me up. You can’t drive after the procedure, because the anaesthetic can put your leg(s) to sleep. So big thanks to A&lt;sup&gt;d&lt;/sup&gt; for taking time off work to rescue me here.&lt;/p&gt;
&lt;p&gt;They told me they wouldn’t do the procedure unless they had someone drop me off, But when I arrived they gave zero shits and waved me through to the sitting area.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;The nurse who called my name was a little weird. But to be fair, I was a little weird at that point too, pretty freaked out. She guided me down a stark white corridor dotted with doors to unknowable places, before plopping me in a chair and handing me a bunch of consent forms with all the information I wish I’d had earlier. Then she disappeared down the corridor through one of the doors. I didn’t see where she went.&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p class=&quot;long&quot;&gt;Your Doctor has requested that you undergo a Nerve Root Sleeve (NRSI) and/or Epidural Injection (El). A Queensland X-Ray radiologist will perform this procedure.&lt;/p&gt;
&lt;p&gt;NRSI is the introduction of local anaesthetic and/or steroid into the sheath around the nerve. This is usually requested because the nerve is thought to be inflamed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I took a moment to scan the forms before signing them. As I was reading, another patient was discharged. An older lady. The technician pointed down the corridor to the exit and sent her on her way. But as she shuffled her way toward freedom the technician burst back through the door, calling out and waving a disposable coffee cup.&lt;/p&gt;
&lt;p&gt;The lady turned around in confusion, then put her hands over her mouth as a look of horror crossed her face.&lt;/p&gt;
&lt;p&gt;This can’t be coffee, I surmised as the nurse led the woman into a bathroom for some privacy. The cup contained, presumably, a full set of false teeth left behind accidentally. It’s quite common, I’m sure.&lt;/p&gt;
&lt;p&gt;I signed the documents and stood around awkwardly until my nurse returned.&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p class=&quot;long&quot;&gt;This examination is best performed under CT guidance and may take up to 30 minutes to perform. You will be lying on your stomach for the duration of the procedure. A scan is taken to localise the area to be injected. The skin on your back/ back of your neck will be cleaned with an antiseptic solution and local anaesthetic will be injected into the area. This may sting slightly for a short time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;She led me through one of the mystery doors. My prize: a bed covered in pillows, hanging out of a machine that looked like a giant robot doughnut. I was pretty impressed the first time I did a CT scan, I didn’t realise how they worked until I looked it up. It’s essentially an x-ray machine that spins around, building up a 3D image of whatever irradiated subject happens to be in its path. Similar principle to an MRI I guess but different tech.&lt;/p&gt;
&lt;p&gt;She asked me to lay on the bed in a Superman pose, which confused the hell out of me and took a minute to work out what she meant. But then I carefully deposited myself face down on the bed and rearranged some pillows for comfort. Later I’d wish I’d rearranged them more. But that was the least of my problems.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;With consent the nurse lifted my shirt and pulled down my shorts, then put some kind of measuring tool on my back. Then the fun started. The bed crawled slowly into the machine, going back and forth a bit to adjust itself, then a robot told me not to move and to stop breathing now. &lt;/p&gt;
&lt;p&gt;Gasp.&lt;/p&gt;
&lt;p&gt;It wasn’t until that point that I realised how shallow I’d have been breathing already. The thought of someone digging around in my spinal cord while I was taking big gasps and moving around wasn’t super appealing, and the pillows were constricting my ability to breathe as well. So when I was asked to hold my breath, the fire in my lungs added to all the other anxieties racing through my head and once I was finally allowed to take a breath I was almost hyperventilating. I had to really focus on breathing shallow, but enough to be comfortable.&lt;/p&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p class=&quot;long&quot;&gt;A needle is then directed into the area under imaging guidance. The position of the needle-tip is checked with imaging and x-ray dye (contrast) may be injected. The local anaesthetic and/or steroid are then injected. There may be an increase in the leg pain when the injection commences. This pain usually eases as the anaesthetic spreads around the nerve root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After that was all lined up, the doctor came in and introduced himself to me. I was laying face down on a bed, and couldn’t exactly move so he crouched down next to me like I was a toddler to ask me if I’d ever had this procedure before.&lt;/p&gt;
&lt;p&gt;“Nevarrr” I said, immediately realising how weird a response that was. My brain was racing and I was afraid to move even though nothing had happened yet. I wasn’t the best conversationalist.&lt;/p&gt;
&lt;p&gt;But the doctor was good. He explained what was about to happen, much the same as the consent form, before getting to work.&lt;/p&gt;
&lt;p&gt;Actually he was very good. I barely felt the anaesthetic, which from past experience can be pretty unpleasant. I mentioned that to him and he said he did it really slowly, so thanks for that. But the next section was the one I was especially not looking forward to.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;I saw the nurse rifling around on the table for the needle. I saw how long the packaging was, but at that point I didn’t see how long the needle itself was. A small blessing, because when they removed it afterwards it looked like something you might use on a hippo or some other large animal you didn’t particularly like. Definitely not what I’d want going into my own body.&lt;/p&gt;
&lt;p&gt;But it did. The doctor had several goes guiding the needle in, then sending me into the CT machine to check the positioning. As it got closer I started to feel something not unlike pain. I mentioned it and he told me the anaesthetic may not reach quite so far in. Thankfully it wasn’t pain as much as a weird pain-adjacent sensation, I continued doing the best job I could laying completely still and not moving.&lt;/p&gt;
&lt;p&gt;It took maybe six 6 or 7 trips into the CT before he was satisfied. He injected a contrast dye and sent me back in one last time, apparently to highlight the nerves and make sure the needle wasn’t going to smash into them. Then the scary part began.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Risks associated with the procedure may include:&lt;/p&gt;
&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Pain and/or bruising at the needle puncture site.&lt;/li&gt;
&lt;li&gt;Bleeding in the deeper tissues.&lt;/li&gt;
&lt;li&gt;Infection involving redness, swelling, or increased pain over the injection site. Fever or chills need to be directly reported to QXR Radiology and your referring doctor.&lt;/li&gt;
&lt;li&gt;Headache can occur in less than 1% of uncomplicated Epidural Injections.&lt;/li&gt;
&lt;li class=&quot;long&quot;&gt;Occasionally, the sac containing the spinal nerve roots may be punctured during the needle placement. If the sac is punctured there is a potential for fluid to slowly leak out over time giving you a headache. The radiologist performing the procedure will alert you if this has occurred. This is not a serious situation. You will be asked to lie flat for 4 hours and to drink plenty of fluid. This helps prevent any headache. The risk of headache after sac puncture is about 5%.&lt;/li&gt;
&lt;li class=&quot;long&quot;&gt;Direct injection into the nerve root produces intense pain and may damage the nerve resulting in loss of sensation and weakness of the muscle supplied by the affected nerve.&lt;/li&gt;
&lt;li&gt;Very rare complications (&amp;lt;1:100 000) include direct damage to the spinal cord or injury to a blood vessel causing weakness in limbs or other spinal symptoms such as problems with bladder and bowel function.&lt;/li&gt;
&lt;li&gt;Risks associated with injectable drugs used including allergic reaction or toxicity. Very severe reaction is rare and a life threatening reaction is extremely rare (&amp;lt; 1: 100,000).&lt;/li&gt;
&lt;li&gt;Adverse effects from injected steroids&lt;/li&gt;
&lt;li class=&quot;long&quot;&gt;On occasion the cortisone (steroid) used in this injection can initially cause a reaction known as a “steroid flare”, which may cause more pain in the area of injection.&lt;/li&gt;
&lt;li&gt;Backache or sciatica due to the muscle being aggravated by the insertion of the needle or injection. This is usually mild and temporary.&lt;/li&gt;
&lt;li&gt;Injury to the spinal cord.&lt;/li&gt;
&lt;li&gt;Any procedure has the potential to be associated with unpredictable risks including death.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;The doctor reminded me that there may be a flare of pain as the steroid went in. I’ve been dealing with this for weeks now, so I was pretty relaxed about that. In the end I felt a bit of tightness, some pain adjacency, and then it was done. He ripped the needle out and that was that.&lt;/p&gt;
&lt;p&gt;After it was complete it felt weird. I felt weird. I continued laying face down not wanting to move; less of a Superman, more of a scare goat. Based on feeling, I wasn’t 100% sure everything had been removed so I didn’t want to jeopardise my precious spine until the nurse confirmed I could sit up.&lt;/p&gt;
&lt;p&gt;I had a bit of trouble getting up, that’s one of the things that has been hurting this whole time. I wasn’t sure how much the injection would be masking, I wasn’t sure if putting pressure on my spine was a good idea at this point. But the professionals assured me it was ok, so I pushed myself into a sitting position with a small but diminished amount of pain, then stood up.&lt;/p&gt;
&lt;p&gt;Then I sat straight back down again. I was light-headed and felt like I was going to tumble over. The nurse said the colour had drained out of my face, and asked the technician to bring a glass of water.&lt;/p&gt;
&lt;p&gt;I thanked her and said, “hopefully this one’s not full of teeth.”&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;It was a weird experience. I realised the background sciatic pain I’d been dealing with was completely gone. I hadn’t noticed how bad it was until it wasn’t there anymore, so that was a success. There was still a bit of back pain, but that that’s not what the treatment was for so I wasn’t too fussed. But I could still feel a tugging in my back as I walked, so that bit of tightness was still there even though the anaesthetic had calmed the nerves.&lt;/p&gt;
&lt;p&gt;The whole procedure from walking in the door to walking out again was only 45 minutes, and once again they gave zero shits about my driver. I should have just taken an Uber.&lt;/p&gt;
&lt;p&gt;On the way out I messaged B:&lt;/p&gt;
&lt;style type=&quot;text/css&quot;&gt;
/*
Copyright (c) 2023 by Samuel Kraft (https://codepen.io/samuelkraft/pen/kYrjwX)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
.bubbles {
  --us-bg: #65c467;
  --us-color: white;
  --them-bg: #e5e5ea;
  --them-text: black;
  font-family: &quot;Helvetica Neue&quot;, Helvetica, sans-serif;
  font-size: 20px;
  font-weight: normal;
  max-width: 450px;
  margin: 50px auto;
  display: flex;
  flex-direction: column;
  background-color: var(--background, white);
}

.bubbles p {
  max-width: 255px;
  word-wrap: break-word;
  margin-bottom: 12px;
  line-height: 24px;
  position: relative;
  padding: 10px 20px;
  border-radius: 25px;
}
.bubbles p:before,
.bubbles p:after {
  content: &quot;&quot;;
  position: absolute;
  bottom: 0;
  height: 25px;
}

.us {
  color: var(--us-color);
  background: var(--us-bg);
  align-self: flex-end;
}
.us a{
  color:var(--us-color);
  border:none;
}
.us:before {
  right: -7px;
  width: 20px;
  background-color: var(--us-bg);
  border-bottom-left-radius: 16px 14px;
}

.us:after {
  right: -26px;
  width: 26px;
  background-color: var(--background,white);
  border-bottom-left-radius: 10px;
}

.them {
  background: var(--them-bg);
  color: black;
  align-self: flex-start;
}
.them a{
  color:var(--them-color);
  border:none;
}
.them:before {
  left: -7px;
  width: 20px;
  background-color: var(--them-bg);
  border-bottom-right-radius: 16px 14px;
}

.them:after {
  left: -26px;
  width: 26px;
  background-color: var(--background,white);
  border-bottom-right-radius: 10px;
}

&lt;/style&gt;
&lt;aside class=&quot;bubbles&quot;&gt;
&lt;p class=&quot;us&quot;&gt;Ufhhhhh I fucken hated that&lt;/p&gt;
&lt;p class=&quot;us&quot;&gt;But it’s done&lt;/p&gt;
&lt;p class=&quot;us&quot;&gt;And no real pain&lt;/p&gt;
&lt;p class=&quot;us&quot;&gt;I’m gonna go home and probably have a good cry at this rate and then do lots of laying down I guess&lt;/p&gt;
&lt;/aside&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;The anaesthetic started to wear off a bit past midnight. But the steroid definitely didn’t: when I tried to sleep I felt my heart rate going crazy. I checked my watch and it had slowly ticked up all afternoon since the injection, to somewhere between 100 and 110 beats per minute just laying in bed. A resting heart rate of 90. Hectic.&lt;/p&gt;
&lt;p&gt;It’s a bit weird but apparently insomnia can be a side effect too. Laying in bed at 2am writing blog posts, I definitely felt like I’d been smashing coffee all day. Whenever I closed my eyes there was another thought or impulse or distraction to keep me from sleeping and it was very frustrating, especially since I was planning to be woken in a few hours by the construction site and my upstairs neighbour’s asshole dog.&lt;/p&gt;
&lt;p&gt;But the heart rate started heading back down a little, and the sciatica was under control mostly. I wouldn’t have even called it pain, just another distraction.&lt;/p&gt;
&lt;p&gt;But I’m feeling optimistic about things. The next few days will be taking it very easy, and I’ve already spoken to the physio about what recovery looks like. I don’t have a program yet, but I think on the weekend I’m going to go do some bench press. It’s allowed. I wouldn’t if it weren’t. And I’m very motivated not to let this happen again.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Pain.</title>
    <link href="https://ashk.au/2025/08/01/pain/"/>
    <updated>2025-07-31T18:05:02Z</updated>
    <id>https://ashk.au/2025/08/01/pain/</id>
    <summary>Shirley Manson found God in tramadol, but all I got was this lousy pain in the butt. I’ve always had issues with my leg, whenever I’ve done weight training or intense cycling. If I don’t do enough preventative stretching, or sometimes even if I do, the muscles in my butt tighten up and start causing …  Continue reading “Pain.”</summary><category term="life" /><category term="pain" /><category term="Blog" /><content type="html">&lt;p&gt;Shirley Manson found God in tramadol, but all I got was this lousy pain in the butt.&lt;/p&gt;
&lt;p&gt;I’ve always had issues with my leg, whenever I’ve done weight training or intense cycling. If I don’t do enough preventative stretching, or sometimes even if I do, the muscles in my butt tighten up and start causing leg pain, lower back pain, sometimes a little sciatica. I thought that’s what was happening this time, but instead of getting better it just kept getting worse.&lt;/p&gt;
&lt;p&gt;It started fairly innocuously, a bit of tightness in the leg. Soreness when I tried to stretch it out, mostly manifesting as lower back pain. After a couple of weeks of not getting better I went to see the physio for some massage and a program of stretches. It helped. Movement helps. Once I’ve warmed up, I’m doing great. But every night things would reset. &lt;/p&gt;
&lt;p&gt;There was no one thing that caused it, but driving to my parents place didn’t help. My car isn’t uncomfortable, but sitting in the driving position for 4 hours at a time still really aggravates things. I kept up my physio program while I was up there, but the drive back just made things worse again.&lt;/p&gt;
&lt;p&gt;Then on Sunday I thought I’d take a little bike ride. Nothing intense, just to get some movement, you know? The thing that helps?&lt;/p&gt;
&lt;p&gt;Well it didn’t. &lt;/p&gt;
&lt;p&gt;That night was agony. I’ve never had pain quite like it. Neural pain. The kind that you can’t touch; there’s no particular spot it hurts, it’s just everywhere and nowhere all at once. Waves of fire without the heat, giving way to a hellish TV static of misfiring nerves where the best you can do is tune to the least offensive channel. Settling for a pervasive ache that never quite goes away.&lt;/p&gt;
&lt;p&gt;On the first few nights the only relief I could get was standing up. Ironically, sitting or laying would make everything arc up, but to get some relief I would wander around upright in my darkened living room until the tiredness got the better of me. As you can imagine, it’s not a good position to sleep in, so I didn’t get any. &lt;/p&gt;
&lt;p&gt;I’d been self-medicating with ibuprofen since I figured an anti-inflammatory would help, but it didn’t really. So on Tuesday I went to the doctor and he prescribed tramadol, a scary sounding opiate that turned out to not do very much of anything at all.&lt;/p&gt;
&lt;p&gt;I’ve been to the physio a few times now and the doctor largely agreed with his diagnosis of piriformis syndrome: a condition where the piriformis muscle in the butt gets angry and pinches the sciatic nerve. But since it can also be caused by more scary spinal issues he sent me for a CT scan to rule that out. It’s almost 4:00 a.m. now so that means my follow-up appointment in about 5 hours, and I’m looking forward to the possibility of a steroid injection to chill things right the fuck out. I hear good things.&lt;/p&gt;
&lt;p&gt;In the meantime I’m laying here on the couch with my heat pad turned up to 5. I thought I was improving because yesterday was fantastic, but then I woke up to that pervasive jangly fire-not-fire down my leg. And it’s not that the drugs don’t work. It’s just that they transmogrify the pain instead of entirely killing it. Like the upgrade from analogue to digital; the signal is still messed up but at least now it’s in high definition.&lt;/p&gt;
&lt;hr class=&quot;wp-block-separator has-alpha-channel-opacity&quot; /&gt;
&lt;p&gt;I walked into the doctor’s office and saw he was carrying a little model of the spine, and I was like, ‘fuck’. So it’s not best case. But it’s not the worst, so whatever.&lt;/p&gt;
&lt;p&gt;In the next post, join me for a &lt;a href=&quot;https://ashk.au/2025/08/06/pain-ii-the-nerve-root-injection/&quot;&gt;nerve root injection&lt;/a&gt; (but I won’t blame you if you don’t).&lt;/p&gt;
</content>
  </entry>
</feed>