My Favorite Tech Media

I have a work trip to San Diego and all my other blog post ideas need more time, so here’s a quick list of my favorite tech media. I stick with these outlets for their staunch commitment to quality and independence; they’re all owned and run by excellent people with that ineffable and intangible quality of taste.

These lists aren’t in any particular order.

Websites

Podcasts

I Might Be a Runner?

And it’s a potentially positive identity crisis.

Throughout college, I ran two Turkey Trots in St. Paul, and two Goldy’s Runs at the UMN Twin Cities campus. None of those 5K races were completed without pauses to walk, and I don’t believe any of them were finished in faster than 35 minutes. I played baseball, which famously doesn’t involve much beyond sprinting. I never thought I’d catch this particular fitness bug.

A couple of weeks ago, I ran a sub-30 minute 5K in my neighborhood. Last Friday, I ran 5 miles on 0.75-mile intervals, with some walking between them. Running has materialized as the next addition to my Year of Fitness, stacking onto my workouts in Fitbod and coinciding with a diet shift that’s proven to be successful.

I’ve toyed with running on and off over the years. I spent several months in San Francisco grinding out 20 or 25 minutes on a treadmill at the university gym. It was fine. Uninspired. Boring. It never stuck.

Since committing to running outside and using interval training on my Garmin watch two months ago, I’ve made incredible strides1Hah, puns. in my running ability. The ensuing success required two mental adjustments:

  1. I can focus solely on my running and not compare it to others.
  2. I already walk thousands of steps each day. Why not earn some of them while running?

I no longer conceive of one mile as the longest feasible distance to run without stopping, and I look forward to my lunchtime jogs as much as I ever desired my lunchtime walks over the years. Running is no longer a terrible burden. Instead, it’s a great solo activity that helps my day feel complete.

I have no ambitions for running. I’m not attempting to train for a race of any particular length, besides an informal one-mile trial against my sisters that we’ve often discussed. Three or four times each week, I check how I’m feeling and decide what kind of running workout to attack: long intervals at a slower pace, or short intervals at a fast pace. I’m confident I can do either combination for at least 3 miles.

Running has, most importantly, opened up my own self-conception to believe in what’s possible with dogged determination. By competing against myself and seeing tangible improvement, I’m encouraged to keep going. I wouldn’t consider myself a runner quite yet—we’ll see how I handle winter—but it’s become a focal point of my 2025 theme, and that will keep it around for a while.

  • 1
    Hah, puns.

Omelet

I nearly made an omelet.1This post is excerpted from a letter I wrote to a friend on a whim.

Omelets are simple and almost unattainable. I last made one two weeks ago while attempting scrambled eggs. Is there a metaphor in there?

I cut up two bacon strips, half a green bell pepper, and bits of onion from a baggie in the fridge. I did not wear goggles. I cooked the bacon first, then mixed in the veggies to sauté for a few minutes. This was all done in my trusty fifteen-dollar medium nonstick pan, which, naturally, was also where I wanted to cook the eggs. So, I transferred the filling into a separate pan on low heat, added a bit of oil to the main pan since much of the bacon grease had gone with the bacon, and poured in the eggs.

I’ve since identified three issues.

First, our current eggs are larger than we’ve had. I added my normal splash of milk before whisking the eggs but had a nagging thought that there was a smidge too much egg.

Second, I used the back-left burner for the eggs when I always use the front-left. The former is the second-largest burner on our gas range. I didn’t sufficiently adjust the heat. Instead of a uniformly pleasing eggy yellow one would expect in a diner or cafe of any price range, I had a slightly overcooked exterior with a vaguely gelatinous interior.

Even so, I tossed my mix-ins and a torn-up piece of white American cheese—an East coast delicacy with a strikingly dissimilar look to orange plastic—onto the left half and carefully folded the egg over.

It didn’t break. Hope sprang eternal.

I then agonized several seconds too long over transferring this newly-christened omelet to my plate. The eggs cooked. I didn’t use my spatula to ease it off the pan. The eggs cooked. I committed to a flip.

I’d appreciate it if you paused to gather a minuscule violin and a box of tissues.

Bits of egg stuck to the pan as the flip became a flop.

A rend in the top of this would-be omelet matched the newest scar on my heart as I failed to honor the memory of these two eggs. But I didn’t let my inadequacy ruin my enjoyment. It was omelet enough for me and I ate it as such. Paired with homemade bread and jam the meal was delightful.

Oh, the third issue: I was cooking before 6AM on five hours of sleep. The world isn’t ready for the breakfast I would make with a full night’s rest.

  • 1
    This post is excerpted from a letter I wrote to a friend on a whim.

Surviving the Card Aisle

I’m a certified card guy. A notable greeting card enthusiast. A frequent mail-based correspondent. I think Bob at my local post office recognizes me.

I purchase two or three birthday cards from my local grocery and drug stores each month, and I don’t cut corners. I am steadfastly selective. Below are my card criteria I recommend everyone use to ensure a meaningful choice, and to encourage card manufacturers to improve their options.

  1. Any card you buy must, upon first read, elicit an audible laugh or a shrug.
  2. If you anticipate the card’s punchline, it’s not funny enough.
  3. Avoid crude humor unless the recipient regularly tells and enjoys such jokes.
  4. Singing cards are an intruding embarrassment, unless you’re confident the recipient finds them charming.
  5. Select cards with no more than two sentences of text in the interior. One sentence is preferred. The only exception is if it’s tremendously funny.
  6. When in doubt, choose a card that plainly states the occasion on the outside and lacks inside text altogether.
  7. If you find a card that would be ideal for a particular person in the future, buy it.

Happy shopping.

Mario Kart World

I’ve played hundreds of hours of Mario Kart 8 on the Nintendo Switch,1Over 910 hours. and though I don’t fancy myself a leading expert on the game—I’m still short of my 10,000 hours—one picks up a thing or two after seven years of gameplay.

I bought the Nintendo Switch 2 to play Mario Kart World and have played a few hours in both solo and split-screen modes. I’m conflicted by their new take on the original Grand Prix while remaining intrigued by the new Knockout Tour option, and I’m too afraid of my free time to dive into Open Roam. Since I’ve spent most of my time with traditional Grand Prix races, I’ll focus my thoughts on them.

Continue reading “Mario Kart World”
  • 1
    Over 910 hours.

Revisiting Morning Pages

I discussed morning pages just over one year ago when I was one month into the practice and, as it turned out, one month away from dropping it. My last set of morning pages was July 27, 2024.

I’ve been in a creative rut over the last couple months, often writing blog posts last-minute, not making progress on other projects, and not even taking time to read consistently. It’s hard to pin down a cause but that doesn’t mean I couldn’t try a treatment. I’ve written 1000 words of morning pages each day of the past week, having made two changes that I hope will help it stick.

First, I created a simple shortcut on my phone, iPad, and MacBook that builds a new morning page sheet in Ulysses, requiring only a single tap (or click) to begin writing. This reduces any potential friction in the process and, by adding the shortcut to my home screens, keeps the process in mind immediately upon waking up. Even when I consciously choose to complete some or most of my morning puzzles first, I won’t push it back too far.

Second, I’m treating these morning pages as less of an additional journal and more of an exploration of my creative brain. I definitely get out thoughts related to recent events and day-to-day miscellany, but I want to treat morning pages as a space for my brain, unencumbered by a long day, to let loose and find elements of my creative voice that I’ve misplaced. I’m using to freely explore new blog post ideas, play with perspectives and literary devices to make them more engaging, more of what I want to read. Having a goal beyond mental decluttering has made the time feel more worthwhile without putting additional pressure on the concept.

Even after a few days I can sense my brain is refreshed and excited to tackle projects old and new. A year was too long, but I’m glad I decided to restart regardless.

Catchers Get Bigger

I’m fairly confident all Major League Baseball players have gotten bigger over time, but I specifically decided to use the newest version of the Lahman Baseball Database to look at the average weight of catchers by the decade in which they debuted. Their listed weights are static so we can’t be certain what their debut weights were, but we’re looking at large trends. I also required any catcher in the list to have caught at least 200 career games.

Distributions of MLB catcher weights by debut decade, with averages and number of players inset.

We can also look only at the average weights per decade to get a clearer sense of the overall trend.

Average weights of MLB catchers by debut decade.

There’s a pronounced increase in the 1940s and again in the 1990s through 2000s, the latter of which being when players started eating balanced breakfasts.

Technical Details

I first ran this query in the Lahman Database loaded on my computer.

WITH
	"catchers" AS (
		SELECT
			People.playerID,
			People.nameFirst,
			People.nameLast,
			MAX(People.weight) AS "weight",
			SUM(Appearances.G_c) AS "gamesCaught",
			SUBSTRING(People.debut, 1, 4) AS "debutYear"
		FROM
			People
			LEFT JOIN Appearances ON Appearances.playerID = People.playerID
		WHERE
			Appearances.G_c >= 10
			AND People.weight > 0
		GROUP BY
			People.playerID,
			People.nameFirst,
			People.nameLast
		ORDER BY
			weight
	)
SELECT
	"playerID",
	"nameFirst",
	"nameLast",
	"weight",
	"gamesCaught",
	"debutYear"
FROM
	"catchers"
WHERE
	"gamesCaught" >= 200
ORDER BY
	"debutYear"

I exported the resulting data as catchers.csv and used a Jupyter Notebook for the rest.

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
from os import path


DATA_DIR = '/Users/markrichard/Downloads'

df = pd.read_csv(path.join(DATA_DIR, 'catchers.csv'))

# Remove 2020 partial decade
df = df[df['Decade'] != 2020]

### MAKE DISTRIBUTIONS ###

# Create base plot
g = sns.displot(df, 
                x='weight',
                kind='kde',
                col='Decade',
                col_wrap=3,
                fill=True,
                common_norm=False,
                aspect = 1.75)

# Calculate and add average lines
decade_stats = df.groupby('Decade')['weight'].agg(['mean', 'count']).round(1)

for decade, ax in zip(g.col_names, g.axes.flat):
    if decade in decade_stats.index:
        mean_weight = decade_stats.loc[decade, 'mean']
        player_count = decade_stats.loc[decade, 'count']
        
        # Add vertical line at mean
        ax.axvline(mean_weight, color='red', linestyle='--', linewidth=2, alpha=0.8)
        
        # Add text annotation
        ax.text(0.02, 0.98, f'Avg: {mean_weight} lbs\nN: {player_count}', 
                transform=ax.transAxes, 
                verticalalignment='top',
                bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))

plt.tight_layout()
plt.show()

### MAKE AVERAGES SCATTER PLOT ###

plt.figure(figsize=(10, 6))

# Simple scatter with consistent sizing
plt.scatter(decade_stats.index, decade_stats['mean'], 
           s=100, 
           alpha=0.8, 
           color='steelblue',
           edgecolors='white',
           linewidth=2)

# Connect points with a line
plt.plot(decade_stats.index, decade_stats['mean'], 
         color='steelblue', 
         alpha=0.6,
         linewidth=2)

# Clean styling
plt.xlabel('Decade')
plt.ylabel('Average Weight (lbs)')
plt.title('Average MLB Catcher Weight by Debut Decade')
plt.grid(True, alpha=0.2)


plt.tight_layout()
plt.show()