<?xml version="1.0" encoding="utf-8" standalone="no"?><rss version="2.0"><channel><title>as days pass by: a weblog by Stuart Langridge</title><link>https://www.kryogenix.org/days/</link><description>scratched tallies on the prison wall</description><lastBuildDate>Sat, 14 Mar 2026 23:23:00 +0000</lastBuildDate><item><title>Calculating a rolling average without keeping all the numbers around</title><link>https://www.kryogenix.org/days/2026/03/14/calculating-a-rolling-average-without-keeping-all-the-numbers-around/</link><description>&lt;p&gt;OK, if you know anything about maths you will read this and shake your head in dismay because it's just &lt;em&gt;obvious&lt;/em&gt; and why did I even need to think about it? But... I was quite pleased to think this through, so I thought I'd write it down in case anybody else finds it useful.&lt;/p&gt;
&lt;p&gt;So. Your job is to calculate the average of a set of numbers. This is not hard: the average value&lt;sup id="sf-calculating-a-rolling-average-without-keeping-all-the-numbers-around-1-back"&gt;&lt;a href="#sf-calculating-a-rolling-average-without-keeping-all-the-numbers-around-1" class="simple-footnote" title="maths people, I mean mean, obvs"&gt;1&lt;/a&gt;&lt;/sup&gt; is the sum of the numbers divided by the count of the numbers. So the average of &lt;code&gt;2, 7, 8&lt;/code&gt; is&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;8&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;17&lt;/span&gt;
&lt;span class="o"&gt;-----&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.666&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You know that bit already. But now I say: ok, here's a new number to add to the end of the list: &lt;code&gt;12&lt;/code&gt;. What's the average now?&lt;/p&gt;
&lt;p&gt;One obvious way is to say, well, we know the numbers are &lt;code&gt;2, 7, 8&lt;/code&gt;, so I'll just re-do the calculation with &lt;code&gt;2, 7, 8, 12&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;7&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;8&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;12&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;29&lt;/span&gt;
&lt;span class="o"&gt;--------&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.25&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But that means that you have to keep all the numbers around forever. What if the list of numbers is big? That seems like hassle. It would be nice if we were able to calculate the average just from knowing what the &lt;em&gt;previous&lt;/em&gt; average was, wouldn't it? So you know &lt;code&gt;5.666…&lt;/code&gt; and you get told that the new number to add is 12: how do you get the new average of &lt;code&gt;7.25&lt;/code&gt; just from that?&lt;/p&gt;
&lt;p&gt;I needed to do this and didn't know how, so I sat and thought about it for a short time and it's actually not hard, which was good; I'd just never considered it. So, as long as you know the "current" average number, &lt;em&gt;and&lt;/em&gt; you know how many numbers there were in the list, you're all good. Consider: we know the current average is &lt;code&gt;5.666…&lt;/code&gt; and that there were &lt;code&gt;3&lt;/code&gt; numbers in the list that made that up. We don't need to know what the 3 numbers were (&lt;code&gt;2, 7, 8&lt;/code&gt;) because if the three numbers we'd been given before were &lt;em&gt;all&lt;/em&gt; &lt;code&gt;5.666…&lt;/code&gt; -- that is, the list of numbers up to now was &lt;code&gt;5.666…, 5.666…, 5.666…&lt;/code&gt; then when we took the average of that list, it would still be &lt;code&gt;5.666…&lt;/code&gt;, because that's what an average means. So, even though we don't know what the 3 numbers were, we can pretend that we do, and now say that our new list is &lt;code&gt;5.666…, 5.666…, 5.666…, 12&lt;/code&gt;. And we can work out the average of that! Or, put another way, we know that the average is&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;         sum of all numbers
5.666… = ------------------
          count of numbers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and because we can pretend that all the numbers were the average, we can say that the sum of all numbers is just the current average multiplied by the count:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;         5.666…, 5.666…, "count" times
5.666… = -----------------------------
                   count
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and so the &lt;em&gt;new&lt;/em&gt; average is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;              (5.666…, 5.666…, "count" times) + 12
new average = -----------------------------
                       count + 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which is&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;              (current average × count) + new number
new average = --------------------------------------
                          count + 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So if you know the current average &lt;code&gt;a&lt;/code&gt;, how many numbers there have been so far &lt;code&gt;c&lt;/code&gt;, and the new number &lt;code&gt;n&lt;/code&gt;, then the new &lt;code&gt;a = ((a*c)+n)/(c+1)&lt;/code&gt;. And you only need to keep &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; around, rather than the whole list of numbers.&lt;/p&gt;
&lt;p&gt;Simple? Yes. But it was a useful thought for me that I could actually do this without having to keep the list around, which was handy in order to be able to &lt;a href="https://mastodon.social/@sil/116230077286633335"&gt;calculate pi very inefficiently with coin flips on a BBC Micro&lt;/a&gt;, a perfectly reasonable thing to do on your Saturday evening in my opinion.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-calculating-a-rolling-average-without-keeping-all-the-numbers-around-1"&gt;maths people, I mean mean, obvs &lt;a href="#sf-calculating-a-rolling-average-without-keeping-all-the-numbers-around-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Sat, 14 Mar 2026 23:23:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2026-03-14:/days/2026/03/14/calculating-a-rolling-average-without-keeping-all-the-numbers-around/</guid><category>General musings</category></item><item><title>Using 11ty's WebC to separate web components out into separate files</title><link>https://www.kryogenix.org/days/2026/03/02/using-11ty-s-webc-to-separate-web-components-out-into-separate-files/</link><description>&lt;p&gt;For a thing I'm building I'm using web components to give me a convenient
way to bundle up functionality of different bits of a web page into separate
units. I'm not doing anything particularly clever here with web components;
there's no shadow DOM here, the components aren't redistributable or
particularly reusable anywhere else, and aren't required. They're simply a
useful way to group "what happens when you click on this thing" into its own
little section of code. Anyway, what I had was one big page with the styles for
the components and the scripting for them all intertwined. &lt;/p&gt;
&lt;h2&gt;A simple example&lt;/h2&gt;
&lt;p&gt;Here's a simpler example. In this stripped down "page", you've got a 
&lt;code&gt;&amp;lt;light-bulb&amp;gt;&lt;/code&gt; component which either displays a little coloured
dot and "on", or a black dot and "off". There are two of them, a green one and
a yellow one. Then there's a &lt;code&gt;&amp;lt;light-bulb-switch&amp;gt;&lt;/code&gt; component
with three positions: green one on, both off, yellow one on. These aren't
intended to be particularly &lt;em&gt;good&lt;/em&gt; examples of web components (and you don't
need to read or understand&lt;sup id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-1-back"&gt;&lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-1" class="simple-footnote" title="or critique!"&gt;1&lt;/a&gt;&lt;/sup&gt; the code in any detail), but they
demonstrate the point.&lt;/p&gt;
&lt;iframe height="300" style="width: 100%;" scrolling="no" title="Untitled" src="https://codepen.io/stuartlangridge/embed/preview/gbwaLoj?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true"&gt;
  See the Pen &lt;a href="https://codepen.io/stuartlangridge/pen/gbwaLoj"&gt;
  Untitled&lt;/a&gt; by Stuart Langridge (&lt;a href="https://codepen.io/stuartlangridge"&gt;@stuartlangridge&lt;/a&gt;)
  on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;

&lt;p&gt;The HTML there is all one page; CSS and JS to define and style the components all
inline. What I wanted was the &lt;em&gt;output&lt;/em&gt; to be that, but to be able to split up
the components into their own separate files, for ease of editing -- it's hard to
keep track of where you are in a big long page. This isn't any sort of improvement
to the user experience -- the long page with everything inline was exactly what I
intend to serve to the user. This is purely a developer experience
improvement.&lt;sup id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-2-back"&gt;&lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-2" class="simple-footnote" title="Obviously there are many things that I could and will be doing to improve the user experience of this whole thing! But that's not what this particular writeup is about."&gt;2&lt;/a&gt;&lt;/sup&gt; I could have put the web component parts into
their own CSS and JS files, and then added &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and
&lt;code&gt;&amp;lt;link rel=stylesheet&amp;gt;&lt;/code&gt; lines for each, of course, and had the
browser load them… but I wanted to keep all the code for each component together
to make it easy for me to think about, but &lt;em&gt;not&lt;/em&gt; have it all in the main file
which was too confusing to navigate around in while editing it. Clearly there
ought to be something which can do the purely mechanical task of "break this
big file up into bits and then glue them back together in a build step of some
kind". Helper libraries for web components tend to think of themselves as doing
a lot of stuff to aid you in how you do that, either at build time or at run time;
stuff like &lt;a href="https://lit.dev/docs/"&gt;Lit&lt;/a&gt; is pretty neat but it does
six billion things I don't want, and the client-side half of this equation gives
you React and all its cousins and I didn't need or want any of that either. All
I needed was something to break a page apart into separate jigsaw pieces and
mechanically glue them back together. I could probably write this myself in about
twelve lines of python; it's not hard.&lt;/p&gt;
&lt;h2&gt;WebC&lt;/h2&gt;
&lt;p&gt;But then I was reminded by a search for &lt;code&gt;"distributing web components in a
single file"&lt;/code&gt; that &lt;a href="https://github.com/11ty/webc"&gt;WebC&lt;/a&gt; exists
as part of the 11ty project. And… it sorta looks like it does this? But it's not
all that clear; WebC isn't brilliantly documented. (It's fairly clear what the
API is, at a technical level, but there's not very much about the &lt;em&gt;vibe&lt;/em&gt;: what's
WebC &lt;em&gt;for&lt;/em&gt;, rather than how it does it.) I am a very big believer indeed in the
notion of using software in accordance with its vibe; it's possible (and sometimes
quite fun) to contort a bit of software into doing things that its authors don't
think that it's for, and would perhaps be horrified to see it used for, but that's
for hack value projects. When using a thing as a tool, use it the way it's meant
to be used. If you swim against the tide on this, it will at some point bite you
in the arse&lt;sup id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-3-back"&gt;&lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-3" class="simple-footnote" title="can tides bite arses? we are in mixed metaphor city. Sorry, Uncle George. Jackboots in melting pots all over the place here"&gt;3&lt;/a&gt;&lt;/sup&gt;
and then you'll be sad. So, sure, WebC looks like it could do this mechanical
reassemble-the-jigsaw task; is that what it's for? I needed support.&lt;/p&gt;
&lt;p&gt;So I got support for WebC in the most scalable and efficient way possible: chat
to Zach Leatherman in the pub directly and ask him, after State of the Browser 2026.
Isn't this how everyone gets support for everything, just talk directly to the
author over a pint?&lt;sup id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-4-back"&gt;&lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-4" class="simple-footnote" title="OK, tongue-in-cheek yes, but honestly this is quite a good way to find out about a piece of software! go to conferences and chat to the author. This isn't going to work if what you want help with is, like, Photoshop or something, but most people in the web community at least, and open source projects more generally, are in my experience not just willing but positively eager to talk to you about how they feel about a project they've built"&gt;4&lt;/a&gt;&lt;/sup&gt; And Zach
said: yup, that's what it's for. I promised to try it, and maybe write something
down about it so that future people might not be as confused as I was, and now
you're reading that very thing.&lt;/p&gt;
&lt;p&gt;(I'm not sure this is useful &lt;em&gt;documentation&lt;/em&gt;. But I'm basically incapable of
describing how to do anything without turning it into a short story. Insert that
Mark Twain(?) quotation about writing a shorter letter here.)&lt;/p&gt;
&lt;p&gt;So I took my page above and broke it up into separate files. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;shell.html&lt;/code&gt; is the "main page". The component names stay in place, but
all their inner code is removed, and so are their styles and scripting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Combined page&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;light-bulb data-color="orange"&amp;gt;
&amp;lt;/light-bulb&amp;gt;
&amp;lt;light-bulb data-color="green"&amp;gt;
&amp;lt;/light-bulb&amp;gt;
&amp;lt;light-bulb-switch&amp;gt;
&amp;lt;/light-bulb-switch&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then each of the components gets put into a separate file. This file
contains the HTML for the component, including a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and
&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element for its CSS and JS. Here's (a cut-down version
of) &lt;code&gt;light-bulb-switch.webc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;input type="range" min="0" 
            max="2" value="1" list="bulbs"&amp;gt;
    &amp;lt;datalist id="bulbs"&amp;gt;
        &amp;lt;option value="0"&amp;gt;orange&amp;lt;/option&amp;gt;
        &amp;lt;option value="1"&amp;gt;neither&amp;lt;/option&amp;gt;
        &amp;lt;option value="2"&amp;gt;green&amp;lt;/option&amp;gt;
    &amp;lt;/datalist&amp;gt;

&amp;lt;style&amp;gt;
light-bulb-switch {
    […css snipped…]
}
&amp;lt;/style&amp;gt;

&amp;lt;script&amp;gt;
window.customElements.define('light-bulb-switch', class extends HTMLElement {
    […js snipped…]
});

&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And there's a similar &lt;code&gt;light-bulb.webc&lt;/code&gt; as well. (You can find all
the code for this little demo, such as it is, at &lt;a href="https://github.com/stuartlangridge/webc-simple-demonstration"&gt;github&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Now this separation is done, we need WebC to glue it all back together. And
this is a matter of a short bit of scripting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { WebC } from "@11ty/webc";
import { writeFileSync } from "node:fs";

// create WebC object
let page = new WebC();

// tell it where our components are
page.defineComponents("*.webc");
// have it collect style and script 
// elements all together
page.setBundlerMode(true);
// this is the page to read and 
// populate with components
page.setInputPath("shell.html");

// actually do the work
let {
    html, css, js, components
} = await page.compile();

// haha who needs webpack? not me!
// just stick the js and css inline
const out_js = `&amp;lt;script&amp;gt;
${js.join("\n")}
&amp;lt;/script&amp;gt;`
const out_css = `&amp;lt;style&amp;gt;
${css.join("\n")}
&amp;lt;/style&amp;gt;`
const out = html
    .replace("&amp;lt;/body&amp;gt;", 
        out_js + "&amp;lt;/body&amp;gt;")
    .replace("&amp;lt;/head&amp;gt;", 
        out_css + "&amp;lt;/head&amp;gt;");

writeFileSync("combined.html", out);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;WebC reads in our &lt;code&gt;shell.html&lt;/code&gt; and all the component definitions in
their own files, and gives back HTML, CSS, and JS; the HTML is a long string,
which we can write to a file. The CSS and JS are both arrays of strings,
each one being the code in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;
element in a web component (and all together because we turned on 
&lt;code&gt;bundlerMode&lt;/code&gt;). Since we still need those styles and scripts in the
output page somewhere, we bodge them into place by dropping a new &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element into the page &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; and similarly for script and
body. It's certainly possible to be a lot cleverer than this (and if you wanted
to put the scripts in one separate file here and minify it on the way, this
is where you'd do it) but for our example here, I don't want any of that cleverness.&lt;/p&gt;
&lt;p&gt;And that's all. WebC does quite a lot more than this, but I don't need any of that at
the moment. If you do, check out the &lt;a href="https://github.com/11ty/webc"&gt;WebC docs&lt;/a&gt;.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-1"&gt;or critique! &lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-2"&gt;Obviously there are many things that I could and will be doing
to improve the user experience of this whole thing! But that's not what this
particular writeup is about. &lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-2-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-3"&gt;can tides bite arses? we are in mixed metaphor city. Sorry, &lt;a href="https://www.orwellfoundation.com/the-orwell-foundation/orwell/essays-and-other-works/politics-and-the-english-language/#:~:text=The%20Fascist%20octopus%20has%20sung%20its%20swan%20song"&gt;Uncle
George&lt;/a&gt;. Jackboots in melting pots all over the place here &lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-3-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-4"&gt;OK, tongue-in-cheek yes, but honestly this is quite a good
way to find out about a piece of software! go to conferences and chat to the author.
This isn't going to work if what you want help with is, like, &lt;em&gt;Photoshop&lt;/em&gt; or
something, but most people in the web community at least, and open source projects
more generally, are in my experience not just willing but positively eager to
talk to you about how they feel about a project they've built &lt;a href="#sf-using-11ty-s-webc-to-separate-web-components-out-into-separate-files-4-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Mon, 02 Mar 2026 05:22:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2026-03-02:/days/2026/03/02/using-11ty-s-webc-to-separate-web-components-out-into-separate-files/</guid><category>General musings</category></item><item><title>fifty</title><link>https://www.kryogenix.org/days/2026/01/29/fifty/</link><description>&lt;p&gt;fifty.&lt;/p&gt;
&lt;p&gt;that can't be right.&lt;sup id="sf-fifty-1-back"&gt;&lt;a href="#sf-fifty-1" class="simple-footnote" title="no big long writeup this time of musings on a year passing like I normally do. I think I did most of that earlier in the month."&gt;1&lt;/a&gt;&lt;/sup&gt; fifty years ago was the war.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-fifty-1"&gt;no big long writeup this time of musings on a year passing like I &lt;a href="https://www.kryogenix.org/days/2025/01/30/forty-nine/"&gt;normally&lt;/a&gt; do. I think I did most of that &lt;a href="https://www.kryogenix.org/days/2026/01/04/flipping-the-cube-2025-is-over/"&gt;earlier in the month&lt;/a&gt;. &lt;a href="#sf-fifty-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Thu, 29 Jan 2026 23:19:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2026-01-29:/days/2026/01/29/fifty/</guid><category>General musings</category></item><item><title>Flipping the cube: 2025 is over</title><link>https://www.kryogenix.org/days/2026/01/04/flipping-the-cube-2025-is-over/</link><description>&lt;p&gt;You know that optical illusion with a wireframe cube? Where you can't tell which way around it is, but if you concentrate a bit you can make the cube "flip" from being oriented one way to being oriented another?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kryogenix.org/images/necker-1.png" alt="a wireframe cube"&gt;&lt;/p&gt;
&lt;p&gt;So you look at that cube (it's called a &lt;a href="https://en.wikipedia.org/wiki/Necker_cube"&gt;Necker cube&lt;/a&gt; when you're viewing it as an optical illusion) and with a bit of concentration you can change how you perceive it, from pointing in one direction to pointing in another.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kryogenix.org/images/necker-2.png" alt="two wireframe cubes, one with one face filled in so that face appears at the front, and the other with another face filled in so that other face appears at the front instead, displaying the two perceiveable orientations"&gt;&lt;/p&gt;
&lt;p&gt;A bit like the dancer who might be spinning one way or the other, or the picture that's a duck or a rabbit. The key point here is: it's either one way, or it's the other. You can switch between ways, but it takes a bit of mental effort. And it can't be both at once.&lt;/p&gt;
&lt;p&gt;This is how I feel about 2025. Some stuff happened, some of it amazing, and I am glad the year is over.&lt;/p&gt;
&lt;p&gt;There are, I think, four major separate stuffs on that list. Obviously, what actually occupies your mind day to day is the more minor things; you got bolognaise sauce on your good shirt (did that) or your job changed a bit (did that) or you really need to lose some weight or you're nearly fifty or they're &lt;em&gt;still&lt;/em&gt; not &lt;a href="https://en.wikipedia.org/wiki/2025_Birmingham_bin_strike"&gt;collecting the bins&lt;/a&gt;. But I'll not bore you to tears with all of that; only my head should be pecked by this sort of thing, not everybody's.&lt;/p&gt;
&lt;p&gt;Last year, on my birthday, I &lt;a href="https://www.kryogenix.org/days/2025/01/30/forty-nine/"&gt;wrote&lt;/a&gt; "Maybe 2025 will be better, although to be honest I doubt it." Oh, sweet summer child. If you only knew.&lt;/p&gt;
&lt;p&gt;In March, my dad died.&lt;/p&gt;
&lt;p&gt;This, for those of you who do not know, sucks. A very large amount.&lt;/p&gt;
&lt;p&gt;It wasn't &lt;em&gt;unexpected&lt;/em&gt;. He'd been unwell for a while, and getting worse. And he knew what was happening, and wasn't suffering much, and was content. It could have been considerably worse on that front than it was. We might consider that sort of thing tactical unhappiness: short term, dominates your thinking right now, but you need to reach beyond it. The strategic unhappiness, that's what he could see ahead for himself, and he didn't want it, and I think he was right in that and I'm glad that's what he chose, and I'm glad that he chose it. We tried to express that at the &lt;a href="https://www.kryogenix.org/peter/"&gt;funeral&lt;/a&gt; and the wake with eulogies and readings and hugs and memories, and that's where I said everything that I wanted to say, and that went OK -- well, as OK as you can expect for your dad's funeral. Mardi Gras it was not. But the old man left a handprint on the world that made it better, and that's most of what you can hope for.&lt;/p&gt;
&lt;p&gt;It's hard with him gone, though. You play all these moody film moments in your head with yourself in the starring role, of monumental grief that takes people's breath away, of sombre melancholy where you're nonetheless handsomely well-dressed in a rainy black-and-white street scene and people whisper under their breath how brave and noble you are for handling it so well. You imagine how you'll be when the next one dies. What you should have done instead. Or, maybe &lt;em&gt;you&lt;/em&gt; don't do that at all, which is probably a better and more caring way to be. The things people warn you about -- how you'll think, oh that's cool I need to ask dad about that, and then remember that ah, no, I can't -- that goes away pretty fast. The needle changes direction from sadness to wistfulness, loss rather than bitterness; the thing's still there but the opacity of it drops gradually over time, even though it'll never fully go away. Not entirely.&lt;/p&gt;
&lt;p&gt;It's harder on my mum. They were married for over fifty years, for goodness's sake. I only managed ten; I can hardly imagine what it's like for something that's been there for half a century to suddenly be gone. How do you even imagine that? It's been a rough year for her, it really has. Christmas, the first Christmas, was hard. I'm used to living alone; she is not, quite apart from the emotional turmoil. And that's where all the minor things come into play; something tiny that happens each day and feels like so much more a burden. You're supposed to not let the bastards grind you down, but the problem is that a lot of the time there aren't any bastards. There's just... life. Life's hard to deal with, when you're dealing with things.&lt;/p&gt;
&lt;p&gt;So, was it all bad? No. There are some bright spots: some that are so great that they're almost blinding.&lt;/p&gt;
&lt;p&gt;My daughter is engaged, to a decent guy, and they're pregnant. Next June, I'll be a grandfather.&lt;/p&gt;
&lt;p&gt;This is cool.&lt;/p&gt;
&lt;p&gt;I think I'm the first, among my friends. This isn't that surprising; I was the first to get married, too, we got started early. But I'm pretty excited about the idea of being a grandad. I still have to work stuff out -- do I need to keep Werther's Originals in my pocket all the time? Buy a flat cap? -- but man, this is going to be great. I liked my grandparents, even the ones I don't really remember; if I can be as good as they were, as good as my dad was for my daughter, I'll be happy with that. And we can read books together! Go look at cool stuff! Make dinners and giggle a lot! Maybe not for the first couple of years, admittedly, but as I understand it when the more, er, brown-stained bits of parenthood come around I get to just hand my grandchild back. I served my time on that sort of thing. I just get the fun bits. Hooray!&lt;/p&gt;
&lt;p&gt;It did occur to me that there were times when my daughter was very young that we'd say, hey, we can drop her on the grandparents for a weekend and actually go to the pub again. And now I am the grandad being dropped upon... but I am OK with this. There are &lt;em&gt;so&lt;/em&gt; many books we're going to share together. Bring it on. I'm really looking forward to it.&lt;/p&gt;
&lt;p&gt;You see, it's not all bad. I have wonderful friends -- we almost all got together in what I am told is now called "Twixtmas" (and which I refuse to call "the merryneum", thank you Bruce), the bit in between Christmas and New Year, and I had such an excellent day. One of the highlights of my year, every year. &lt;a href="https://open-web-advocacy.org/en/"&gt;Open Web Advocacy&lt;/a&gt; has had quite some successes this year. I got a neat new cooking pot for Christmas which I'm really looking forward to trying out.&lt;/p&gt;
&lt;p&gt;But then there's the fourth thing: we've had death, engagement, grandfatherhood, and finally we have... the world. A world which is getting a little bit worse, every day. A little bit more mean, a little bit more cold, a little bit more gilded and segregated and exclusionary. My industry, my internet, my web, the thing I built (other people were also involved, I admit) is not what we all wanted it to be. Our place, our internet, is no longer ours; it belongs to the arseholes, to everyone who exchanges decency and joy for money and control and considers themselves superior and visionary for doing it. Plenty of people who actually are wise and eloquent have written recently about this, and how bad it is, and what can be done about it, or just how exhausting it is for it to happen: see &lt;a href="https://craphound.com/shop/"&gt;Cory&lt;/a&gt; or &lt;a href="https://henry.codes/writing/a-website-to-destroy-all-websites/"&gt;Henry&lt;/a&gt; or &lt;a href="https://ethanmarcotte.com/wrote/our-frail-thoughts/"&gt;Ethan&lt;/a&gt; or &lt;a href="https://whitep4nth3r.com/blog/this-is-not-a-2025-wrap-up-post/"&gt;Salma&lt;/a&gt; or, or, or. But the world is worse, every day, and it makes me dread what's coming.&lt;/p&gt;
&lt;p&gt;And so I confront the question: was 2025 a bad year with some good bits, or a good year with some bad bits?&lt;/p&gt;
&lt;p&gt;I think that it's up to me. Remember the Necker cube, from one hundred years ago when I started writing this? How you get to choose, with a little bit of effort, which way it points? I reckon that seems right. I could see 2025 as having been a crap year with a couple of bright spots that were drowned by the misery, and that is in fact what I've mostly been doing. But I don't think it helps, and I don't think it's healthy. The world is increasingly shit, and it's considerably more shit for people who aren't as lucky as me, and it's that way because of people who want it that way, and there there really are bastards and I could let &lt;em&gt;those&lt;/em&gt; bastards grind me down... but I don't wanna. I'm trying to make a conscious effort to flip the cube, to look forward to the good stuff that's coming and not look down at my feet to avoid the bad. I apologise in advance for all the days to come when I don't quite manage it.&lt;/p&gt;
&lt;p&gt;So, that was 2025. Goodbye, dad. Hello, your great-grandchild, whoever they turn out to be. I hope it's just better and I don't need to try to perceive it that way, but... if it isn't, I'll try anyway.&lt;/p&gt;
&lt;p&gt;Here's to 2026. Fingers crossed.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Sun, 04 Jan 2026 16:21:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2026-01-04:/days/2026/01/04/flipping-the-cube-2025-is-over/</guid><category>General musings</category></item><item><title>Making a Discord activity with PHP</title><link>https://www.kryogenix.org/days/2025/07/08/making-a-discord-activity-with-php/</link><description>&lt;p&gt;Another post in what is slowly becoming a series, after describing &lt;a href="https://www.kryogenix.org/days/2024/01/14/making-a-discord-bot-with-php/"&gt;how to make a Discord bot with PHP&lt;/a&gt;; today we're looking at how to make a Discord activity the same way.&lt;/p&gt;
&lt;p&gt;An activity is simpler than a bot; Discord activities are basically a web page which loads in an iframe, and can do what it likes in there. You're supposed to use them for games and the like, but I suspect that it might be useful to do quite a few bot-like tasks with activities instead; they take up more of your screen while you're using them, but it's much, much easier to create a user-friendly experience with an activity than it is with a bot. The user interface for bots tends to look a lot like the command line, which appeals to nerds, but having to type &lt;code&gt;!mybot -opt 1 -opt 2&lt;/code&gt; is incomprehensible gibberish to real people. Build a little web UI, you know it makes sense.&lt;/p&gt;
&lt;p&gt;Anyway, I have not yet actually published one of these activities, and I suspect that there is a whole bunch of complexity around that which I'm not going to get into yet. So this will get you up and running with a Discord activity that you can test, yourself. Making it available to others is step 2: keep an eye out for a post on that.&lt;/p&gt;
&lt;p&gt;There are lots of "frameworks" out there for building Discord activities, most of which are all about "use React!" and "have this complicated build environment!" and "deploy a node.js server!", when all you &lt;em&gt;actually&lt;/em&gt; need is an SPA web page&lt;sup id="sf-making-a-discord-activity-with-php-1-back"&gt;&lt;a href="#sf-making-a-discord-activity-with-php-1" class="simple-footnote" title="it's gotta be an SPA. Discord does not like it when the page navigates around"&gt;1&lt;/a&gt;&lt;/sup&gt;, a JS library, a small PHP file, and that's it. No build step required, no deploying a node.js server, just host it in any web space that does PHP (i.e., all of them). Keep it simple, folks. Much nicer.&lt;/p&gt;
&lt;h2&gt;Step 1: set up a Discord app&lt;/h2&gt;
&lt;p&gt;To have an activity, it's gotta be tied to a Discord app. Get one of these as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an application at &lt;a href="https://discord.com/developers/applications"&gt;discord.com/developers/applications&lt;/a&gt;. Call it whatever you want&lt;/li&gt;
&lt;li&gt;Copy the "Application ID" from "General Information" and make a &lt;code&gt;secrets.php&lt;/code&gt; file; add the application ID as &lt;code&gt;$clientid = "whatever";&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In "OAuth2", "Reset Secret" under Client Secret and store it in &lt;code&gt;secrets.php&lt;/code&gt; as $clientsecret&lt;/li&gt;
&lt;li&gt;In "OAuth2", "Add Redirect": this URL doesn't get used but there has to be one, so fill it in as some URL you like (&lt;code&gt;http://127.0.0.1&lt;/code&gt; works fine)&lt;/li&gt;
&lt;li&gt;Get the URL of your activity web app (let's say it's &lt;code&gt;https://myserver/myapp/&lt;/code&gt;). Under URL Mappings, add &lt;code&gt;myserver/myapp&lt;/code&gt; (no &lt;code&gt;https://&lt;/code&gt;) as the Root Mapping. This tells Discord where your activity is&lt;/li&gt;
&lt;li&gt;Under Settings, tick Enable Activities. (Also tick "iOS" and "Android" if you want it to work in the phone app)&lt;/li&gt;
&lt;li&gt;Under Installation &amp;gt; Install Link, copy the Discord Provided Link. Open it in a browser. This will switch to the Discord desktop app. Add this app to the server of your choice (not to everywhere), and choose the server you want to add it to&lt;/li&gt;
&lt;li&gt;In the Discord desktop client, click the Activities button (it looks like a playstation controller, at the end of the message entry textbox). Your app should now be in "Apps in this Server". Choose it and say Launch. Confirm that you're happy to trust it because you're running it for the first time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And this will then launch your activity in a window in your Discord app. It won't do anything yet because you haven't written it, but it's now loading.&lt;/p&gt;
&lt;h2&gt;Step 2: write an activity&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You'll need the Discord Embedded SDK JS library. Go off to &lt;a href="https://www.jsdelivr.com/package/npm/@discord/embedded-app-sdk"&gt;jsdelivr&lt;/a&gt; and see the URL it wants you to use (at time of writing this is &lt;code&gt;https://cdn.jsdelivr.net/npm/@discord/embedded-app-sdk@2.0.0/+esm&lt;/code&gt; but check). Download this URL to get a JS file, which you should call discordsdk.js. (Note: do not link to this directly. Discord activities can't download external resources without some semi-complex setup. Just download the JS file)&lt;/li&gt;
&lt;li&gt;Now write the home page for your app -- index.php is likely to be ideal for this, because you need the client ID that you put in &lt;code&gt;secrets.php&lt;/code&gt;. A very basic one, which works out who the user is, looks something like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="x"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;I am an activity! You are &amp;lt;output id="username"&amp;gt;...?&amp;lt;/output&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;scr ipt type="module"&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;import {DiscordSDK} from './discordsdk.js';&lt;/span&gt;
&lt;span class="x"&gt;const clientid = '&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$clientid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;';&lt;/span&gt;
&lt;span class="x"&gt;async function setup() {&lt;/span&gt;
&lt;span class="x"&gt;  const discordSdk = new DiscordSDK(clientid);&lt;/span&gt;
&lt;span class="x"&gt;  // Wait for READY payload from the discord client&lt;/span&gt;
&lt;span class="x"&gt;  await discordSdk.ready();&lt;/span&gt;
&lt;span class="x"&gt;  // Pop open the OAuth permission modal and request for access to scopes listed in scope array below&lt;/span&gt;
&lt;span class="x"&gt;  const {code} = await discordSdk.commands.authorize({&lt;/span&gt;
&lt;span class="x"&gt;    client_id: clientid,&lt;/span&gt;
&lt;span class="x"&gt;    response_type: 'code',&lt;/span&gt;
&lt;span class="x"&gt;    state: '',&lt;/span&gt;
&lt;span class="x"&gt;    prompt: 'none',&lt;/span&gt;
&lt;span class="x"&gt;    scope: ['identify'],&lt;/span&gt;
&lt;span class="x"&gt;  });&lt;/span&gt;
&lt;span class="x"&gt;  const response = await fetch('/.proxy/token.php?code=' + code);&lt;/span&gt;
&lt;span class="x"&gt;  const {access_token} = await response.json();&lt;/span&gt;
&lt;span class="x"&gt;  const auth = await discordSdk.commands.authenticate({access_token});&lt;/span&gt;

&lt;span class="x"&gt;  document.getElementById("username").textContent = auth.user.username;&lt;/span&gt;
&lt;span class="x"&gt;  /* other properties you may find useful:&lt;/span&gt;
&lt;span class="x"&gt;     server ID: discordSdk.guildId&lt;/span&gt;
&lt;span class="x"&gt;     user ID: auth.user.id&lt;/span&gt;
&lt;span class="x"&gt;     channel ID: discordSdk.channelId */&lt;/span&gt;
&lt;span class="x"&gt;}&lt;/span&gt;
&lt;span class="x"&gt;setup()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will see that in the middle of this, we call &lt;code&gt;token.php&lt;/code&gt; to get an access token from the &lt;code&gt;code&lt;/code&gt; that &lt;code&gt;discordSdk.commands.authorize&lt;/code&gt; gives you. While the URL is &lt;code&gt;/.proxy/token.php&lt;/code&gt;, that's just a &lt;code&gt;token.php&lt;/code&gt; file right next to &lt;code&gt;index.php&lt;/code&gt;; the &lt;code&gt;.proxy&lt;/code&gt; stuff is because Discord puts all your requests through their proxy, which is OK. So you need this file to exist. Following the &lt;a href="https://discord.com/developers/docs/activities/building-an-activity#step-5-authorizing-authenticating-users"&gt;Discord instructions for authenticating users with OAuth&lt;/a&gt;, it should look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"secrets.php"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$postdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;http_build_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;"client_id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$clientid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"client_secret"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$clientsecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"grant_type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"authorization_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"code"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'method'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'header'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'Content-Type: application/x-www-form-urlencoded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'User-Agent: mybot/1.00'&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$postdata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'ignore_errors'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$context&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;stream_context_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$result_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://discord.com/api/oauth2/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result_json&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"no response"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;array_key_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;error_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Got JSON response from /token without access_token &lt;/span&gt;&lt;span class="si"&gt;$result_json&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"no token"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$access_token&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And... that's all. At this point, if you Launch your activity from Discord, it should load, and should work out who the running user is (and which channel and server they're in) and that's pretty much all you need. Hopefully that's a relatively simple way to get started.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-making-a-discord-activity-with-php-1"&gt;it's gotta be an SPA. Discord does not like it when the page navigates around &lt;a href="#sf-making-a-discord-activity-with-php-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Tue, 08 Jul 2025 08:11:00 +0100</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-07-08:/days/2025/07/08/making-a-discord-activity-with-php/</guid><category>General musings</category></item><item><title>A (limited) defence of footnotes</title><link>https://www.kryogenix.org/days/2025/07/03/a-limited-defence-of-footnotes/</link><description>&lt;p&gt;So, Jake Archibald wrote that we should "&lt;a href="https://jakearchibald.com/2025/give-footnotes-the-boot/"&gt;give footnotes the boot&lt;/a&gt;", and... I do not wholly agree. So, here are some arguments against, or at least perpendicular to. Whether this is in grateful thanks of or cold-eyed revenge about him making me drink a limoncello and Red Bull last week can remain a mystery.&lt;/p&gt;
&lt;p&gt;Commentary about footnotes on the web tends to boil down into two categories: that they're foot, and that they're notes. Everybody&lt;sup id="sf-a-limited-defence-of-footnotes-1-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-1" class="simple-footnote" title="sensible"&gt;1&lt;/a&gt;&lt;/sup&gt; agrees that being foot is a problem. Having a meaningless little symbol in some text which you then have to scroll down to the end of a document to understand is stupid. But, and here's the point, &lt;em&gt;nobody does this&lt;/em&gt;. Unless a document on the web was straight up machine-converted from its prior life as a printed thing, any "footnotes" therein will have had some effort made to conceptually locate the content of the footnote inline with the text that it's footnoting. That might be a link which jumps you down to the bottom, or it might be placed at the side, or it might appear inline when clicked on, or it might appear in a popover, but the content of a "footnote" can be reached without your thread of attention being diverted from the point where you were previously&lt;sup id="sf-a-limited-defence-of-footnotes-2-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-2" class="simple-footnote" title="for good implementations, anyway; if you make your footnotes a link down to the end of the document, and then don't provide a link back via either the footnote marker or by adding it to the end, then you are a bad web author and I condemn you to constantly find unpaired socks, forever"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;He's right about the numbers&lt;sup id="sf-a-limited-defence-of-footnotes-3-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-3" class="simple-footnote" title="or, ye gods and little fishes, a selection of mad typographic symbols which most people can't even type and need to be copied from the web search results for &amp;quot;that little squiggly section thingy&amp;quot;"&gt;3&lt;/a&gt;&lt;/sup&gt; being meaningless, though, and that they're bad link text; the number "3" gives no indication of what's hidden behind it, and the analogy with "click here" as link text is a good one. We'll come back to this, but it is a correct objection.&lt;/p&gt;
&lt;h3&gt;What is a footnote, anyway?&lt;/h3&gt;
&lt;p id="footnotes-being-set-off"&gt;The issue with footnotes being set off this way (that is: that they're notes) isn't, though, that it's bad (which it is), it's that the alternatives are worse, at least in some situations. A footnote is an extra bit of information which is relevant to what you're reading, but not important enough that you &lt;em&gt;need&lt;/em&gt; to read it right now. That might be because it's explanatory (that is: it expands and enlarges on the main point being made, but isn't directly required), or because it's a reference (a citation, or a link out to where this information was found so it can be looked up later and to prove that the author didn't just make this up), or because it's commentary (where you don't want to disrupt the text that's written with additions inline, maybe because you didn't write it). Or, and this is important, because it's funnier to set it off like this. A footnote used this way is like the voice of the narrator in The Perils of Penelope Pitstop being funny about the situation. Look, I'll choose a random book from my bookshelf&lt;sup id="sf-a-limited-defence-of-footnotes-4-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-4" class="simple-footnote" title="alright, I chose a random Terry Pratchett book to make the point, I admit; I'm not stupid. But it really was the closest one to hand; I didn't spend extra time looking for particularly good examples"&gt;4&lt;/a&gt;&lt;/sup&gt;, &lt;em&gt;Reaper Man&lt;/em&gt; by Terry Pratchett.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://kryogenix.org/images/pratchett-footnote-example.jpg" alt="A photograph of a book page. Most of the text is a little blurred to distract attention from it. Midway down the page, unblurred text reads: 'Even the industrial-grade crystal ball was only there as a sop to her customers. Mrs Cake could actually read the future in a bowl of porridge.¹ She could have a revelation in a panful of frying bacon.' At the bottom of the page is the text referenced by the footnote marker, which reads: '¹ It would say, for example, that you would shortly undergo a painful bowel movement.'"&gt;&lt;/p&gt;
&lt;p&gt;This is done because it's funny. Alternatives... would not be funny.&lt;sup id="sf-a-limited-defence-of-footnotes-5-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-5" class="simple-footnote" title='This is basically "explaining the joke", something which squashes all the humour out of it like grapes in a press. Sorry, PTerry.'&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;If this read:&lt;/p&gt;
&lt;blockquote&gt;Even the industrial-grade crystal ball was only there as a sop to her customers. Mrs Cake could actually read the future in a bowl of porridge. (It would say, for example, that you would shortly undergo a painful bowel movement.) She could have a revelation in a panful of frying bacon.&lt;/blockquote&gt;

&lt;p&gt;then it's too distracting, isn't it? That's giving the thing too much prominence; it derails the point and then you have to get back on board after reading it. Similarly with making it a long note via &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; or via making it &lt;code&gt;&amp;lt;section role="aside"&amp;gt;&lt;/code&gt;, and Jake does make the point that that's for longer notes.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Even the industrial-grade crystal ball was only there as a sop to her customers. Mrs Cake could actually read the future in a bowl of porridge.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Note&lt;/summary&gt;It would say, for example, that you would shortly undergo a painful bowel movement.&lt;/details&gt;
&lt;p&gt;She could have a revelation in a panful of frying bacon.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Now, admittedly, half the reason Pratchett's footnotes are funny is because they're imitating the academic use. But the other half is that there is a place for that "voice of the narrator" to make snarky asides, and we don't really have a better way to do it.&lt;/p&gt;
&lt;p&gt;Sometimes the parenthesis is the best way to do it. Look at the explanations of "explanatory", "reference", and "commentary" in &lt;a href="#footnotes-being-set-off"&gt;the paragraph above about what a footnote is&lt;/a&gt;. They needed to be inline; the definition of what I mean by "explanatory" should be read along with the word, and you need to understand my definition to understand why I think it's important. It's directly relevant. So it's inline; you must not proceed without having read it. It's not a footnote. But that's not always the case; sometimes you want to expand on what's been written without requiring the reader to read that expansion in order to proceed. It's a help; an addition; something relevant but not &lt;em&gt;too&lt;/em&gt; relevant. (I think this is behind the convention that footnotes are in smaller text, personally; it's a typographic convention that this represents the niggling or snarky or helpful "voice in your head", annotating the ongoing conversation. But I haven't backed this up with research or anything.)&lt;/p&gt;
&lt;h3&gt;What's the alternative?&lt;/h3&gt;
&lt;p&gt;See, this is the point. Assume for the moment that I'm right&lt;sup id="sf-a-limited-defence-of-footnotes-6-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-6" class="simple-footnote" title="I always do"&gt;6&lt;/a&gt;&lt;/sup&gt; and that there is some need for this type of annotation -- something which is important enough to be referenced here but not important enough that you must read it to proceed. How do we represent that in a document?&lt;/p&gt;
&lt;p&gt;Jake's approaches are all reasonable in some situations. A note section (a "sidebar", I think newspaper people would call it?) works well for long clarifying paragraphs, little biographies of a person you've referenced, or whatever. If that content is less obviously relevant then hiding it behind a collapsed revealer triangle is even better. Short stuff which is that smidge more relevant gets promoted to be entirely inline and put in brackets. Stuff which is entirely reference material (citations, for example) doesn't really need to be in text in the document at all; don't footnote your point and then make a citation which links to the source, just link the text you wrote directly to the source. That certainly is a legacy of print media. There are annoying problems with most of the alternatives (a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; can't go in a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; even if inline, which is massively infuriating; sidenotes are great on wide screens but you still need to solve this problem on narrow, so they can't be the answer alone.) You can even put the footnote text in a tooltip as well, which helps people with mouse pointers or (maybe) keyboard navigation, and is what I do right here on this site.&lt;/p&gt;
&lt;p&gt;But... if you've got a point which isn't important enough to be inline and isn't long enough to get its own box off to the side, then it's gotta go &lt;em&gt;somewhere&lt;/em&gt;, and if that somewhere isn't "right there inline" then it's gotta be somewhere &lt;em&gt;else&lt;/em&gt;, and... that's what a footnote &lt;em&gt;is&lt;/em&gt;, right? Some text elsewhere that you link to.&lt;/p&gt;
&lt;p&gt;We can certainly take advantage of being a digital document to display the annotation inline if the user chooses to (by clicking on it or similar), or to show a popover (which paper can't do). But if the text isn't displayed to you up front, then you have to click on something to show it, and that thing you click on must not itself be distracting. That means the thing you click on must be small, and not contentful. Numbers (or little symbols) are not too bad an approach, in that light. The technical issues here are dispensed with easily enough, as &lt;a href="https://front-end.social/@leaverou/114784950642671149"&gt;Lea Verou&lt;/a&gt; points out: yes, put a bigger hit target around your little undistracting numbers so they're not too hard to tap on, that's important.&lt;/p&gt;
&lt;p&gt;But as Lea goes on to say, and Jake mentioned above... how do we deal with the idea that "3" needs to be both "small and undistracting" but also "give context so it's not just a meaningless number"? This is a real problem; pretty much by definition, if your "here is something which will show you extra information" marker gives you context about what that extra information &lt;em&gt;is&lt;/em&gt;, then it's long enough that you actually have to &lt;em&gt;read&lt;/em&gt; it to understand the context, and therefore it's distracting.&lt;sup id="sf-a-limited-defence-of-footnotes-7-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-7" class="simple-footnote" title="I've seen people do footnote markers which are little words rather than numbers, and it's dead annoying. I get what they're trying to do, which is to solve this context problem, but it's worse"&gt;7&lt;/a&gt;&lt;/sup&gt; This isn't really a circle that can be squared: these two requirements are in opposition, and so a compromise is needed.&lt;/p&gt;
&lt;p&gt;Lea makes the same point with "How to provide context without increasing prominence? Wrapping part of the text with a link could be a better anchor, but then how to distinguish from actual links? Perhaps we need a convention." And I agree. I think we need a convention for this. But... I think we've already &lt;em&gt;got&lt;/em&gt; a convention, no? A little superscript number or symbol means "this is a marker for additional information, which you need to interact with&lt;sup id="sf-a-limited-defence-of-footnotes-8-back"&gt;&lt;a href="#sf-a-limited-defence-of-footnotes-8" class="simple-footnote" title="you might 'interact' with this marker by clicking on it in a digital document, or by looking it up at the bottom of the page in a print doc, but it's all interaction"&gt;8&lt;/a&gt;&lt;/sup&gt; to get that additional information". Is it a perfect convention? No: the numbers are semantically meaningless. Is there a &lt;em&gt;better&lt;/em&gt; convention? I'm not sure there is.&lt;/p&gt;
&lt;h3&gt;An end on't&lt;/h3&gt;
&lt;p&gt;So, Jake's right: a whole bunch of things that are currently presented on the web as "here's a little (maybe clickable) number, click it to jump to the end of the document to read a thing" could be presented much better with a little thought. We web authors could do better at this. But should footnotes go away? I don't think so. Once all the cases of things that should be done better &lt;em&gt;are&lt;/em&gt; done better, there'll still be some left. I don't hate footnotes. I do hate limoncello and Red Bull, though.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-a-limited-defence-of-footnotes-1"&gt;sensible &lt;a href="#sf-a-limited-defence-of-footnotes-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-2"&gt;for good implementations, anyway; if you make your footnotes a link down to the end of the document, and then &lt;em&gt;don't&lt;/em&gt; provide a link &lt;em&gt;back&lt;/em&gt; via either the footnote marker or by adding it to the end, then you are a bad web author and I condemn you to constantly find unpaired socks, forever &lt;a href="#sf-a-limited-defence-of-footnotes-2-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-3"&gt;or, ye gods and little fishes, a selection of mad typographic symbols which most people can't even type and need to be copied from the web search results for "&lt;a href="https://nerdtechy.com/how-to-type-double-s-vertical-symbol"&gt;that little squiggly section thingy&lt;/a&gt;" &lt;a href="#sf-a-limited-defence-of-footnotes-3-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-4"&gt;alright, I chose a random &lt;em&gt;Terry Pratchett&lt;/em&gt; book to make the point, I admit; I'm not stupid. But it really was the closest one to hand; I didn't spend extra time looking for particularly good examples &lt;a href="#sf-a-limited-defence-of-footnotes-4-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-5"&gt;This is basically "explaining the joke", something which squashes all the humour out of it like grapes in a press. Sorry, PTerry. &lt;a href="#sf-a-limited-defence-of-footnotes-5-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-6"&gt;I always do &lt;a href="#sf-a-limited-defence-of-footnotes-6-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-7"&gt;I've seen people do footnote markers which are little words rather than numbers, and it's dead annoying. I get what they're trying to do, which is to solve this context problem, but it's worse &lt;a href="#sf-a-limited-defence-of-footnotes-7-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-a-limited-defence-of-footnotes-8"&gt;you might 'interact' with this marker by clicking on it in a digital document, or by looking it up at the bottom of the page in a print doc, but it's all interaction &lt;a href="#sf-a-limited-defence-of-footnotes-8-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Thu, 03 Jul 2025 07:12:00 +0100</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-07-03:/days/2025/07/03/a-limited-defence-of-footnotes/</guid><category>General musings</category></item><item><title>Serving streaming video that adapts to bandwidth from your own website</title><link>https://www.kryogenix.org/days/2025/04/16/serving-streaming-video-that-adapts-to-bandwidth-from-your-own-website/</link><description>&lt;p&gt;Recently, I was involved in an event where a video was shown, and the event was filmed. It would be nice to put the video of the event up somewhere so other people who weren't there could watch it. Obvious answer: upload it to YouTube. However, the video that was shown &lt;em&gt;at&lt;/em&gt; the event is Copyrighted Media Content and therefore is disallowed by YouTube and the copyright holder; it's not demonetised (which wouldn't be a problem), it's flat-out blocked. So YouTube is out.&lt;/p&gt;
&lt;p&gt;I'd like the video I'm posting to stick around for a long time; this is a sort of archival, reference thing where not many people will ever want to watch it but those that do might want to do so in ten years. So I'm loath to find some other random video hosting site, which will probably go bust, or pivot to selling online AI shoes or something. And the best way to ensure that something keeps going long-term is to put it on your own website, and use decent HTML, because that means that even in ten or twenty years it'll still work where the latest flavour-of-the-month thing will go the way of other old technologies and fade away and stop working over time. HTML won't do that.&lt;/p&gt;
&lt;p&gt;But... it's an hour long and in full HD. 2.6GB of video. And one of the benefits of YouTube is that they'll make the video adaptive: it'll fit the screen, and the bandwidth, of whatever device someone's watching it on. If someone wants to look at this from their phone and its slightly-shaky two bars of 4G connection, they probably don't want to watch the loading spinner for an hour while it buffers a full HD video; they can ideally get a cut down, lower-quality but quicker to serve, version. But... how is this possible?&lt;/p&gt;
&lt;p&gt;There are two aspects to doing this. One is that you serve up different &lt;em&gt;resolutions&lt;/em&gt; of video, based on the viewer's screen size. This is exactly the same problem as is solved for images by the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element to provide responsive images (where if you're on a 400px-wide screen you get a 400px version of the background image, not the 2000px full-res version), and indeed the magic words to search for here are &lt;em&gt;responsive video&lt;/em&gt;. And the person you will find who is explaning all this is &lt;a href="https://bsky.app/profile/scottjehl.com"&gt;Scott Jehl&lt;/a&gt;, who has written &lt;a href="https://scottjehl.com/posts/using-responsive-video/"&gt;a good description of how to do responsive video&lt;/a&gt; which explains it all in detail. You make versions of the video at different resolutions, and serve whichever one best matches the screen you're on, just like responsive images. Nice work; just what the doctor ordered.&lt;/p&gt;
&lt;p&gt;But there's also a second aspect to this: responsive video adapts to screen size, but it doesn't adapt to bandwidth. What we want, in addition to the responsive stuff, is that on poor connections the viewer gets a lower-bandwidth version as well as a lower-resolution version, and that the viewer's browser can dynamically switch from moment to moment between different versions of the video to match their current network speed. This task is the job of &lt;a href="https://en.wikipedia.org/wiki/HTTP_Live_Streaming"&gt;HTTP Live Streaming&lt;/a&gt;, or HLS. To do this, you essentially encode the video in a bunch of different qualities and screen sizes, so you've got a bunch of separate videos (which you've probably already done above for the responsive part) and then (and this is the key) you chop up each video into a load of small segments. That way, instead of the browser downloading the whole one hour of video at a particular resolution, it only downloads the next &lt;em&gt;segment&lt;/em&gt; at its current choice of resolution, and then if you suddenly get more (or less) bandwidth, it can switch to getting segment 2 from a &lt;em&gt;different&lt;/em&gt; version of the video which better matches where you currently are.&lt;/p&gt;
&lt;p&gt;Doing this sounds hard. Fortunately, all hard things to do with video are handled by ffmpeg. There's a nice writeup by Mux on &lt;a href="http://web.archive.org/web/20250416092600/https://www.mux.com/articles/how-to-convert-mp4-to-hls-format-with-ffmpeg-a-step-by-step-guide"&gt;how to convert an mp4 video to HLS with ffmpeg&lt;/a&gt;, and it works great. I put myself together a &lt;a href="https://gist.github.com/stuartlangridge/c0ae266306604c7db23e9855eea83976"&gt;little Python script&lt;/a&gt; to construct the ffmpeg command line to do it, but you can do it yourself; the script just does some of the boilerplate for you. Very useful.&lt;/p&gt;
&lt;p&gt;So now I can serve up a video which adapts to the viewer's viewing conditions, and that's just what I wanted. I have to pay for the bandwidth now (which is the &lt;em&gt;other&lt;/em&gt; benefit of having YouTube do it, and one I now don't get) but that's worth it for this, I think. Cheers to Scott and Mux for explaining all this stuff.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Wed, 16 Apr 2025 09:26:00 +0100</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-04-16:/days/2025/04/16/serving-streaming-video-that-adapts-to-bandwidth-from-your-own-website/</guid><category>General musings</category></item><item><title>Use RSS to read newsletters</title><link>https://www.kryogenix.org/days/2025/02/08/use-rss-to-read-newsletters/</link><description>&lt;p&gt;Everyone's got a newsletter these days (like everyone's got a podcast). In general, I think this is OK: instead of going through a middleman publisher, have a direct connection from you to the people who want to read what you say, so that that audience can't be taken away from you.&lt;/p&gt;
&lt;p&gt;On the other hand, I don't actually &lt;em&gt;like&lt;/em&gt; newsletters. I don't really like giving my email address to random people&lt;sup id="sf-use-rss-to-read-newsletters-1-back"&gt;&lt;a href="#sf-use-rss-to-read-newsletters-1" class="simple-footnote" title="despite how it's my business to do so and it's right there on the front page of the website, I know, I know"&gt;1&lt;/a&gt;&lt;/sup&gt;, and frankly an email app is not a great way to read long-form text! There are many apps which are a lot better at this.&lt;/p&gt;
&lt;p&gt;There is a solution to this and the solution is called RSS. &lt;a href="https://bell.bz/love-newsletters-youre-gonna-love-rss/"&gt;Andy Bell explains RSS&lt;/a&gt; and this is exactly how I read newsletters. If I want to read someone's newsletter and it's on Substack, or ghost.io, or buttondown.email, what I actually do is subscribe to their newsletter &lt;em&gt;but&lt;/em&gt; what I'm actually subscribing to is their RSS feed. This sections off newsletter stuff into a completely separate app that I can catch up on when I've got the time, it means that the newsletter owner (or the site they're using) can't decide to "upsell" me on other stuff they do that I'm not interested in, and it's a better, nicer reading experience than my mail app.&lt;sup id="sf-use-rss-to-read-newsletters-2-back"&gt;&lt;a href="#sf-use-rss-to-read-newsletters-2" class="simple-footnote" title="Is all of this doable in my mail client? Sure. I could set up filters, put newsletters into their own folders/labels, etc. But that's working around a problem rather than solving it"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I use &lt;a href="https://netnewswire.com/"&gt;NetNewsWire&lt;/a&gt; on my iOS phone, but there are a bunch of other newsreader apps for every platform and you should choose whichever one you want. Andy lists a bunch, above.&lt;/p&gt;
&lt;p&gt;The question, of course, then becomes: how do you find the RSS feed for a thing you want to read?&lt;sup id="sf-use-rss-to-read-newsletters-3-back"&gt;&lt;a href="#sf-use-rss-to-read-newsletters-3" class="simple-footnote" title="I suggested to Andy that he ought to write this post explaining how to do this and then realised that I should do it myself and stop being such a lazy snipe, so here it is"&gt;3&lt;/a&gt;&lt;/sup&gt; Well, it turns out... you don't have to.&lt;/p&gt;
&lt;p&gt;When you want to subscribe to a newsletter, you literally just put the web address of the newsletter itself into your RSS reader, and that reader will take care of finding the feed and subscribing to it, for you. It's magic. Hooray! I've tested this with substack, with ghost.io, with buttondown.email, and it works with all of them. You don't need to do anything.&lt;/p&gt;
&lt;p&gt;If that &lt;em&gt;doesn't&lt;/em&gt; work, then there is one neat alternative you can try, though. &lt;a href="https://kill-the-newsletter.com/"&gt;Kill The Newsletter&lt;/a&gt; will give you an email address for any site you name, and provide the incoming emails to that &lt;em&gt;as an RSS feed&lt;/em&gt;. So, if you've found a newsletter which &lt;em&gt;doesn't&lt;/em&gt; exist on the web (boo hiss!) and doesn't provide an RSS feed, then you go to KTN, it gives you some randomly-generated email address, you subscribe to the intransigent newsletter &lt;em&gt;with that email address&lt;/em&gt;, and then you can subscribe to the resultant feed in your RSS reader. It's dead handy.&lt;/p&gt;
&lt;p&gt;If you run a newsletter and it &lt;em&gt;doesn't&lt;/em&gt; have an RSS feed and you want it to have, then have a look at whatever newsletter software you use; it will almost certainly provide a way to create one, and you might have to tick a box. (You might also want to complain to the software creators that that box wasn't ticked by default.) If you've got an RSS feed for the newsletter that you write, but putting your site's address into an RSS reader doesn't &lt;em&gt;find&lt;/em&gt; that RSS feed, then what you need is &lt;a href="https://www.rssboard.org/rss-autodiscovery"&gt;RSS autodiscovery&lt;/a&gt;, which is the "magic" alluded to above; you add a line to your site's HTML in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section which reads &lt;code&gt;&amp;lt;link rel="alternate" type="application/rss+xml" title="RSS" href="https://URL/of/your/feed"&amp;gt;&lt;/code&gt; and then it'll work.&lt;/p&gt;
&lt;p&gt;I like this. Read newsletters at my pace, in my choice of app, on my terms. More of that sort of thing.&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-use-rss-to-read-newsletters-1"&gt;despite how it's my business to do so and it's right there on the front page of the website, I know, I know &lt;a href="#sf-use-rss-to-read-newsletters-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-use-rss-to-read-newsletters-2"&gt;Is all of this doable in my mail client? Sure. I could set up filters, put newsletters into their own folders/labels, etc. But that's working around a problem rather than solving it &lt;a href="#sf-use-rss-to-read-newsletters-2-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-use-rss-to-read-newsletters-3"&gt;I &lt;a href="https://bsky.app/profile/sil.kryogenix.org/post/3lhnuv42cqk2e"&gt;suggested to Andy&lt;/a&gt; that he ought to write this post explaining how to do this and then realised that I should do it myself and stop being such a lazy snipe, so here it is &lt;a href="#sf-use-rss-to-read-newsletters-3-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Sat, 08 Feb 2025 15:09:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-02-08:/days/2025/02/08/use-rss-to-read-newsletters/</guid><category>General musings</category></item><item><title>Blog Questions Challenge</title><link>https://www.kryogenix.org/days/2025/02/03/blog-questions-challenge/</link><description>&lt;p&gt;The latest thing circulating around people still blogging is the Blog Questions Challenge; &lt;a href="https://hicks.design/journal/blog-questions-challenge"&gt;Jon&lt;/a&gt; did it (and &lt;a href="https://mastodon.social/@jonhicks/113922089500368028"&gt;asked if I was&lt;/a&gt;) and so have &lt;a href="https://adactio.com/journal/21674"&gt;Jeremy&lt;/a&gt; and &lt;a href="https://ethanmarcotte.com/wrote/blog-questions-challenge/"&gt;Ethan&lt;/a&gt; and a bunch of others, so clearly it is time I should get on board, fractionally late as ever.&lt;sup id="sf-blog-questions-challenge-1-back"&gt;&lt;a href="#sf-blog-questions-challenge-1" class="simple-footnote" title="In my defence, it was my birthday."&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;Why did you start blogging in the first place?&lt;/h3&gt;
&lt;p&gt;Some other people I admired were doing it. I think the person I was most influenced by to start doing it was &lt;a href="https://simonwillison.net"&gt;Simon Willison&lt;/a&gt;, who is also still at it&lt;sup id="sf-blog-questions-challenge-2-back"&gt;&lt;a href="#sf-blog-questions-challenge-2" class="simple-footnote" title="although no longer at simon.incutio.com -- what even was Incutio?"&gt;2&lt;/a&gt;&lt;/sup&gt;, but a whole bunch of people got on board at around that same time, back in the early days when you be a medium-sized fish in a small pool just by participating. Mark Pilgrim springs to mind as well -- that's a good example of having influence, when the "standard format" of permalinks got sort of hashed out collectively to be &lt;code&gt;/2025/02/03/blog-questions-challenge,&lt;/code&gt; which a lot of places still adhere to (although it feels faintly quaint, these days).&lt;/p&gt;
&lt;p&gt;Interestingly, a lot of the early posts on this site are short two-sentence half-paragraph things, throwaway thoughts, and that all got sucked up by social media... but social media hadn't been invented, back in 2002.&lt;/p&gt;
&lt;p&gt;Also interestingly: the second post on this here blog&lt;sup id="sf-blog-questions-challenge-3-back"&gt;&lt;a href="#sf-blog-questions-challenge-3" class="simple-footnote" title='I resisted the word "blog" for a long time, calling it a "weblog", and the activity being "weblogging", because "blog" is such an ugly word. Like most of the fights I was picking in the mid 2000s, this also seems faintly antiquated and passé now. Sic transit gloria mundi and all that.'&gt;3&lt;/a&gt;&lt;/sup&gt; was &lt;a href="https://www.kryogenix.org/days/2002/04/21/release/"&gt;bitching at Mozilla about the Firefox release schedule&lt;/a&gt;. Nothing new under the sun.&lt;sup id="sf-blog-questions-challenge-4-back"&gt;&lt;a href="#sf-blog-questions-challenge-4" class="simple-footnote" title="or &amp;quot;nihil sub sole novum&amp;quot;, since we're doing Latin quotes today"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;What platform are you using to manage your blog and why did you choose it? Have you blogged on other platforms before?&lt;/h3&gt;
&lt;p&gt;Cor. When it started, this site was being run by &lt;a href="https://www.kryogenix.org/code/castalian/"&gt;Castalian&lt;/a&gt;, which was basically "classic ASP but Python instead of VBScript", a thing I built. This is because I was using ASP at work on Windows machines, so that was the model for "dynamic web pages" that I understood, but I wasn't on Windows&lt;sup id="sf-blog-questions-challenge-5-back"&gt;&lt;a href="#sf-blog-questions-challenge-5" class="simple-footnote" title="and Windows's relationship with Python has always been a bit unsteady, although it's better these days now that Microsoft are prepared to acknowledge that other people can have ideas"&gt;5&lt;/a&gt;&lt;/sup&gt; and so I built it myself. No idea if it still works and I very much doubt it since it's old enough to buy all the drinks these days.&lt;/p&gt;
&lt;p&gt;After that it was &lt;a href="https://en.wikipedia.org/wiki/Movable_Type"&gt;Movable Type&lt;/a&gt; for a bit and then, because I'd discovered the idea of funky caching&lt;sup id="sf-blog-questions-challenge-6-back"&gt;&lt;a href="#sf-blog-questions-challenge-6" class="simple-footnote" title='you write the pages in an online form, but then a server process builds a static HTML version of them; the advanced version of this where pages were only built on request was called "funky caching" back then'&gt;6&lt;/a&gt;&lt;/sup&gt; it was &lt;a href="https://www.kryogenix.org/code/vellum/"&gt;Vellum&lt;/a&gt;, that model (a) in Python and (b) written by me. &lt;em&gt;Then&lt;/em&gt; for a while it was "Thort", which was based on CouchDB&lt;sup id="sf-blog-questions-challenge-7-back"&gt;&lt;a href="#sf-blog-questions-challenge-7" class="simple-footnote" title="if a disinterested observer were to consider this progression, they might unfairly but accurately conclude that whatever this site runs on is basically a half-arsed system I built based on the latest thing I'm interested in, mightn't they?"&gt;7&lt;/a&gt;&lt;/sup&gt;, and then it was WordPress, and then in 2014 I &lt;a href="https://www.kryogenix.org/days/2014/02/13/static-electricity/"&gt;switched from WP to a static build&lt;/a&gt; based on &lt;a href="https://getpelican.com/"&gt;Pelican&lt;/a&gt;, which it still is to this day. Crikey, that was over ten years ago!&lt;sup id="sf-blog-questions-challenge-8-back"&gt;&lt;a href="#sf-blog-questions-challenge-8" class="simple-footnote" title="tempus fugit. OK, I'll stop now."&gt;8&lt;/a&gt;&lt;/sup&gt; I like static site generators: I even wrote &lt;a href="https://websitesetup.org/best-static-site-generators/"&gt;10 Popular Static Site Generators&lt;/a&gt; a few years ago for WebsiteSetup which I think is still pretty good.&lt;/p&gt;
&lt;h3&gt;How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that’s part of your blog?&lt;/h3&gt;
&lt;p&gt;In my text editor, which is &lt;a href="https://www.sublimetext.com/"&gt;Sublime Text&lt;/a&gt;. The static setup is here on my machine; I write a post, I type &lt;code&gt;make kryogenix&lt;/code&gt;, and it runs a whole little series of scripts which invoke Pelican to build the static HTML for the blog, do a few things that I've added (such as add footnote handling&lt;sup id="sf-blog-questions-challenge-9-back"&gt;&lt;a href="#sf-blog-questions-challenge-9" class="simple-footnote" title="like this!"&gt;9&lt;/a&gt;&lt;/sup&gt;, make &lt;code&gt;og:image&lt;/code&gt; links and images&lt;sup id="sf-blog-questions-challenge-10-back"&gt;&lt;a href="#sf-blog-questions-challenge-10" class="simple-footnote" title="an idea I stole shamelessly from Zach Leatherman"&gt;10&lt;/a&gt;&lt;/sup&gt;, and sort of handle webmentions but that's broken at the moment) and then copy it up to my actual website (via git) to be published.&lt;/p&gt;
&lt;p&gt;It's all a bit lashed together, to be honest, but this whole website is like that. It is something like an ancient city, such as London or Rome; what this site is mostly built on is the ruins of the previous history of the city. Sometimes the older bits poke through because they're still actually OK, or they never got updated; sometimes they've been replaced with the new shiny. You should see the &lt;code&gt;.htaccess&lt;/code&gt; file, which operates a bewildering set of redirects through about six different generations of URLs so &lt;a href="https://www.w3.org/Provider/Style/URI"&gt;all the old links still work&lt;/a&gt;.&lt;sup id="sf-blog-questions-challenge-11-back"&gt;&lt;a href="#sf-blog-questions-challenge-11" class="simple-footnote" title="Outgoing links are made to continue to work via unrot.link from the excellent Remy Sharp"&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;When do you feel most inspired to write?&lt;/h3&gt;
&lt;p&gt;When the muse seizes me. Sometimes that's a lot; sometimes not. I do quite a lot of &lt;a href="https://www.kryogenix.org/books/"&gt;paid writing&lt;/a&gt; as part of my various day jobs for others, and quite a lot of creative writing as part of running a &lt;a href="https://www.youtube.com/@pbproleplaying3278"&gt;play-by-post D&amp;amp;D campaign&lt;/a&gt;, and that sucks up a reasonable amount of the writing energy, but there are things which just demand going on the website. Normally these days it's things where I want them to be a reference of some kind -- maybe of a useful tech thing, or some important thought, or something interesting -- for myself or for others. &lt;/p&gt;
&lt;p&gt;Alternatively you might think the answer is "while in the pub, which leads to making random notes in an email to myself from my phone and then writing a blog post when I get home" and while this is not true, it's not &lt;em&gt;not&lt;/em&gt; true either. I do not want to do a histogram of posting times from this site because I am worried that I will find that the majority are at, like, 11.15pm.&lt;/p&gt;
&lt;h3&gt;Do you publish immediately after writing, or do you let it simmer a bit as a draft?&lt;/h3&gt;
&lt;p&gt;Always post immediately. I have discovered about myself that, for semi-ephemeral stuff like posts here or projects that I do for fun, that I need to get them done as part of that initial burst of inspiration and energy. If I don't get it done, then my enthusiasm will fade and they will linger half-finished for ever and never get completed. I don't necessarily &lt;em&gt;like&lt;/em&gt; this, but I've learned to live with it. If I think of an idea for a post and write a note about it and then &lt;em&gt;don't&lt;/em&gt; do it, when I rediscover the note a week later it will not seem anything like as compelling. So posts are mostly written as one long stream-of-consciousness to capitalise on the burning of the creative fire before it gets doused by time or work or everything going on in the world. Carpe diem, I guess.&lt;sup id="sf-blog-questions-challenge-12-back"&gt;&lt;a href="#sf-blog-questions-challenge-12" class="simple-footnote" title="I was lying about not doing this any more, obviously"&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;What’s your favourite post on your blog?&lt;/h3&gt;
&lt;p&gt;Maybe &lt;a href="https://www.kryogenix.org/days/2012/02/04/it-s-cold-outside/"&gt;It's Cold Outside&lt;/a&gt;, or &lt;a href="https://www.kryogenix.org/days/2012/03/11/monkey-island-2-for-about-the-fifth-time/"&gt;Monkey Island 2, for about the fifth time&lt;/a&gt;, or &lt;a href="https://www.kryogenix.org/days/2017/10/15/charles-paget-wade-and-the-underthing/"&gt;Charles Paget Wade and the Underthing&lt;/a&gt; for writing, although each of them have little burrs in the wording that I want to polish when I re-read them. The &lt;a href="https://www.kryogenix.org/days/2025/01/30/forty-nine/"&gt;series of birthday posts&lt;/a&gt; have been going on since the beginning, one every year, which probably wins for consistency. For technical stuff, maybe &lt;a href="https://www.kryogenix.org/days/2014/05/13/some-thoughts-on-soonsnap-and-little-big-details/"&gt;Some thoughts on soonsnap and little big details&lt;/a&gt; (now sadly defunct) or &lt;a href="https://www.kryogenix.org/days/2013/06/06/the-thing-and-the-whole-of-the-thing-on-drm-in-html/"&gt;The thing and the whole of the thing: on DRM in HTML&lt;/a&gt;. I like my own writing, mostly. Arrogant, I know.&lt;/p&gt;
&lt;h3&gt;Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature?&lt;/h3&gt;
&lt;p&gt;Not really at the moment, but, as above, these things tend to arrive in a blizzard of excitement and implementation and then linger forever once done. But right now... it all seems to work OK. Ask me when I get back from the pub.&lt;/p&gt;
&lt;h3&gt;Next?&lt;/h3&gt;
&lt;p&gt;Well, I should probably point back at some of the people who inspired me to do this or other things and keep doing so to this day. So &lt;a href="https://simonwillison.net/"&gt;Simon&lt;/a&gt;, &lt;a href="https://remysharp.com/"&gt;Remy&lt;/a&gt;, and &lt;a href="https://brucelawson.co.uk/"&gt;Bruce&lt;/a&gt;, perhaps!&lt;/p&gt;&lt;ol class="simple-footnotes"&gt;&lt;li id="sf-blog-questions-challenge-1"&gt;In my defence, it was &lt;a href="https://www.kryogenix.org/days/2025/01/30/forty-nine/"&gt;my birthday&lt;/a&gt;. &lt;a href="#sf-blog-questions-challenge-1-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-2"&gt;although no longer at &lt;code&gt;simon.incutio.com&lt;/code&gt; -- what even was Incutio? &lt;a href="#sf-blog-questions-challenge-2-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-3"&gt;I resisted the word "blog" for a &lt;em&gt;long&lt;/em&gt; time, calling it a "weblog", and the activity being "weblogging", because "blog" is such an ugly word. Like most of the fights I was picking in the mid 2000s, this also seems faintly antiquated and passé now. Sic transit gloria mundi and all that. &lt;a href="#sf-blog-questions-challenge-3-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-4"&gt;or "nihil sub sole novum", since we're doing Latin quotes today &lt;a href="#sf-blog-questions-challenge-4-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-5"&gt;and Windows's relationship with Python has always been a bit unsteady, although it's better these days now that Microsoft are prepared to acknowledge that other people can have ideas &lt;a href="#sf-blog-questions-challenge-5-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-6"&gt;you write the pages in an online form, but then a server process builds a static HTML version of them; the advanced version of this where pages were only built on request was called "funky caching" back then &lt;a href="#sf-blog-questions-challenge-6-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-7"&gt;if a disinterested observer were to consider this progression, they might unfairly but accurately conclude that whatever this site runs on is basically a half-arsed system I built based on the latest thing I'm interested in, mightn't they? &lt;a href="#sf-blog-questions-challenge-7-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-8"&gt;tempus fugit. OK, I'll stop now. &lt;a href="#sf-blog-questions-challenge-8-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-9"&gt;like this! &lt;a href="#sf-blog-questions-challenge-9-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-10"&gt;an idea I stole &lt;em&gt;shamelessly&lt;/em&gt; from &lt;a href="https://www.zachleat.com/"&gt;Zach Leatherman&lt;/a&gt; &lt;a href="#sf-blog-questions-challenge-10-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-11"&gt;&lt;em&gt;Outgoing&lt;/em&gt; links are made to continue to work via &lt;a href="https://unrot.link/"&gt;unrot.link&lt;/a&gt; from the excellent &lt;a href="https://remysharp.com/"&gt;Remy Sharp&lt;/a&gt; &lt;a href="#sf-blog-questions-challenge-11-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li id="sf-blog-questions-challenge-12"&gt;I was lying about not doing this any more, obviously &lt;a href="#sf-blog-questions-challenge-12-back" class="simple-footnote-back"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Mon, 03 Feb 2025 19:17:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-02-03:/days/2025/02/03/blog-questions-challenge/</guid><category>General musings</category></item><item><title>Forty Nine</title><link>https://www.kryogenix.org/days/2025/01/30/forty-nine/</link><description>&lt;p&gt;The sum of the digits of the square of 49 (2401) is the square root of 49.&lt;/p&gt;
&lt;p&gt;49 is the first square where the digits are squares. In this case, 4 and 9 are square numbers. &lt;/p&gt;
&lt;p&gt;It seems that 49 is an age of squares.&lt;/p&gt;
&lt;p&gt;I find myself increasingly OK with this.&lt;/p&gt;
&lt;p&gt;It is interesting, although really quite disillusioning, reading through the series of posts I've done on my birthday (follow the links back from &lt;a href="https://www.kryogenix.org/days/2024/01/30/somewhere-between-silver-and-tin/"&gt;last year&lt;/a&gt; for 22 years now) and seeing the trend in recent years. The world has got more worrying. A lot more. I hate this.&lt;/p&gt;
&lt;p&gt;So I've tried to find joy where I can get it. I'll give you an example. I looked up the number 49 for this, the same as I always do, and apparently 49 is the smallest discriminant of a totally real cubic field. Now, I'm sure that there are maths people out there who understand this. But to me, it is impossible to read this without putting on a mock Valley bro accent: &lt;em&gt;yeah brah, this field is&lt;/em&gt; todally &lt;em&gt;real, yah?&lt;/em&gt; And that made me smile. Bill dropped a birthday message on me at half seven this morning. Yesterday, I remembered a throwaway joke line that &lt;a href="https://popey.com/"&gt;popey&lt;/a&gt; dropped on me in 2015 and I literally laughed out loud in my own living room at the thought of it. Two days ago &lt;a href="https://jessica.tech"&gt;Jess&lt;/a&gt; and I went to see &lt;a href="https://murderontheorientexpressplay.com/"&gt;Murder on the Orient Express&lt;/a&gt; on stage at the Alexandra Theatre. Tonight I've been for beers with &lt;a href="https://brucelawson.co.uk"&gt;Bruce&lt;/a&gt; and Matt. My D&amp;amp;D group are about to confront Harazos to let them perform a ritual they've made up. Everyone in my team at Barnardo's was really nice about my birthday. None of this helps fix the world crashing down around us, but all of it certainly makes it easier to cope with, because all of it is a little bit of joy, of niceness, in the midst of unjoy and unniceness. Thank you, all of you.&lt;/p&gt;
&lt;p&gt;This has been not a great year. There's been ups and there's been downs. I'm still a lot better off than almost everyone -- I'm not under threat, I'm not more under threat than I was before, I can afford to do what I like as long as what I like isn't too extravagant. But it has nonetheless been tough. Maybe 2025 will be better, although to be honest I doubt it. And I have this sense of regret, not about the world getting worse, but that the idea that the world might get better has basically gone away. When I was younger, the thing was that the world &lt;em&gt;would&lt;/em&gt; get better, year on year, as time passed. And I am 100% sure that this was a thing that I got to think because I was privileged to not be being screwed over by the world. But I still had that thought, that things were OK today and would be slightly more than OK tomorrow, and at some point that went away. Things probably won't be better tomorrow. If they're the same, that probably in itself is a victory. This isn't really my tragedy; it's for people younger than me, who haven't experienced this change, who assume that things getting steadily more grey and sad and worse is just the way the world is; that's just &lt;em&gt;normal&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So I look for little bits of joy. Hopefully you have some little bits of joy yourself, as I do. Happy birthday to you, even if it's not your birthday. Eat some cake.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sil</dc:creator><pubDate>Thu, 30 Jan 2025 23:04:00 +0000</pubDate><guid isPermaLink="false">tag:www.kryogenix.org,2025-01-30:/days/2025/01/30/forty-nine/</guid><category>General musings</category></item></channel></rss>