jcd.pub

posts by Jonathan Davies

Archive Music Mixtapes
  • Disabling the Raspberry Pi 3B power LED on a mainline kernel

    Aug 18, 2026

    The raspberry pi 3B doesn’t present /sys/class/leds/PWR when you’re running the upstream kernel rather than the raspberry pi foundation’s official fork. You get /sys/class/leds/ACT which controls the green LED correctly, but no PWR entry for the red one. You’ll also see that dtparam=pwr_led_trigger=none and related commands that you are advised to add to config.txt do nothing.

    This is because the upstream device-tree simply doesn’t have an entry for it, as it’s implemented weirdly in the hardware. The fix is to define it yourself. Click here to skip to the code, or read on for an explanation.

    Read on →

  • Bit-Reversal Line Rasterization

    May 15, 2025

    On the left, smooth and stable. On the right, flickering and janky.
    Matthias with his trophy

    On the 3rd of June 2023, at the X demoparty in Someren in the southern Netherlands, quiss aka Matthias Kramm presented Boo as his entry in the C64 4K Intro category. You can watch it on YouTube here. While the demo mainly featured a couple of spinning cubes, the edges of those cubes were drawn using a new algorithm he calls “Bit-Reversal Rendering”. This brought the number of CPU cycles needed to plot each pixel of a line on a Commodore 64 down from 7 to 5, reducing the overhead on a number of key graphical effects. This earned him first place, and the adoration of the assembled demosceners.

    There are maybe half a dozen line plotting methods in existence and most of them date back to the cold war, so presenting a brand new one as a 4 kilobyte C64 intro that does parallel processing on the floppy disk controller is pretty cool.

    Of more general interest though, is the better temporal stability of this algorithm in comparison to others, which can be seen in the above animation.

    He later published details of how the algorithm works, but didn’t include an implementation. In this article I provide such an implementation, along with some further notes and explanations. I have also reproduced his animations in interactive form, and at the end I’ll show a new variation of the function I came up with.

    Read on →

  • The gamer's guide to line algorithms

    Apr 10, 2025

    The five algorithms from this article frolicking in their natural habitat. Can you tell which one is which?

    If you’ve ever tried making your own roguelike, you’ve likely encountered all sorts of strange-looking 1960s algorithms for doing geometry. Bresenham’s line algorithm is the most famous, but you often see people talking about “DDA lines” among other things. Hopefully, you found your way to the excellent Red Blob Games article on the subject, which describes a modern implementation in detail, with lovely interactive examples.

    A line algorithm used to menace an innocent watervine farmer

    Perhaps this left you with unanswered questions. These vintage functions look so different from each other, but they all seem to do the same thing!

    This post is structured as an ahistorical journey back through time. We start with the floating point algorithm given by Red Blob, which is optimal on modern computers, and end at Bresenham’s 1962 algorithm which uses pure integer arithmetic, as was optimal on the hardware of the day. We can then consider the other functions as steps along our imagined refactoring process, replacing floats piece by piece with integers.

    The aim is to present these functions in a unified style so they can be easily compared, and to try and give a little intuition as to what is going on with the geometry.

    Read on →

  • pure_rng - an immutable random number generator

    Mar 14, 2025

    Stepping through bad RNG configurations. PureRNG will of course produce pure white noise :)

    Anyone who considers arithmetical methods of producing random digits is, of course, in a state of sin.

    — John von Neumann, 1949

    PureRng, my newest creation, is a random number generator library for rust. The twist is that each generator gets consumed after outputting a single value. To get a new value you must instantiate a new generator, with a new seed. A nice API is provided to ensure this isn’t as much hassle as it sounds - a hash function is employed to generate seeds from arbitrary data.

    Read on →

  • NixOS on a Raspberry Pi 4 in 2025

    Jan 30, 2025

    Below is my minimal NixOS configuration for a Raspberry Pi 4B. It includes everything needed, ready to be loaded from a generic flake.nix.

    First enable binfmt on your build machine:

    boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
    

    Then build the SD card image with:

    nix build '.#nixosConfigurations.YOUR_HOSTNAME.config.system.build.sdImage'
    

    …and rebuild remotely with:

    nixos-rebuild switch --flake .#YOUR_HOSTNAME --target-host RASPI_IP --use-remote-sudo
    

    Below is the configuration.nix, with notes on how this all works after the fold:

    { lib, pkgs, modulesPath, ... }:
    {
      imports = [
        # This module installs the firmware
        "${modulesPath}/installer/sd-card/sd-image-aarch64.nix"
      ];
    
      nix.settings = {
        # This is needed to allow building remotely
        trusted-users = [ "YOUR_USERNAME" ];
    
        # The nix-community cache has aarch64 builds of unfree packages,
        # which aren't in the normal cache
        substituters = [
          "https://nix-community.cachix.org"
        ];
        trusted-public-keys = [
          "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
        ];
      };
    
      nixpkgs = {
        hostPlatform = "aarch64-linux";
        config = {
          allowUnfree = true;
        };
      };
    
      # These options make the sd card image build faster
      boot.supportedFilesystems.zfs = lib.mkForce false;
      sdImage.compressImage = false;
    
      networking = {
        # Set your hostname
        hostName = "YOUR_HOSTNAME";
        useNetworkd = true;
      };
    
      # Replace networkd with NetworkManager at your discretion
      systemd = {
        network = {
          enable = true;
    
          networks."10-lan" = {
            # This is the correct interface name on my raspi 4b
            matchConfig.Name = "end0";
    
            networkConfig.DHCP = "yes";
            linkConfig.RequiredForOnline = "routable";
          };
        };
      };
    
      # Add your username and ssh key
      users.users.YOUR_USERNAME = {
        isNormalUser = true;
        extraGroups = [ "wheel" ];
        openssh.authorizedKeys.keys = [ "YOUR_SSH_PUBLIC_KEY" ];
      };
    
      # Our user doesn't have a password, so we let them
      # do sudo without one
      security.sudo.wheelNeedsPassword = false;
    
      services = {
        openssh.enable = true;
      };
    
      # Set your timezone
      time.timeZone = "YOUR_TIMEZONE";
    
      environment.systemPackages = with pkgs; [
        libraspberrypi
        raspberrypi-eeprom
      ];
    
      hardware.enableRedistributableFirmware = true;
    
      system.stateVersion = "24.11";
    }
    

    Some notes:

    Read on →

  • Setting the zellij tab title to the running process in zsh

    Jun 24, 2024

    Here’s how to do it:

    function current_dir() {
        local current_dir=$PWD
        if [[ $current_dir == $HOME ]]; then
            current_dir="~"
        else
            current_dir=${current_dir##*/}
        fi
        
        echo $current_dir
    }
    
    function change_tab_title() {
        local title=$1
        command nohup zellij action rename-tab $title >/dev/null 2>&1
    }
    
    function set_tab_to_working_dir() {
        local result=$?
        local title=$(current_dir)
        # uncomment the following to show the exit code after a failed command
        # if [[ $result -gt 0 ]]; then
        #     title="$title [$result]" 
        # fi
    
        change_tab_title $title
    }
    
    function set_tab_to_command_line() {
        local cmdline=$1
        change_tab_title $cmdline
    }
    
    if [[ -n $ZELLIJ ]]; then
        add-zsh-hook precmd set_tab_to_working_dir
        add-zsh-hook preexec set_tab_to_command_line
    fi
    

    Not actually the running process, but the last command line. This does unfortunately add a couple of milliseconds of lag but it’s fairly imperceptible. If you can make this happen faster, please comment on the gist.

  • The tailscale-mullvad integration doesn't allow custom ports

    Jun 17, 2024

    Mole with hard hat - Stable Diffusion 2024

    A quick note on the mullvad exit node integration feature offered by tailscale.

    Tailscale doesn’t allow you to select a different UDP port for the connection to the mullvad node! It’s hardcoded to port 51820.

    This is in contrast to the official mullvad client, which lets you use port 53 or a custom port of your choice.

    If you’re using this feature in the mullvad client to tunnel out of a network which blocks common VPN ports like 51820, the tailscale integration won’t work! And there will be nothing you can do about it. Tailscale’s relay network doesn’t come into play here, the client just seems content to leave your machine unable to route any packets at all. Presumably there is debug logging to be found somewhere.

    There’s an issue open on the tailscale client github repo - go and vote for it so I can tunnel out via mullvad’s servers when I’m in the library rather than a spare VPS.

  • cargo-open

    May 16, 2024

    Cargo crates - Stable Diffusion 2024

    I’ve released a rust crate called cargo-open. It provides a handy cargo subcommand that opens an installed crate’s directory in your $EDITOR. It’s modelled on bundle open from Ruby’s Bundler.

    To install it, run:

    cargo install cargo-open
    

    Then:

    cargo open crate-name
    

    Note that this is meant for inspecting a crate’s contents, making changes is not a good idea.

    Have fun! 🦀

« Older