Author: Andy Balaam
-
Maths: The Fun Parts – Graphs video
More of my series about my favourite bits of Maths. This time, Graphs: Slides for Maths: The Fun Parts – Graphs
-
Maths: The Fun Parts – Groups video
More of my series about my favourite bits of Maths. This time, Groups: Slides for Maths: The Fun Parts – Groups
-
Maths: The Fun Parts – Sets video
I’ve been wanting to make series about the cool bits of Maths for years and I’m finally getting around to it. It’s called “Maths: The Fun Parts” and the first part is on Sets: Slides for Maths: The Fun Parts – Sets
-
GitHub API GraphQL snippets
Every time I try to use GitHib’s GraphQL API I find myself totally lost, so here are some snippets I have found useful. All of these can be pasted into the GitHub GraphQL API Explorer, which can run the code as your logged-in user in the browser withou…
-
Live code reviews make life better
I’ve just got off a call with a colleague. During that call I: learned a lot about how the work he is doing fits in with what my team is working on understood the specific code we were discussing much better than if I’d looked at it alone helped him f…
-
Air-Source Heat Pump – 1 year later
10 months ago I wrote a blog post Air-Source Heat Pump – our experience so far, 2 months in about our new air source heat pump. Have a look back at that for photos of the device itself and more detail about installation etc. Less energy We used a lot l…
-
Deleted my Twitter account
Update: my twitter archive is here: artificialworlds.net/tweets. Update: you can see the hacky scripts I used to build this, based on twitter-archive-parser. This evening I deleted my Twitter account. I’m feeling surprisingly unsettled by doing it, to …
-
IETF115 Trip Report (Can Matrix help messaging standardisation through MIMI?)
Geeks don’t like to be formal, but we do like to be precise. That is the contrast that comes to mind as I attend my first IETF meeting, IETF 115 in London in November 2022. Like most standards bodies, IETF appears to have been formed as a reaction to s…
-
Setting the text selection in a browser: just use setBaseAndExtent
The Selection API is confusing and weird. But, here’s what I’ve discovered: just use setBaseAndExtend, and when (rarely) needed, extend. Summary Every selection in a browser consists of: an “anchor” – the beginning, where you started dragging, and a “…
-
Tips for contenteditables
I’ve been working a bit with contenteditable tags in my HTML, and learnt a couple of things, so here they are. Update: See also my demo of how to select text in various ways in a contenteditable. Why can’t I see the cursor inside an empty contenteditab…
-
Outreachy August 2022 update
I had the pleasure of being a mentor this summer for an Outreachy internship for the Matrix organisation. Outreachy provides internships to people subject to systemic bias and impacted by underrepresentation in the technical industry where they are liv…
-
Transcoding video files for playback in a browser
ffmpeg -i original.mkv -c:v libx264 -c:a aac -ac 2 -ab 384000 -ar 48000 new.mp4 (Short answer: use the above ffmpeg command line. Read on for how I did this in Tdarr.) I recently discovered Jellyfin, which gives me a Netflix-like UI for viewing my own …
-
Matrix is a Distributed Real-time Database Video
Curious to know a bit more about Matrix? This video goes into the details of what kinds of requests you need to send to write a Matrix client, and why it’s interesting to write a Matrix server. Slides: Matrix is a Distributed Real-time Database Slides…
-
Building cross-platform Rust for Web, Android and iOS – a minimal example
One of the advantages of writing code in Rust is that it can be re-used in other places. Both iOS and Android allow using native libraries within your apps, and Rust compiles to native. Web pages can now use WebAssembly (WASM), and Rust can compile to …
-
Deporting desperate people from the UK
Letter to my MP on deporting refugees to Rwanda, 2022-06-06. Dear Ben Spencer, Please do what you can to reverse the policy of sending asylum seekers to Rwanda. We are breaking our proud tradition of commitment to refugees. This policy seems to have th…
-
Improving my vimrc live on stream
I was becoming increasingly uncomfortable with how crufty my neovim config was getting, and especially how I didn’t understand parts of it, so I decided to wipe it clean and rebuild it from scratch. I did it live on stream, to make it feel like a worth…
-
Comparison of Matrix events before and after “Extensible Events”
[Updated 2022-11-17 based on the new draft of the MSC, notably removing backwards compatibility and the abbreviated forms.] (Background: Matrix is the awesome open standard for messaging that I get to work on now that I work at Element.) The Extensible…
-
Air-Source Heat Pump – our experience so far, 2 months in
[Update: see also Air-Source Heat Pump – 1 year later.] Summary: less energy, more money 2 months ago, we replaced our gas boiler with an air-source heat pump, which uses electricity to heat our home and boiler. This is a report of our experience so fa…
-
Providing MapLibre-compatible style JSON from openstreetmap-tile-server
[Previous: Self-hosting maps on my laptop] In the previous post I showed how to run OSM tile server stack locally. Now I’ve managed to connect a MapLibre GL JS front end to my local tile server and it’s showing maps! Sharing a location in Element Web (…
-
Self-hosting maps on my laptop
[See also: Providing MapLibre-compatible style JSON from openstreetmap-tile-server] As part of my research for working on location sharing for Element Web, the Matrix-based instant messenger, I have been learning about tile servers. I managed to get OS…
-
Streaming to Twitch and PeerTube simultaneously using nginx on Oracle cloud
Simulcasting RTMP using NGINX I want people to be able to watch my Matrix and Rust live coding streams using free software, so I’d like to simulcast to PeerTube as well as Twitch. This is possible using NGINX and its RTMP module. It does involve buildi…
-
New game: Tron – frantic multiplayer retro action
My newest game is out now on Smolpxl Games – Tron: Play at smolpxl.gitlab.io/tron. It’s a frantic multiplayer retro pixellated thingy playable in your browser. Try to stay alive longer than everyone else! This version allows many players (up to 16 if …
-
Preventing Virgin Media hijacking my DNS
Yesterday I learned that Virgin Media is inserting itself into some of my DNS requests. Much as I am not a fan of how powerful Cloudflare are, if they are telling the truth about their DNS, then it’s safe, so I followed their instructions on how to use…
-
Letter to my MP about climate emergency
[Introduction including details about my own air source heat pump install, and mention of the ending of the RHI funding in April 2022.] After I have installed an air source heat pump, I will pay more money to heat my home, even though I am using less e…
-
New Job at Element (Matrix)
I started a new job today at Element! It has been a long-standing ambition of mine to work in Free and Open Source software, and I am very excited to work for a company that is the main developer of a really important project: the Matrix communication …
-
What to cache when building Rust using Gitlab CI or similar
Update: caching $RUSTUP_HOME and $CARGO_HOME does not work for me – I removed them. When building your project with Gitlab CI or a similar build tool, you can end up spending a lot of time watching your build repeat the same steps over and over. This i…
-
Printing the version of a Maven project
To print a Maven project’s version number, try: mvn help:evaluate -Dexpression=project.version -q -DforceStdout -q suppresses all output(!) -DforceStdout unsuppresses the actual thing you asked to be printed. I will refrain from commenting on how diffi…
-
Minimal example of a Maven pom for a mixed Kotlin and Java project
The Kotlin docs describe some things you need in your pom.xml to create a project that is a mix of Kotlin and Java code, but there is no complete example, so here is mine: pom.xml:<project> <modelVersion>4.0.0</modelVersion> <g…
-
Importing/migrating from one peertube server to another
My Peertube server is shutting down, so I need to move my videos to another one. The official scripts don’t seem to cover this case very well, so here is what I did. My script fetches videos and their details and uploads them to the new server via the …
-
Why write an entire game (including Graphics) in a single, hand-coded JavaScript file?
My new game, Rightwaves, is out now! It’s a tribute to the great classic R-Type. The entire implementation, including all the graphics, is contained within a single ~5000-line JavaScript file. Why? This is a terrible idea Let me start by saying I do n…
-
Matrix is the only (chat) game in town
On my phone and computer I use WhatsApp, Signal, Slack, Keybase, Discord, IRC, XMPP/Jabber and Element/Matrix. In addition, I occasionally use the messaging features of Mastodon, Twitter and even LinkedIn. I’ve never used Telegram, Line, WeChat, Sessio…
-
Writing Snake in Terraform Video
This was quite a challenge, and I nearly gave up, but I managed to write a Snake game in Terraform: Slides: artificialworlds.net/presentations/snake-terraform Source code: gitlab.com/andybalaam/snake Slides source code: gitlab.com/andybalaam/videos-sn…
-
Suspending the computer using Kupfer
I have recently started using Kupfer again as my application launcher in Ubuntu MATE, and I found it lacked the ability to suspend the computer. Here is the plugin I wrote to support this. To install it, quit Kupfer, create a directory in your home dir…
-
Uploading to PeerTube from the command line
PeerTube’s API documentation gives an example of how to upload a video, but it is missing a couple of important aspects, most notably how to provide multiple tags use form-encoded input, so my more complete script is below. Use it like this: # First, m…
-
Republishing Bartosz Milewski’s Category Theory lectures
Category Theory is an incredibly exciting and challenging area of Maths, that (among other things) can really help us understand what programming is on a fundamental level, and make us better programmers. By far the best explanation of Category Theory…
-
Announcing I-DUNNO 1.0 and web-i-dunno
It’s hard to believe it’s already a year since the release of RFC 8771 (The Internationalized Deliberately Unreadable Network NOtation), which for me at least made me think about IP addresses in a whole new way. So, it seems fitting for the anniversary…
-
Automatically filling in the UK COVID test results page with Selenium IDE
Lots of people are filling in the extremely detailed UK government COVID test result page twice every week. It asks you to fill in a very large list of details, most of which are the same every time, but it doesn’t remember what you typed last time. I …
-
Toggle window decorations on Linux GTK3 with Python3
The Internet is full of outdated Python code for doing things with windows, so here is what I got working today in a Python 3, GTK 3 environment. This script toggles the window decorations on the active window on and off. I have it bound to Ctrl+NumPad…
-
Questions about RFC 8771
During my work on RFC 8771 The Internationalized Deliberately Unreadable Network NOtation (I-DUNNO) I have come across a number of questions. I am documenting them here so I can send them to the authors and try to improve my understanding of the intent…
-
Announcing Rust I-DUNNO
At the ACCU Conference last week I learned about RFC 8771 The Internationalized Deliberately Unreadable Network NOtation (I-DUNNO) from Jim Hague, and thought it would be fun to knock up a Rust implementation. The project is here: gitlab.com/andybalaam…
-
Letter to my MP on Yemen
Sent via WriteToThem on Monday 1 March 2021. Dear Ben Spencer, Your experience on the front line in medicine means you are probably much more able than I to imagine the human impact of the situation in Yemen. On Sunday OCHA stated that 16 million peopl…
-
Making Smolpxl work on phones and tablets
I’ve added the first features intended to make Smolpxl games work well on touch interfaces like phones and tablets: I’ve added a button bar at the bottom (and moved the navigation buttons to the top). I’m looking for feedback on this: Does it work on…
-
Limiting the number of open sockets in a tokio-based TCP listener
I learned quite a bit today about how to think about concurrency in Rust. I was trying to use a Semaphore to limit how many open sockets my TCP listener allowed, and I had real trouble making it work. It either didn’t actually work, allowing any number…
-
Recommendation against the use of WhatsApp in your company
Here is the email I just sent to the organisation I volunteer for. Feel free to adapt and use in your context. Dear [organisation leaders], Much of the tech industry (e.g. [1]) is warning against the use of WhatsApp due to its policy of collecting and …
-
Streaming video with Owncast on a free Oracle Cloud computer
I just streamed about 40 minutes of me playing Trials Fusion using Owncast. Owncast is a self-hosted alternative to streaming services like Twitch and YouTube live. Normally, you would need to pay for a computer to self-host it on. Owncast suggest this…
-
Pinephone update
I got a Pinephone for Christmas! Here is quick summary of my experience with it. (Originally published on mastodon.) Update on the pinephone as promised. I love it, but I would definitely not recommend expecting to use it as your actual phone. I have …
-
Is your program a function or a service?
Maybe everyone knows this already, but for my own clarity, I think there are really two types of computer program: A function: something that you run, and get back a result. Example: a command-line tool like ls A service: something that sits around wa…
-
Shutdown order consistency: how Rust helps
Some Java code with bugs Here’s my main method (in Java). Can you guess the bug? Db db = new Db(); Monitoring monitoring = new Monitoring(); Monitoring mon2 = new Monitoring(); Billing billing = new Billing(db, monitoring); monitoring.setDb(db); runMa…
-
Edge computing providers
I’m looking into Edge computing at work. By Edge computing I mean running WASM programs in lots and lots of smallish computers in places near to actual people (rather than in huge cloud data centres). I think it’s cool because I love Rust, and Rust is …
-
Schema upgrades should be reversible (also other transformations, actually)
Are you writing schema upgrade code? Then I humbly suggest you take the time to write schema downgrade code too. “Why would I do that?” you might well ask, “I won’t ever need to downgrade.” Now, I imagine you’re expecting me to say you actually will ne…
-
Make levels for Rabbit Escape using the level editor!
You can make levels for Rabbit Escape!
-
Announcing Smolpxl Scores – a high score table for your game
It’s a very early beta for now, but I’m ready to announce Smolpxl Scores, which provides high-score tables for Free and Open Source games. Each game can have multiple high-score tables – for example, you might want one for each level. At the moment it’…
-
Dovecot not working after upgrade to Ubuntu 20.04.1 (dh key too small)
I upgraded to Ubuntu 20.04.1 and chose to keep my existing config files, and my mail server stopped working. In the log I saw: Nov 25 09:07:57 machine dovecot: imap-login: Error: Failed to initialize SSL server context: Can’t load DH parameters: error:…
-
Letter to my MP on the overseas aid budget.
Letter I sent to my MP today on the overseas aid budget. Let’s not be foolish. Dear Ben Spencer, Please use your influence to persuade the government to maintain our overseas aid budget commitment at 0.7% of national income. I believe that changing th…
-
Profile a Java unit test (very quickly, with no external tools)
I have a unit test that is running slowly, and I want a quick view of what is happening. I can get a nice overview of where the code spends its time by adding this to the JVM arguments: -agentlib:hprof=cpu=samples,lineno=y,depth=3,file=hprof.samples.tx…
-
Why a Free Software web games site?
Recently I’ve been having a lot of fun working on Smolpxl, which is a web site featuring some little retro web games that are all Free and Open Source Software. Here’s a sneak preview of the game I am working on: Why do this? Apart from the fact that …
-
Code your first game: Snake in JavaScript (on Raspberry Pi)
Welcome! We are going to code a whole snake game. It’s going to look like this: It doesn’t matter if you have never written any code before: I am going to try and explain everything from scratch. I’m going to assume you are using a Raspberry Pi. but y…
-
Play and create little retro games at Smolpxl
I love simple games: playing them and writing them. But, it can be overwhelming getting started in the complex ecosystems of modern technology. So, I am writing the Smolpxl library, which is some JavaScript code that makes it quite simple to write simp…
-
shareon.js.org now has a Share to Mastodon button
I was looking for the right way to make a “Share This”-style button for my tiny games site Smolpxl, and I found shareon which worked exactly the way I wanted (load the JavaScript and call a function to display the buttons, with no privacy concerns), an…
-
Coding a tiny game in JavaScript video
I’m working on a little JavaScript library called Smolpxl. It aims to make it really easy to create retro-style pixellated games that run well in the browser, using simple JavaScript. This is me live-streaming writing a tiny “game” using Smolpxl: To …
-
short – command line tool to truncate lines to fit in the terminal
Sometimes I run grep commands that search files with hugely-long lines. If those lines match, they are printed out and spam my terminal with huge amounts of information, that I probably don’t need. I couldn’t find a tool that limits the line-length of …
-
Set the date (EXIF) of a photo on Linux
To set the date when a photo was taken, install ExifTool e.g.: sudo apt install libimage-exiftool-perl # If on Ubuntu sudo dnf install perl-Image-ExifTool.noarch # If on Fedora And modify the photo with a command like this: exiftool -DateTimeOrigina…
-
How to send an SMS using netcat (via SMPP)
SMPP is a binary protocol used by phone companies to send text messages, otherwise known as SMS messages. It can work over TCP, so we can use netcat on the command line to send messages. A much better way to understand this protocol is to use Wireshark…
-
Letter to my MP on racism in the US and UK
Here is the text of a letter I wrote to my MP today via writetothem.com: Dear [], I am writing to you to express my growing horror at the ongoing police violence and brutality Black protesters are facing in the US, and the UK’s disturbing silence on t…
-
Example Android project with repeatable tests running inside an emulator
I’ve spent the last couple of days fighting the Android command line to set up a simple project that can run automated tests inside an emulator reliably and repeatably. To make the tests reliable and independent from anything else on my machine, I want…
-
Creating a tiny Docker image of a Rust project
I am building a toy project in Rust to help me learn how to deploy things in AWS. I’m considering using Elastic Beanstalk (AWS’s platform-as-a-service) and also Kubernetes. Both of these support deploying via Docker containers, so I am learning how to …
-
Keeping track of podcast times with a simple bash script on Linux
I was recording some podcast audio tonight and wanted to be able to press a single key when I reached a significant moment, so I could add the times to the show notes. I couldn’t find anything that already did this, so I wrote a tiny bash script. I ran…
-
Custom Bash tab completion for my program
I love Bash tab completion, and I want it for the command I am writing, so it can automatically complete parts of the command line when I run my program. Code Here is the script (install-bash-completion) I wrote to set it up (no need to be root – it in…
-
Struggling with Rust to figure out the right types for a function signature
I am loving writing code in Rust. So many things about the language and its ecosystem feel so right*. * For example: ownership of objects, expressive type system, compile to native, offline API docs, immutability, high quality libraries. One of the thi…
-
React Concepts Video
I tried to describe the main ideas that will help you write decent React code. Maybe it will help you understand the docs a bit more when you read them… Slides: React Concepts slides
-
Converting HTML slides to a PDF with Firefox
I have not found an automated way to generate a nice PDF from some slides written in HTML – if you know of one please add a comment! In the meantime, if you create slides using my HTML Slides template, then you can make a decent-ish-looking PDF like th…
-
Support the Software Freedom Conservancy
The Software Freedom Conservancy helps Free/Open Source software projects by providing infrastructure, financial structures, and legal help. It is a not-for-profit organisation that is dedicated to software freedom, something that I think is an importa…
-
KotlinJS: First impressions (formed by writing Snake) video
What it was like to write a snake game in KotlinJS: Slides: Snake in KotlinJS slides Code: github.com/andybalaam/snake
-
Dependency Injection frameworks: reasons to avoid them video
At my job we have done a great deal of work to remove Guice from our codebase. Here I try to explain why we did that, and try to apply my reasoning to dependency injection frameworks in general. Slides: Dependency Injection frameworks: reasons to avo…
-
Coding workshop example worksheets
This week we did a coding workshop exercise: 2 teams implemented the different sides of the SMPP protocol (without speaking to each other) and this morning we tried out connecting them together. We successfully sent a message and received an acknowledg…
-
Building an all-in-one Jar in Gradle with the Kotlin DSL
To build a “fat” Jar of your Java or Kotlin project that contains all the dependencies within a single file, you can use the shadow Gradle plugin. I found it hard to find clear documentation on how it works using the Gradle Kotlin DSL (with a build.gra…
-
Creating a self-signed certificate for Apache and connecting to it from Java
Our mission: to create a self-signed certificate for an Apache web server that allows us to connect to it over HTTPS (SSL/TLS) from a Java program. The tricky bit for me was generating a certificate that contains Subject Alternative Names for my server…
-
ACCU Talk “How Kotlin makes your Java code better”
Here is the live version of my talk designed to help you advocate for adopting Kotlin:
-
ACCU Talk “How Git really works”
Here is the talk CB Bailey and I did at ACCU Conference 2019. This was the first talk I have done with someone else, and I really enjoyed it:
-
Examples of Kotlin making your Java code better video
People at work suggested Kotlin was “just syntactic sugar”, so I set out to explain how Kotlin can really make better code, and here is the result: Slides: Examples of Kotlin making your Java code better slides
-
What is a Monad? Video
Slightly jokey video about what a Monad is, but with a genuine explanation, all in just about 5 minutes. First given as a lightning talk at the ACCU Conference in 2019. Slides: What is a Monad? Slides
-
Build with a different Java version (e.g. 11) using Docker
To spin up a temporary environment with a different Java version without touching your real environment, try this Docker command: docker run -i -t –mount “type=bind,src=$PWD,dst=/code” openjdk:11-jdk bash (Change “11-jdk” to the version you want as li…
-
Rabbit Escape t-shirts
If you’d like to help promote Rabbit Escape, my free software Android/PC game, how about a t-shirt? Use the link below to design your shirt: Rabbit Escape t-shirt Note: there’s no profit in this for us – it’s just for promotion. If you’d like to contr…
-
Scheduling a task in Java within a CompletableFuture
When we want to do something later in our Java code, we often turn to the ScheduledExecutorService. This class has a method called schedule(), and we can pass it some code to be run later like this: ScheduledExecutorService executor = Executors.new…
-
Rabbit Escape 0.12 out now, with water
The newest feature of Rabbit Escape, water, has been brewing a long time, but we now think it’s ready: Water can flow, it can put out fires, and it can drown rabbits. Rabbots seem to be immune though… Check out the 20 new levels we have released! (T…
-
Convert a video to a GIF with reasonable colours
Here’s a little script I wrote to avoid copy-pasting the ffmpeg command from superuser every time I needed it. It converts a video to a GIF file by pre-calculating a good palette, then using that palette. Usage: ./to_gif input.mp4 output.gif The file t…
-
Gradle: what is a task, and how can I make a task depend on another task?
In an insane world, Gradle sometimes seems like the sanest choice for building a Java or Kotlin project. But what on Earth does all the stuff inside build.gradle actually mean? And when does my code run? And how do you make a task? And how do you persu…
-
Python Async basics video (100 million HTTP requests)
I found something difficult in Python, which was a bit of a first, so I wrote a whole blog series about it, and now a whole video: Slides: Python Async Basics slides Blog posts: asyncio basics, large numbers in parallel, parallel HTTP requests, addin…
-
Keybase chat bot in 10 lines of bash
I’ve been getting very excited about keybase.io recently, not least because it offers secure conversation, and you can have bots. I wrote a quick bot to simulate Arnold Schwarzenegger which I thought I’d share to demonstrate how easy it is. It is based…
-
Performance of Java 2D drawing operations (part 3: image opacity)
Series: operations, images, opacity Not because I was enjoying it, I seemed compelled to continue my quest to understand the performance of various Java 2D drawing operations. I’m hoping to make my game Rabbit Escape faster, especially on the Raspberry…
-
Performance of Java 2D drawing operations (part 2: images)
Series: operations, images, opacity In my previous post I examined the performance of various drawing operations in Java 2D rendering. Here I look at some specifics around rendering images, with an eye to finding optimisations I can apply to my game Ra…
-
Performance of Java 2D drawing operations (part 1: types of operation)
Series: operations, images, opacity I want to remodel the desktop UI of my game Rabbit Escape to be more convenient and nicer looking, so I took a new look at game-loop-style graphics rendering onto a canvas in a Java 2D (Swing) UI. For more on images,…
-
London Python Meetup January 2019 – Async Python and GeoPandas
It was a pleasure to go to the London Python Meetup organised by @python_london. There were plenty of friendly people and interesting conversations. I gave a talk “Making 100 million requests with Python aiohttp” (slides, Blog post) explaining the basi…
-
Run bash inside any version of Linux using Docker
Docker is useful for some things, and not as useful as you think for others. Here’s something massively useful: get a throwaway bash prompt inside any version of any Linux distribution in one command: docker run -i -t –mount “type=bind,src=$HOME/Deskt…
-
Windows missing from grub menu after Ubuntu install
Windows was missing from my grub menu after I installed Ubuntu. Fortunately it was fixed by running: sudo grub-mkconfig -o /boot/grub/grub.cfg and rebooting. Phew.
-
New home page design
After years of getting around to it, I have redesigned my home page at artificialworlds.net. It’s basically intended to make me look clever or productive or interesting or something. Alternatively, it gives you somewhere to find that thing you know I m…
-
Code Like a Girl T-shirts
There are lots of people missing from the programming world: lots of the programmers I meet look and sound a lot like me. I’d really like it if this amazing job were open to a lot more people. One of the weird things that has happened is that somehow w…
-
Poor performance in Chrome (especially on mobile) – caused by SVG background images
I have spent the last few hours investigating abysmal performance in my latest little game project Cross The Road. Firefox was fine, but Chromium and Chrome, especially on mobile, was rendering at about three frames per second. When I stopped using SVG…
-
Interesting Characters (UTF-16, utf-8, Unicode, encodings) video
In which I try to express the weird joy I felt learning about all the odd quirks of the simple task of turning bytes into characters, and characters into bytes. Slides: Interesting Characters slides
-
Elm makes me happy (updated for Elm 0.19) video
Series: Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test, Elm JSON With up-to-date examples, and anecdotes from the last couple of years of continuing to enjoy writing web pages in Elm, here’s a new version of my Elm advert: Slides: Elm ma…