Some Thoughts On Adventurer's Codex

Last year Adventurer's Codex reached its 8th birthday. It's a milestone that I honestly didn't believe we'd ever hit, both because eight years is a long time and because—if I'm honest—I've never been sure how long this little project would actually last.

These days Adventurer's Codex is primarily built and maintained by three people: two are founders (including myself) and the third is a friend of ours, and as with any long-lived side-project Adventurer's Codex has occasionally suffered a lack of interest or enthusiasm from all of us. At the beginning the three founders met every week (sometimes twice a week) to build new features and plan the roadmap, later we rarely met at all, and sometimes nearly a year could go by before any of us even thought about Adventurer's Codex at all.

These days though the app is alive and healthy and with the support of our amazing patrons on Patreon we continue to make improvements and release features at a steady pace, and this year we're set to release a slate of amazing features that eight-years-ago-me could only dream of.

It makes me very happy to say this: the future is very bright for Adventurer's Codex.

More Scripting Nonsense: Rocket Edition

I was browsing my Github Gists the other day, as you do, and I came across my first one published back in 2015: Rocket.py. This script was one part of a rocket trajectory simulator I built for a class in college. Now I don't remember much about that project, but I do remember that the simulation never actually worked—or rather that it never worked properly.

A screenshot of the trajectory over time

So since I had some idle time on my hands, I decided to spend an evening and take another stab at the project. These days I'm a far better programmer but I remember next to nothing about rocketry, and so I added a bit of a challenge for myself: try to look up as little as possible.

About two hours later I had this, a new rocket simulation that seems to produce accurate values (if you neglect drag—more on that later). Now my first simulation only output a set of numerical values for each second of flight, and while those numbers were useful for the class, they're not very interesting to look at so I added a little visualization (drawn with Python's turtle graphics).

Each phase of the flight is color-coded: green for powered flight, blue for cruising, and red for descent, and the flight statistics seem accurate enough to me when I plug in the numbers for the first stage of SpaceX's Falcon 9, so that bodes well. And at the end of each flight you get a summary readout in the terminal.

The code is pretty full-featured, and since it uses turtle graphics (and my other drawing project) it can even save the trajectories as EPS files for easy printing & saving. You can customize the launch angle, the burn time, specific impulse, fuel and payload weights and even the screen refresh rate using the many CLI flags. The script requires nothing but Python3.10 or later (with the tkinter module included of course), and so it's easy to play with if you're interested.

All in all it was a fun evening project, and I was especially surprised to find that I remembered a lot more of the intricacies than I expected. I still had to look up the equations for specific impulse, but that was it really. The kinematics stuff was beaten into my head in school so I doubt I'll ever forget that. Really the only thing that gave me any trouble was the drag—which I decided to omit anyway.

A screenshot of the stats of the flight in the Terminal

I had initially wanted to add the drag based on the mach number, but my memory failed me on how to do that and late-night searching only threw me down a rabbit hole of further questions. Past me knew a lot more about this stuff than current me does. In the end the drag was a nice-to-have feature anyway and by the time I got to that point it was nearly one in the morning, so I just declared victory and went to bed.

I have no real point to this post other than to show how much (and how little) I remember about rocket mechanics after all these years and to show off a cool bit of scripting. It's toy projects like this that got me interested in programming and they can be a really fun way to spend an idle evening.


Disclaimer: I think the values in the simulation make sense, but I didn't spend too much time validating them. If there's any real rocket people out there who find issues with my results, then I'm sorry; now you know one of the reasons I didn't go into aerospace.

A Cross Post Life

I use Pine.blog for microblogging, and following the Post Once Sindicate Elsewhere strategy, I like to have those updates automatically cross-posted to other platforms like Micro.Blog and Twitter so that they gain a larger audience, and so that anyone who wants to can follow me no matter which platform or software they prefer.

Obviously I'm not manually posting to five different sites—that's what computers are for—and so I need some way to automate that work. Micro.blog makes most of that very easy since it can automatically ingest content from an RSS feed and not only add it to my microblog there, but also cross-post it to Twitter. That only leaves Mastodon out in the cold.

In recent months there's been a lot of new activity on Mastodon and I've really enjoyed seeing a larger segment of people discover such a great open-web focused tool. It was well past time that I add Mastodon to my cross-posting workflow, but there was a problem: Mastodon's posts are text only (with links at the bottom—like Twitter), but Pine.blog outputs HTML (or Markdown). I tried several tools, but each one would mangle my posts or just include the raw HTML as the text, neither of which was an acceptable solution. I needed something custom.

So I wrote a script! It runs every few minutes on the Raspberry Pi under my desk checking for new posts from Pine.blog and posting them to Mastodon. It starts with the raw Markdown, parses the HTML, strips and appends links, and even keeps track of what has and hasn't been posted before. It's a pretty handy script and I encourage you to check it out if you need something like it. I even made use of one of Mastodon's very handy features: Idepotency-Key which helps prevent duplicate entries from being posted in a short period of time.

As of today my posts are now Live On Mastodon, so you can rest assured that if you follow me there you aren't missing a beat. You'll get all... five posts I've made this year!

Automate the Boring Stuff with ChatGPT

There's been a lot of buzz around ChatGPT lately and most recently the discussion followed Tom Scott's video in which he describes how he was able to use ChatGPT to generate working code to solve a problem—including asking it to fix several bugs with its own implementation—and then write an ad spot for him.

In the video Tom expresses his disquiet with the ongoing advances in machine learning and goes so far as to predict that "everything is about to change" in the same way "everything changed" once smartphones came along in the mid-aughts.

I've enjoyed using text-to-image AIs like Midjourney for a while now, but I'd largely stayed clear of things like ChatGPT. Tom's video though got me curious, and so I logged on to see what all the fuss was about.

tl;dr I was pleasantly surprised.

Introducing DeveloperGPT

There are so many things that developers do day-to-day that are at the same time important and dreadfully dull, and like many devs I find myself searching for ways to both save time and automate away as much menial work as I can. It's one of the reasons I love Bash so much. And while at some companies such work is often pawned off to junior developers in need of experience, as a one-man shop I have definite use for a sort of algorithmic junior developer who could take on some of that less-engaging work. And after watching Tom's video I started to hope that perhaps ChatGPT could be exactly that.

In order to see if ChatGPT could fill this role, I did what anyone looking to hire a junior developer would do: I gave it an interview.

Giving ChatGPT a Coding Interview

In total I gave ChatGPT five questions to answer. These questions were almost entirely development related and spanned the range of what I'd expect a junior developer to be able to do given time and access to the internet.

Test 1: Django Unit Tests: ✅
Prompt

write for me 4 python django unit test cases that each make a request to /api/users to do the following: create a user record, fetch a user record, delete a user record, and update a user record. Make sure to include partial update using the PATCH method. And be sure to use Python 3.10 compatible code.

Response

Sure, here are 4 Python Django unit test cases that cover the create, fetch, delete, and update functionality of the /api/users endpoint. These test cases assume that you have a Django application with a User model and a corresponding API viewset that handles CRUD operations for users.

from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from .models import User

class UserTests(APITestCase):
    def test_create_user(self):
        """
        Ensure we can create a new user object.
        """
        url = reverse('user-list')
        data = {'username': 'testuser', 'email': 'test@example.com'}
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(User.objects.count(), 1)
        self.assertEqual(User.objects.get().username, 'testuser')

        # ... (mine: omitted for brevity) ...
Complete Generated Source Code→

These test cases cover the basic functionality of the /api/users endpoint and ensure that users can be created, fetched, updated, and deleted. They also cover partial updates using the PATCH method. Note that you will need to update the URLs used in the reverse() function to match the actual URLs for your application.

The first test I gave ChatGPT was simply to write a suite of unit tests to verify that a Django User REST API allowed for standard CRUD operations. In general it did very well, even including a test for partial updates via the PATCH method, but it did assume I also wanted it to use Django REST Framework and its implementation of the PUT and POST tests don't include all the correct values. The assumption to use DRF is a fair one, but the other mistake is a red flag.

Still though it would have saved me from writing the bulk of the code—assuming it works. I didn't actually test the code.

Test 2: S3 File Clean Up: ✅
Prompt

write for me a python function that pulls data from a CSV file containing files that need to be deleted from S3 and that then deletes each file. Include error handling if a file cannot be deleted and return a list of all files that were and were not deleted. Also include suite of three test cases that ensure the function works, that errors are caught, and that the return value is two lists.

Response

As an AI language model, I don't have the capability to access files from your computer. However, I can provide you with a sample code that demonstrates how to delete files from S3 using the Boto3 library in Python.

    import boto3
    import csv

    def delete_files_from_s3(file_path):
        # Initialize S3 client
        s3 = boto3.client('s3')

        # Read the CSV file containing file names
        with open(file_path, 'r') as f:
            reader = csv.reader(f)
            files = list(reader)

        # Delete each file from S3
        deleted_files = []
        failed_files = []
        for file in files:
            try:
                s3.delete_object(Bucket='your-bucket-name', Key=file[0])
                deleted_files.append(file[0])
            except Exception as e:
                failed_files.append(file[0])
                print(f"Failed to delete {file[0]}: {e}")

        # Return the list of deleted and failed files
        return deleted_files, failed_files

The function takes the path of the CSV file containing file names as input. It reads the file using the csv module and deletes each file from S3 using the delete_object method of the Boto3 client. If a file cannot be deleted, it is added to the list of failed files and an error message is printed. The function returns two lists, one containing the names of deleted files and the other containing the names of failed files.

Here are three test cases to ensure that the function works correctly:

    # Test case 1: All files are deleted successfully
    deleted_files, failed_files = delete_files_from_s3('file_list.csv')
    assert len(deleted_files) == 5
    assert len(failed_files) == 0

    # Test case 2: Some files cannot be deleted
    deleted_files, failed_files = delete_files_from_s3('file_list_with_errors.csv')
    assert len(deleted_files) == 3
    assert len(failed_files) == 2

    # Test case 3: CSV file does not exist
    deleted_files, failed_files = delete_files_from_s3('nonexistent_file.csv')
    assert len(deleted_files) == 0
    assert len(failed_files) == 0

In the first test case, all files are deleted successfully and the function should return a list of 5 deleted files and an empty list of failed files. In the second test case, some files cannot be deleted and the function should return a list of 3 deleted files and a list of 2 failed files. In the third test case, the CSV file does not exist and the function should return empty lists for deleted and failed files.

The second test involved writing a simple Python function to clean up stray files in S3 using a list provided by a CSV file. ChatGPT scored pretty well here. It included the asked-for error handling and API interface, but its tests are not correct. The third test especially would crash when the file was opened, but the script does not account for that.

Once again I didn't run the code, but assuming the use of boto3 is anywhere close to the actual API, it was faster to have ChatGPT write the code than for me to do it.

Test 3: Two-Column Layout in React: ✅
Prompt

write for me a JSX file using React that displays a two column layout with a list of appointments in the left column and the appointment details on the right columns. Use the react-bootstrap library for styling and no other libraries. Assume the list of appointments are passed into the component as props.

Response

Sure, here's an example of a JSX file using React that displays a two-column layout with a list of appointments on the left column and the appointment details on the right column, using the react-bootstrap library for styling:

import React from 'react';
import { Container, Row, Col, ListGroup, Card } from 'react-bootstrap';
    function AppointmentList({ appointments }) {
      return (
        <Container>
          <Row>
            <Col sm={4}>
              <ListGroup>
                {appointments.map((appointment) => (
                  <ListGroup.Item key={appointment.id}>
                    {appointment.title}
                  </ListGroup.Item>
                ))}
              </ListGroup>
            </Col>
            <Col sm={8}>
              <Card>
                <Card.Body>
                  <Card.Title>Appointment Details</Card.Title>
                  <Card.Subtitle className="mb-2 text-muted">Title</Card.Subtitle>
                  <Card.Text>
                    Lorem ipsum dolor sit ...
                  </Card.Text>
                  <Card.Subtitle className="mb-2 text-muted">Date</Card.Subtitle>
                  <Card.Text>
                    January 1, 2023
                  </Card.Text>
                  <Card.Subtitle className="mb-2 text-muted">Location</Card.Subtitle>
                  <Card.Text>
                    123 Main St, Anytown USA
                  </Card.Text>
                </Card.Body>
              </Card>
            </Col>
          </Row>
        </Container>
      );
    }

    export default AppointmentList;

Here, the AppointmentList component takes in a prop called appointments, which is an array of appointment objects. The left column displays the list of appointments using the ListGroup component from react-bootstrap, and the right column displays the appointment details using the Card component. You can customize the appearance of the list and the details as desired by modifying the styling properties of the components.

This test surprised me. I had ChatGPT create a two column layout in React for an appointment management application. Explaining a complex UI in words is difficult without visual aids, so I expected some misunderstandings, but it powered through and did exactly what I asked. It failed to add any navigation to the app nor did it use the data inside of the appointments array, so it's not really usable as a component, but it's certainly a good starting point and one I'd be happy to use as a starting template should I ever need such a thing.

That said, without tooling to make ChatGPT more available during development it would be easier to just write the layout myself.

Test 4: RSS Parsing in Swift: ✅
Prompt

write for me a Swift RSS feed parser that takes in a string of XML data and produces a Post object with a title, description, pubdate, and enclosure fields. Use only the swift standard library

Response

Sure, here's an example Swift RSS feed parser that uses the Swift standard library to parse a string of XML data and produce a Post object with a title, description, pubdate, and enclosure fields:

Check out the code here. It's very long.

This last test was by far the most involved and open-ended. I asked ChatGPT for a Swift-based RSS Feed Parser and it certainly gave me something close. It used the correct Foundation APIs and even understood the need to concatenate the element text, but it failed to generate a valid test fixture (or even valid XML) and there's absolutely no error handling to speak of.

That said, it did produce valid XML once I called it out and there's something to be said for such humility. I would certainly use this code as a starting point if I needed to build something like this in the near future.

Should I Hire ChatGPT?

In general I'd say it performed fairly well as a developer, though its ability to imitate bloggers is very lacking. For the fifth test I tried to have it write this post, but the output didn't sound like me at all and it kept trying to sell itself using features it doesn't have. Overall I'd give ChatGPT a tentative recommendation and would probably hire it contingent of course on a 30-day probationary period to see if it can keep its tendency toward exaggeration in check.

It's Not an AI Apocalypse, It's Better

I don't really buy the idea that ChatGPT and its ilk will do much to disrupt our everyday lives in a dramatic way. I do however see both it and it's text-to-image cousins as being more and more useful as tools to automate away so much of the boring or tedious work of being a developer or a person on the internet in general.

Like I said, I already use Midjourney to create images for my D&D campaign and those images have had a significant impact in improving the gaming experience. We were never going to contract an artist for four once-a-week landscapes and so economically speaking the AI has had no external effect. Our D&D games just got marginally better. And that's great!

ChatGPT seems to me like it could be yet another advantage that enterprising developers would use to enhance their own productivity. If something like ChatGPT ever shipped inside of an IDE like Xcode, I could imagine a whole suite of amazing features (automatic complex refactors, automating test writing, and more). GitHub's Copilot does some of this, but it's in its infancy. In general I'd expect to see tools like ChatGPT improve a developer's productivity and let us focus more on the design and nuances of our code rather than on the tedious implementation; and for that I am very excited.

My Favorite Books From 2022

As I've mentioned before, 2022 was a watershed year for my reading capacity, and while it is probably a little late in the new year for a retrospective post, I did want to take some time and expressly call out, and recommend, a few of the books I read last year.

In no particular order, my favorite books last year were:

Never the Wind, Francesco Dimitri

This book was an absolute joy to read. It's a coming-of-age, ghost story set in nineties Italy, and it is just so well written and charming that I found myself spellbound by every page. Give this one a go if you're in the mood for some excellent fiction.

What Diplomats Do, Brian Sir Barder

This one I read for a yet-unannounced project I'm working on, and while I was hoping the book would be useful to me, I was not prepared for just how much fun it would be to work through. The author, a former U.K. Diplomat, spends as much time explaining the intricacies of overseas diplomacy as he does fictionalizing his time abroad and telling simply incredible stories about how and why certain diplomatic agreements were reached.

A Farewell to Arms, Earnest Hemingway

I read a lot of Hemingway last year, and this one was my favorite. A classic for a reason, and an excellent read. I did also like The Sun Also Rises.

The Matter of the Gods, Clifford Ando

This one, I'll admit, was a bit tough to get through. It's a treatise on Roman Religion, but it is written by and for historians and classical theologians. I picked this up after scouring the citations for one of my favorite blogs, and while the book assumes you've already read the ancient classics, and the medieval christian rebuttals to those texts, (which I have not) it was still an interesting read. Ancient mysticism is fascinating to me, and if the topic sounds enticing to you then I'd recommend skimming through this one. A deep read might be a bit too intense.

Existential Physics, Sabine Hossenfelder

Sabine is one of my favorite YouTubers, and her book is basically just more of the same. The book is a fun romp through many different parts of Theoretical Physics and both acts as a guide to what modern science can, and cannot, hope to explain. It's a good reminder, from the perspective of a physicist, that while science has given us the modern world, it cannot ever hope to solve some very important questions.

A Very Special Anniversary

Today marks the 10 year anniversary of the first post on this blog. It's been a wild ride writing for here, and I never would have predicted that after all this time I'd still be doing this.

For posterity, here are a few links that show how the site itself has evolved over the intervening years, though the site itself isn't really the most important thing to celebrate today.

Really today is special because it gives me the time to reflect on what writing for this blog has taught me. Since writing the first version of this blog I went from being a college student interested in programming to being a professional and independent software developer. In many ways I have achieved what I set out to do all those years ago, though of course not in the way I originally envisioned.

Blogging has been a truly transformational hobby for me. Like many people, I write to think. The process of making something —writing in this case— helps me solidify my own opinion on a topic and putting my thoughts to words helps me consolidate and organize them in a way that no other way does.

This blog has also served as an unofficial resumé for me on several different occasions. I often tell people who are just starting out to set up a blog themselves. You don't have to build your own; you can just use Wordpress. But having a public presence filled with your own complex thoughts on a number of topics helps prospective employers understand your inner machinations. It shows them that you know how to think just like a resumé would. My blog and public Github projects have gotten me just as far (if not further) than an actual resumé.

More than that though, blogging here has given me a chance to get my thoughts out into the world. I'm incredibly fortunate that my writing is occasionally interesting to others and that a handful of my posts have gone micro-viral thanks to sites like Hacker News. To everyone who clicked on one of my posts: thank you. To all of you who actually read the post: thanks even more.

The internet continues to be a force we barely understand, but here I've gotten the chance to experiment, try things, fail, and get back up again. And I've tried to bring you all along with me as I've found out the answers to all sorts of questions.

Thank you all for reading, and here's to another ten years.

On The Web, The Best Outcome Is Email

Like many out there, I've been thinking a lot lately about the Open Web. As many readers are no doubt aware, the chaos at Twitter lately has driven a lot of people off the platform and onto more fledgling alternatives. Mastodon in particular has seen huge spikes in growth recently and I doubt that growth will slow until either the platform succumbs to the load or the chaos at Twitter ends and users return to the platform.

A chart of Mastodon's recent user growth with large spikes around significant Twitter changes
Image credit to Esteban Moro

While these recent developments have been great for Mastodon as a platform, only time will tell if the Fediverse at-large will rise to the occasion. There's been a lot of talk about Mastodon.social the website, but the real story is the sudden surge in usage of the underlying ActivityPub federation protocol that powers it. Very briefly, ActivityPub is a protocol that allows for disparate social media sites to talk to each other, sharing likes, posts, follows, and other social media interactions. Widespread adoption of such a standard on the Web would make it possible to finally break the tyranny of the Network Effect and allow for a wide swath of new Social Networks to emerge. These new networks would be free of the burden to attract a "critical mass" of users to remain viable as every user would be able to communicate and interact with users of any other ActivityPub-capable social platform.

The hurdle that Mastodon —and the swath of other ActivityPub sites like it— must overcome is that they all collectively must reach a critical mass in order to become viable in today's siloed social media landscape. In order to free us from critical mass, Mastodon must attain it.

The fog of current events leave many questions so far unanswered, and only time will tell what path we choose to follow, but for now it is not known whether or not Mastodon will ever reach this critical point and become a sizable player in the social media space. Tumblr's plans to enable ActivityPub support will certainly help the broader Fediverse grow, but I doubt it will be enough on its own to push the project over the edge and into relevancy with the general public, but only time will tell.

Sailing Rough Seas

I've been a vocal supporter of the Open Web for over a decade, but as anyone with a strongly-held and niche belief knows, it can be exhausting to see the world march on impartial to your goals. At my worst, I've grown rather jaded about the possibility of a truly Open Social Web. Like I'm sure many dentists, if given the chance, would choose to wave a magic wand and force all of us to floss our teeth more, I would —if I could— force social media sites to interoperate using a freely open standard, but that's just not how the world works.

For years, I've thought the momentum had settled and that we would —given no other outside forces— be on a path to ever-increasing centralization on the Web, both on and off social media. There are of course many technical solutions to centralization, but I'll admit that I believe the forces of power, control, and profits too strong a wind for the little boat of federation to sail. The only other boats to make the journey set out long ago: both web browsers and email were products of another era on the Internet and I doubt they could come up in this one.

I now hold that the most likely path to achieving a truly interoperable Open Web is through government regulation. I've written before about how Congress is mulling over the idea of mandating Social Media interoperability and I support that effort, but regulation is a difficult stick to wield. The backbones of the modern Web: the web browser and email provider, didn't need laws to force them into interoperability. They were designed as such from the start, and that still is the best way for a technology to progress. Like the situation with phone networks last century, regulation might be the only force powerful enough to push a Social Web forward. I still hope that social media follows a similar path and that we all hold hands and merrily walk into the sunset of a federated world, but I believe that things are likely too far gone; that it never can or will happen like that again.

I have grown fairly confident in this belief, and like any belief too strongly-held it blinded me to what happened next.

An Impetus of Change

Elon Musk's chaotic and seemingly-inept takeover of Twitter shocked many people into giving up on the platform. Thousands upon thousands fled to whatever refuge they could find. Mastodon was an obvious choice for techies to flock to, but its Mastodon's growing segment of less technical users that interests me. Perhaps this new flood of growth holds —and assuming the platform in general and mastodon.social in particular can withstand it— there exists a real possibility that the platform gains critical mass and enters public awareness. It may already be arriving there. The San Diego Union Tribune ran a story last sunday (in print) aiming to help elderly users navigate the migration to Mastodon. That was something I certainly hadn't seen coming.

I'll say this: if the trend holds and Mastodon does continue growing, and if it does gain a foothold as a small but nevertheless notable social platform it would mean that Elon fumbled his way into giving the Open Web the single biggest gift it has received in a decade and certainly the most unintentional one. It would be a poetically ironic moment indeed.

Again though, I'll admit that I am skeptical such a trend will hold. Social media platforms come and go and small ones rarely stick around very long. Mastodon will of course still exist —the Fediverse cannot be killed— but falling out of public consciousness would be a de facto death.

A Sobering Reality

Whether the crazed antics of one man can disrupt the seemingly natural flow of centralization on the Web is doubtful, and I still believe that is more likely than not that regulation will be required to force social media companies to interoperate. What's more, it isn't clear that such regulation could or would come to be in the United States. It might; it might not. The future, as always, remains unclear.

That said, Mastodon (and ActivityPub more broadly) have the potential to remake social networking on the Web in a magnitude that I don't think many fully realize. Indeed some do realize it, but they are often gripped by a naïve optimism I simply cannot share: a blue-sky fantasy that could never come to pass. The broader narrative though seems to take the opposite view: that even if Mastodon (and the Fediverse) were to become relevant, that it simply wouldn't matter very much. They argue that federation and decentralization simply cannot compete with monied interests and that over time the Open Web would re-centralize into one or two big players. Indeed I find myself sympathetic to this view, even though I don't like its conclusions.

I, however, would like to offer a third option for the future of the Social Web: one that is both radically transformative and yet utterly boring at the same time.

What If It's All Just Email, Man?

I love email. I genuinely do. I love it in a way that often raises eyebrows among nearly everyone I encounter, even my fellow techie-types. Why do I love email? Because it, like the Web itself has remained true to the promise of the Internet even after it made unavoidable contact with the real world. Let me explain:

Email, like the Web itself, is a federated system. No one controls the whole thing and while there are a few very large players in the Email space, none of them has the ability to direct its evolution outright. Gmail is huge and partially non-standard, but the system still works. Since its inception, anyone with an email address has been able to email anyone else so long as they know or can guess the email address they want to reach. It's not a perfect system, no (see: spam, blocklisting, etc) but it's damn good and email has utterly transformed how the world does business and it has democratized communication more than almost any other Internet technology to date.

What's more, barring some technical hurdles, anyone with some funding can start an email hosting company. And while it's not practical for single individuals to run their own email server today, it is technically possible and many people indeed do just that. Every year new Email apps and services emerge while others die. The ecosystem is dynamic, healthy, and growing with every new entrant to the Internet. The standard is open, widely available, and —unlike social media accounts— truly universal. Nearly everyone on the Internet has an email address, and that seems set to be true for decades to come.

We need no better signifier of Email's complete and utter domination today than the simple fact that email clients come installed on almost every operating system known to man. That's only possible because of its decentralized, universal, and long-lived history. I long for the day that an ActivityPub-powered "Social" app ships with macOS and Windows like Mail does today.

A truly federated macOS dock When a mail-like "Social.app" ships on macOS, we will have won.

Now, most people I talk to about this tell me they hate their email. It's a spam and junk-filled box full of nothing but coupons, ads, and check-ins from their boss. And I agree. It is just that. Indeed one of the problems with decentralized and federated technologies is exactly that: they're filled with spam, junk, and mixed uses. But that mundanity is the true magic.

Email has so throughly engrained itself in the modern world that we've stopped seeing any of the good it has brought and only the bad; a truer mark of success there isn't (see: liberal democracy, the post office, web browsers, the phone network, etc).

Should federated social media succeed: whether by natural growth, by chaos at Twitter, or by eventual regulation, its ultimate fate can and should be the fate of email. If somehow social media is largely run by enormous companies surrounded by a large collection of smaller providers all talking to each other over the same interoperable, open protocol then the Open Web will have won. Of course this won't be a win for those who imagine a truly decentralized Internet devoid of large actors, but it will be a success as judged by the best examples the Internet has so far given us.

The Social Web of the future isn't complete decentralization or a network of innumerable federated micro-instances. It isn't a Web powered by anonymous cryptocurrencies. It may yet be the Web we have now, should regulatory attempts fail and natural forces be insurmountable. But if things go right and the stars align the Social Web could become like Email, and that would be transformationally mundane.

A Drawing Every Day!

Today's Drawing of the Day. Yes it is live

I love to just mess around with computers. It's a fun way to spend an idle evening, and last night I set up a little script that could generate random line drawings using my little pdraw project from a while back. The script generates a new image every day and places it in a web-accessible place that I can then link to. This morning I finished up my little project by adding another script that can generate an RSS feed for the list of images and voila!

You can find each day's Drawing of the Day here. The page also has its own, dedicated RSS Feed so give that a follow if you're interested.

The script to generate all of this was a fun little experiment that involved pdraw, imagemagick, and xvfb. As with all bash projects it involves no shortage of little hacks (including but not limited to a manual, string-joining, feed-generator function). Overall its a great example of just how powerful simple tools can be.

As an aside, it was projects like this that were my original introduction to programming and to this day they continue to be one of the most fun parts of it for me. Don't get me wrong, I do enjoy building fully-fledged web apps, but throwing little scripts together is also a ton of fun. Hopefully you all enjoy these daily line drawings as much as I enjoyed making them available.

Reading Milestones, Attention Spans, And Cool Charts

Today marks a special occasion. According to my knowledge, I have read more books in 2022 than in any other single year of my life. Better yet, the previous three years are a high-water mark over most of the previous decade.

A chart from the iOS App Book Track depicting books read per year. 2022 is the highest.

Charts via the excellent app Booktrack for iOS/macOS

As a kid I considered myself an average reader. In fact book reading was one of the very few homework assignments I occasionally completed. In high school I read a fairly significant amount of Science Fiction and Fantasy as well as the occasional book of Physics or Philosophy. Once I got to college though, my recreational reading slowly went by the wayside until the end of my senior year and the year that followed. Over the intervening years I've slowly recovered my habit of reading regularly. However, as you can see something changed in 2020 (something quite obvious to everyone).

With significantly more idle time during the early COVID lockdowns, my recreational reading skyrocketed. I was reading at a pace my adult self had never known. In fact, until recently I'd assumed that 2020 would likely be a personal record, and one that would stay unbroken for a while. That turned out to not be the case!

Tracking My Reading

I recently described how I've tracked my steps for over eight years, but until recently I'd never thought about tracking my reading habits. I tried using Goodreads for a while, but the whole social-network component of it never sat well with me. Additionally, I'd always wanted to have more access to statistics about which books I've purchased and which of those I've actually read. Goodreads can do this, but I never liked the feel of it. Enter Booktrack!

More charts A chart from the iOS App Book Track depicting a summary of my reading lately. A chart from the iOS App Book Track depicting books read per day this week.

Booktrack is a really cool indie app that lets you keep track of your entire book collection. Importantly it is available for both iOS and macOS, and it provides me with some oh-so-lovely charts and statistics. I don't really have much more to say about the app other than it's great and you should try it, but yeah. It's great, and you should try it. What's more you'd be supporting an indie developer by doing so.

Deliberate Acts of Reading

Since graduating high school over a decade ago, my reading preferences have largely pivoted away from Sci-Fi and Fantasy and toward Non-Fiction, especially history, politics, and public policy. That said my love of Fantasy never died, I just stopped reading it; instead I listened to it.

Now, I consider listening to audiobooks to be fundamentally equivalent to reading physical books in almost every way. For me though, the act of sitting down to read a book, as opposed to listening while doing something else, is a much more conscious and deliberate act. I've recently gone back to reading Fiction again. I've done this partly to give myself a break from Non-Fiction, and partially to rediscover the joy of actually reading fiction with my eyeballs.

I still love listening to books and I will of course continue to do so, but sitting down with a book is a fundamentally different activity than listening to one. Like many people I've found my own attention span shrinking as I spend more and more time online. I think that's a bad thing, and I think sitting down with a book helps me re-expand that attention span where listening to a book doesn't (for me at least). These past three years then have been not just a personal milestone for me in terms of my ability to read more books, but they've also been a conscious exercise in expanding my own attention span and improving my ability to think. Both of those are important metrics to track in this distraction-filled time we live in.

Watersheds

I know many people might look at my reading statistics and laugh. Nineteen books might not seem like a lot to some, but it's a ton for me, and while I'll never truly be able to recover my book-reading numbers from before 2020 — and especially not from a decade ago — I am fairly confident that I'm correct when I say 2022 has truly been a great year for my reading habits. Let's hope the trend continues and I don't fall of the train anytime soon.

Stargazing Pictures

Last night I went stargazing with a few friends, and while the weather did conspire against us we did get a few good chances to see the stars through a friend's telescope. The photos through the telescope are no where near as good as the actual sights, but that's largely because we didn't bring any camera equipment. With time, practice, and proper gear, we'd likely have done a lot better. That said, w e also took some pictures just with our phones and the results were a lot more impressive than any of us expected.

A shot of stars through the lens of a telescope

This is the best shot I could get through the telescope eyepiece with my phone. The actual view was great, but the pictures: not so much.

A shot of a starfield and clouds through my iphone camera
A shot of the night sky with just my iPhone on a 10-second exposure.
Another shot of a starfield and clouds through my iphone camera
Turns out that the cameras on our phones are very, very good.