Notes

Roberts Radios

istream-radio.jpg

I recently bought a Roberts Revival iStream 3L radio having wanted one for some time. It’s a beautiful thing. I grew up in a house where Radio 4 was on constantly, mostly playing on an old Roberts which my mum carried about the house with her.

While I have Amazon Echos in my kitchen, living room and office/bedroom, I still regularly find myself using my phone to listen to podcasts and radio shows because I can take it with me as I potter about the house and garden. I have two problems with this:

  1. Phone speakers are rubbish and wearing headphones at home is both isolating for me and anyone else I’m sharing a room with.
  2. I’m trying to spend more time with my phone tucked away in a drawer and out of mind.

What I really want here is a dedicated portable device which can play the radio without requiring my phone or laptop to even be switched on. I want it to sound warm like Radio 4 should, regardless of the volume. I want it to feel good and make me smile. I basically want my mum’s old Roberts radio.

But I also want a few modern features – I want it to:

  • Play podcasts while my phone is off.
  • Have an alarm clock and sleep timer.
  • Act as a bluetooth speaker if I need it.
  • Recharge itself when plugged in to mains power.

Which is exactly what the Revival iStream 3L does. It looks, feels and sounds like a classic Roberts and supports FM & DAB+ radio as you’d expect, plus bluetooth, 3.5mm jack and USB stick inputs. It has an alarm and sleep timer. It runs on mains power or six normal AA batteries, but with the flip of a switch and six 2000mAh NiMH rechargable batteries it becomes rechargable like any other modern device. It is exactly the kind of high-quality, well-thought-out, future-proof product that Roberts built their reputation on decades ago.

And importantly for me, it also supports Internet Radio & Podcasts via Frontier Smart’s tech with Airable providing the online catalog, so I can search & stream online radio stations and podcasts while my phone and laptop are both switched off. It also supports streaming services like Spotify, Deezer & Amazon Music, though they all require another device to search and select songs.

All-in-all it’s a cracking piece of kit. And so long as I keep the firmware updated, I expect it to remain so for a lot longer than most modern devices.

It also runs a webserver!

The useguide recommends using the UNDOK app as an easier interface for configuring & managing the radio, which got me wondering how the two communicated. Turns out the radio exposes a simple HTTP API on your local network called FSAPI. Given you know the radio’s IP address, you can establish a session and send simple GET requests to it which return XML:

curl "http://192.168.1.72/fsapi/GET/netRemote.sys.audio.volume?pin=1234"

will return

<fsapiResponse>
<status>FS_OK</status>
<value>
<u8>2</u8>
</value>
</fsapiResponse>

showing that the current volume is 2.

There are quite a few pre-existing libraries for interacting with FSAPI devices, but the interface is small & simple enough that they’re not really necessary.

Building a macOS menubar app

After digging about with curl for a while I asked Claude Code to make a SwiftUI macOS menubar app which shows what’s playing on the radio and lets me control it from my mac. After a couple of hours’ iteration we ended up with this:

screenshot.png

If you have an iStream3L – or possibly any other FS-based radio – you can download a zip containing the menubar app from here. It should auto-discover your radio when you first run it.

copy / view as markdown

Minecraft Bluemap Plugins & Managing a Minecraft Server

For the last few years I’ve shared a survival minecraft world with my friend Cam. It’s hosted on WiseHosting and apart from a bunch of performance mods, the only non-vanilla stuff running on it are Distant Horizons and Bluemap.

While I could save some money by running my own server, I don’t really want to. I play minecraft to get away from my day-to-day, and I also don’t want to be responsible if something goes wrong and ~3 years of building in our world suddenly disappears. Hence I outsource to WiseHosting and avoid messing with the default installation too much.

But last month I found myself wanting three things which didn’t exist:

  1. A Bluemap plugin which shows Chunkbase-style structure markers on our actual world map.
  2. A Bluemap plugin to assist with chunk trimming.
  3. A cheap way to run a persistent shared creative world with Cam, and also to spin up and manage ephemeral minecraft servers for various reasons.

After a few days vibe coding and learning about the minecraft ecosystem I have all three…

Bluemap Structures

mc-bluemap-structures is a Fabric mod and Bluemap plugin which reads your world seed and adds chunkbase-like structure markers to your bluemap maps.

mc-bluemap-structures.png

It works by replicating Minecraft’s algorithms and creating BlueMap markers. You can read about how it works here. It’s currently somewhat limited by the fact that the BlueMap web app doesn’t handle very large numbers of markers performantly, so if you run it with a very large radius BlueMap will get janky. At some point I’ll look into rendering the markers myself as an overlay, which might help.

The process of building this was super interesting – I learned a whole bunch about how Minecraft decides where to generate structures, and also how Chunkbase manages to replicate the algorithms with such accuracy. I was surprised to discover that Chunkbase does this entirely on the client-side with a ~4 MB WebAssembly module compiled from Rust.

The project includes a command-line tool which uses playwright to extract structure positions directly from Chunkbase and save them to a JSON file. I built this as a way to verify my own algorithms against chunkbase, but it could easily be used to create a much simpler Bluemap plugin which simply reads a manually-generated JSON file from Chunkbase and shows markers based on that.

Bluemap Chunk Trimmer

As a minecraft map grows through player exploration, it becomes necessary to occasionally trim away chunks which have been generated but contain nothing valuable. This helps keep the world size (on disk) in check and also prevents players having to travel further and further afield to experience new features which will only appear in newly-generated chunks.

To do this well, you need a detailed map of your explored world and the ability to visually select chunks for deletion/retention. It’s also very helpful to know the playerInhabitedTime for each chunk – if it’s only a few seconds we can assume that it was only generated because someone flew near it and has probably never been visited. If it’s tens of hours we probably don’t want to trim it even if nothing’s been built, because someone’s spent enough time hanging out it’d feel weird if it changed.

Most of the GUI tools for chunk trimming only work on Windows or on local worlds (or both).

mc-bluemap-chunky-trimming is a Bluemap plugin which does two things:

1. Heatmap

mc-bluemap-chunky-heatmap.png

Reads the InhabitedTime NBT data from .mca region files and renders a heatmap as an overlay in bluemap’s 2D flat mode. Chunks with less than 1 minute of inhabited time don’t appear on the heatmap at all, and the rest are colored according to how long they’ve been inhabited, with the highest level being 10+ hours.

When the heatmap is on, a little HUD will show the inhabited time of the chunk under the cursor.

2. Chunk Selector

mc-bluemap-chunky-selector.png

With the Chunk Selector toggled on, control-click on a chunk will select it (or deselect it). Chunks can also be selected by dragging a box, or by “painting” with the mouse.

Selected chunks can be exported as either JSON or MCA-Selector compatible CSV. I intentionally decided against building the actual chunk deletion.

mc-infra

While the two plugins above might work for other people, mc-infra is very much for me alone.

It’s some fairly simple tooling for managing minecraft servers deployed to a Hetzner VPS.

1. Manifest System

The main feature is a manifest system which allows me to specify a bunch of stuff in YAML and use that to generate a suitable docker-compose file for itzg’s docker-minecraft-server and mc-router, as well as a bunch of other bits and pieces.

Given a manifest.yml like this

mod_groups:
fabric-base:
- fabric-api
- lithium
- ferrite-core
- c2me-fabric
- scalablelux
- noisiumforked
servers:
mynewworld:
type: FABRIC
version: LATEST
mode: creative
tier: permanent
seed: '493527618652710797'
mod_groups: [fabric-base]
modrinth_mods: [bluemap, distanthorizons, simple-voice-chat]
svc: true
pregen:
radius: 1500
backup:
interval: 24h
keep: 3

I’ll end up with a properly-configured fabric server called mynewworld with:

  • Suitable memory, disk usage, CPU limits etc for a “permanent”-tier world.
  • My standard fabric mods installed, plus Bluemap, DH and Simple voice Chat.
  • Proper config and port-forwarding to support Simple Voice Chat.
  • The Chunky mod installed and configured properly to pregen chunks to a 1500 radius.
  • 24h backups configured via itzg/docker-mc-backup.
  • Proper configuration so the server is available at mynewworld.mc.danny.is.
  • Because bluemap is included:
    • Proper configuration & setup of Bluemap
    • Nginx configured to serve bluemap at map-mynewworld.mc.danny.is

2. Control Scripts

A bunch of executable shell scripts are available on the PATH for managing the minecraft servers. Some of them are just very thin wrappers around docker commands. Others (like mc-nether-roof) are more complex.

Together, these scripts give me an interface for working with the minecraft servers on the box once I’ve ssh’d in.

3. Setup Scripts & Dev Tooling

The Hetzner box itself is configured with the tooling I want to work on it, including the developer tools I need to work on mods directly on the box. Scripts like setup.sh make it a little easier to recreate this whole thing on a fresh VPS if I ever need to.

4. Monitoring Web App

A simple Hono app provides a web interface for checking the status of the running minecraft servers, and includes some nice minecraft-specific stuff.

mc-infra-dashboard.png

mc-infra-worlddata.png

Wrapping Up

This whole thing was a super-interesting side-quest, and I learned a bunch of stuff despite most of the code here being written by an LLM. I don’t expect I’ll maintain these repos beyond keeping them all working for my own needs.

copy / view as markdown

Tracking Biggles Books

Thirty-odd years ago I borrowed some Biggles Audiobooks (on cassette tape) from my local library and enjoyed them so much I asked the librarian if they had any more. Turned out that while they had a few modern editions on the shelves, they had a whole collection of much older ones downstairs. And so for a year or two in the late ’90s I’d pop in to Eastbourne library every week or two: the lady always had 2-3 ready for me and took the time to ring round other libraries asking them to dust off any they had and sent them to Eastbourne.

Some time in 2000 I decided I wanted to read every Biggles book written and after digging about on a computer in the school library I eventually found a complete list on biggles.nl, which is a wonderful example of the kind of weird, niche hand-rolled website which first got me interested in the internet. (I’m happy to see that the International Biggles Association is still going in 2026!)

[Image]

I never did manage to read them all, but sometime in my early 20’s I started to pick up old copies if I saw them in second-hand bookshops and accidentally became a collector of biggles books. I now have 64 of the 98 published, and someday hope to own a first edition of all 98.

Managing my collection

During the COVID pandemic I accidentally spent fifty quid on eBay buying Biggles Books I already owned, so I set up an Airtable database to track my current collection and provide an easy list of those I’m still looking for. This was partly for me, and partly a reference for friends who spot a Biggles book in charity shops and message me.

Last week I realised my Biggles database is the only thing still on Airtable. So I grabbed a CSV export and had Claude Code create some stuff in Obsidian for me to keep notes, and a tiny interactive website at https://biggles.danny.is/.

The website is just three files:

  • canonical.json - All published books ordered chronologically with the title and publication date, and labelled as Pre-WW1, WW1, Interwar, WW2 or Post War.
  • collection.json – All the copies I own with the metadata I want to track.
  • index.html - Reads the JSON files and renders the site.

All three files are statically served by GitHub Pages.

Reflections

  1. This kind of project simply wouldn’t have happened without Claude Code. Sure, I could have done this without AI - but I just wouldn’t.
  2. Sites like biggles.nl and biggles.info are wonderful examples of the old-school “open web”. They have existed largely unchanged for decades and depend only on someone keeping their servers alive. And they’re still useful.
  3. Libraries are awesome.

Shoutout to the folks who worked at Eastbourne Library in the late ’90s, and to the folks who maintain biggles.nl and biggles.info. And if you’re reading this and have any books on my wanted list, please drop me an email!

copy / view as markdown

Code has always been the easy part | Quoting Kellan Elliott-McCrea

We’ve always had this tension. We’ve always fetishized the act of writing code, the quality of the code, the code as the primary artifact and IP. And on the other hand successful teams have always known that the value is the system, the value is human-technology hybrid that allows a product to be delivered, meet customer needs, evolve to provide more value over time, meet the spoken and unspoken needs of the problem domain, etc. This confusion in our thinking has laid at the heart of why, for example, technical hiring was such a disaster for so long. (Hiring continues to be terrible but it’s actually much better than it used to be. We now make fun of teams that ask you to reverse a linked list on a whiteboard while evaluating if they’d like to have a beer with you. That used to be the norm)

Said another way, we’ve known for a long time that code is the easy part. Has arguably always been the easy part, but certainly has been the easiest part of building software for the last several decades.

copy / view as markdown

Older Notes