Updated Jul 26, 2022

I work on some hobby projects from time to time. Although my GitHub tells most of the story, several projects exist elsewhere and deserve further explanation. That’s what this page is for.



Al Bhed is a fictional language from Final Fantasy X. It’s really just a substitution cipher over English, so “translation” is fairly trivial. The Al Bhed translator app concept has been done time and time again, and in fact, one of the first programs I ever wrote was an implementation of one in an obscure language called Just BASIC. I guess it’s the gaming equivalent of the Python IRC client.

My goal with AlBhedian was to attempt to make an “ultimate” one of sorts, with features collected from several others. The resulting app offers live two-way translation, an approximate pronunciation, and optionally renders in Al Bhed script. It’s built with vanilla JS and uses Tailwind for CSS.

Crimson Wars on the net

Crimson Wars on the net

Fansite for the obscure 1999 RTS game Crimson Wars. There are still few mentions of it across the web, and although I barely made it through the first two levels when I played it, I’m weirdly nostalgic for it. I asked the game’s composer for a high quality version of its soundtrack for the site, and he obliged. I still think that’s pretty cool, especially because the intro theme is such a banger. The site also provides a virtual machine so Crimson Wars can be played on modern PCs.

The site was originally made with ASP.NET Core in 2017, but soon after migrated to Jekyll and then later to Eleventy.


npm version badge

Plugin for the Eleventy static site generator that runs PurgeCSS on the generated site. PurgeCSS is a tool that removes unused CSS selectors from stylesheets, which can significantly reduce the output size of websites.

I originally made it in order to reduce this very website’s Tachyons stylesheet from 72 KB to 4 KB, but figured it might be useful to others in a similar situation. It’s listed as a community plugin on

g_ (for games)


My virtual video game bookshelf. The goal of g_ is to be a central database for my game collection, while being more visually appealing than a spreadsheet. I originally made it after an update to Playfire removed many of its customization options. I’ve since tried several managed services that focus on similar things, but none of them have really hit the sweet spot for me.

g_ is a very old project that’s been through a number of iterations.

  1. Started out as a single PHP page backed by a MySQL database
  2. Rewrote it as an AngularJS page with a REST API (still PHP)
  3. Rewrote the API in ASP.NET Core
  4. Rewrote the frontend in Vue
  5. Replaced the backend entirely with a static CSV file and redesigned the frontend (still Vue)
  6. Replaced the CSV file with Google Sheets proxied through a serverless function

The weird project name comes from the table prefix used in the original database (back then, I had only a single database available for all my projects), and it stuck. I tend to rewrite g_ every few years to learn new technology. While the first few iterations were all about adding features, the last few have been about taking them away. The current version is closed source for the time being, but that’s mostly because I’ve been lazy about keeping secrets out of the repo.

Hammerhavnens Bådfart

Hammerhavnens Bådfart

If you ever visit the Danish island of Bornholm, you can go on a short boat ride from Hammerhavnen near Hammershus. It’s a nice little trip along the rocky coast, which is a geological feature unique to the island in an otherwise flat country. It’s mostly a tourist attraction.

I personally know some of the people running the operation. One of them is fairly tech savvy, and for many years they’ve had a web page with prices and contact information. It was originally made with Microsoft FrontPage for a different era of the web that didn’t consider mobile devices a serious design target. In the last decade, those have completely dominated the web, and using one to read the page’s tiny serif font required zooming and panning. Clearly, the design was due for an update.

I offered to help out and redesigned the page to be mobile-first. I added some niceties that you’d expect, like a Google Maps widget for directions, a Facebook widget for status updates, and expanded information in multiple languages. The page is built with Eleventy and Tailwind.

An online formatter for JSON and XML. I started with the domain name and had more ambitious plans for it, but as is often the case with hobby projects, they never materialized.

Still, it has a single (somewhat unique) feature: it can auto-complete truncated JSON. At a previous day job, we had a request log table with columns that were rarely large enough to contain the entire payload, and trying to manually close partial JSON is a pain, so can do that for you.

The app used to be written in Vue, but later helped me learn some React/Redux, and I then rewrote it (again) in plain JS with RxJS.

TalkDirty2Me / @NormalCommenter


A long time ago, someone discovered that on certain video websites, people leave unsavory yet very entertaining comments. This is a website that displays random samples of these through web scraping. Eventually someone suggested that it should have an appropriately sexy soundtrack. TalDirty2Me consists of a small SPA that talks to a Node.js serverless function which performs the scraping and stores results in MongoDB. There’s even permalink support for when you find a particularly funny one!

This project used to be open source, but its subject matter doesn’t exactly scream professionalism and at some point I was overcome with self-doubt and privated it. I feel pretty safe mentioning it here though, because who’s going to read this far down?

There’s also the @NormalCommenter Twitter bot that posts content from the site a few times a day. That’s a Python app using Tweepy and is still open source, as it’s divorced from any salacious details. Its Twitter presence, however, is not.



A tiny website I made as part of Netlify’s Dusty Domains campaign in December 2021.

All it does is take a direct link to a tweet and render a page with an embedded version of that tweet. As it turns out, embedded tweets don’t respect account blocks, so for a blocked user it’s effectively like viewing the tweet in incognito mode.

Open up a tweet in your browser, replace the t in with a 7, and a Netlify function takes care of the rest. It’s not much, but hey, it’s for charity!

Aplo Events (discontinued)

Aplo Events

Event planning app that I made with some friends; I mainly did node.js backend stuff. It consisted of an administration web app for setting up events, and a mobile app for subscribing to them. Any updates to an event were automatically pushed to subscribers to easily communicate changes.

We started building it in our spare time around 2018 and launched it in late 2020. While it was fun to work on as a social activity, the project lost steam once it actually launched, and we discontinued it in January 2022.