Posts

Pieces I’ve written.

Internet Trawling

I spend too much time reading online.

I tend to trawl content from a variety of sources, save the shiny ones in Instapaper, and read them later.

The majority of the articles I read are sourced from RSS feeds in Feedbin. I’ve organized these feeds into three categories: ‘full read’, ‘skimmable’, and ‘skippable’. This system means that if I ever feel overwhelmed by the volume of content, I can quickly mark a large number of articles as read and move on with life.

I like to complement these feeds with articles from elsewhere. I used to route specific Twitter accounts into Feedbin for this but that option is gone since the Twitter API ‘asplosion1.

Around the same time as that feature disappeared I started using Artifact. It’s from the founders of Instagram and the early development cycle has been impressive. At it’s core is a machine curated personalised news feed2. The recommendation engine feels like it still has room for improvement, I’d love to be presented with more “surprising” articles, but it still manages to throw the odd interesting articles into the mix. Using it feels like panning for gold.


  1. Mastodon has taken up the Twitter link mantle somewhat. ↩︎

  2. I don’t use the social features though it’s clear that’s where its focus is heading. ↩︎

Photoshop Generative Fill

The latest version of Photoshop Beta now includes a feature called generative fill.

I can imagine it’s easy to compromise the authenticity in your photography if you overuse these kinds of tools. With that said, and I know it’s potentially a slippery slide, there are situations where they can be incredibly useful.

For instance, I sometimes want to adjust a crop and need to fill in some areas to maintain the balance of the composition, and my Photoshop pixel-surfing chops aren’t up to the task.

I had a crack at using generative fill on a photo I recently took that I wished had more foreground.

The photo as shot with the canvas expanded to make room for more foreground.

The photo with extra foreground filled in by the default generative fill prompt.

The results look usable.

I also tried a prompt that removed the shadow on the left but the results of that were less natural.

Experimenting with various prompts and browsing their outcomes is far more enjoyable than swearing at the healing and clone brush tools 😂.

Long Live the Work Journal

A window onto green grass

Keep a journal for work, champions.

It’s pretty easy to get started—just create a text file.

Throw in a new heading each day and write down whatever you did—a single line for each task is usually enough. I put the newer dates at the top so it’s less scrolling to get to the most recent content. Over time you end up with your own little private reverse-chronological blog-in-a-file.

Each day, dump in commands you’ve run; links to documents you’ve created, reviewed, or read; tasks you want to get done; or goals you want to achieve.

You’re building a little outboard brain where your work history is just a short grep away.

When that Friday afternoon ennui kicks in, and I’m trying to work out what I’ve contributed, I go back over my work journal for the week.

I’m the rigorous1 type, so each Friday, I summarise my achievements and impediments of the week in a separate note.

When it’s performance review time, I run over these weekly notes and pull together the story for the year.

My journal used to be a bunch of text files saved to a folder in Dropbox. That’s honestly all you need. Use Markdown or Org mode or whatever, and opening a window into what you’ve done is a ⌘f keystroke away.

NotePlan as a work journal

I used text files for years, and then, about a year ago, I switched to NotePlan.

I switched to it mainly because it has built-in daily and weekly notes2, search, sync, an iOS app, and stores everything in plain Markdown files.

It integrates with your calendar (not that I use that feature much) and includes tasks and tagging in the style of Bullet Journaling.

I’m more accustomed to the GTD productivity approach but have found having tasks alongside my journal to be worth tolerating some of the rougher edges on the task management side3.

Eyes on the prize

Knowing that I have a home for all the minutiae of work leaves me more space to dedicate my brain to what’s actually important. 🫡


  1. Ahem obsessive, perhaps? ↩︎

  2. It also has quarterly and yearly notes, though I tend only to use quarterly notes to record what I aim to achieve that quarter. ↩︎

  3. I have knocked together a few Alfred workflows to help me capture tasks into NotePlan from other apps. ↩︎

Organisational Change and Coaching Better Performance

Waves of water

It’s often necessary to roll out organisational changes in a business, e.g. new reporting structures, goal setting frameworks, or planning processes. Each change introduced requires time for people to adapt and normalise.

When businesses introduce a series of changes in quick succession, people deal with them like a swimmer facing a tight set of waves. As they adjust to the wake of one change, they are immediately destabilised by the onset of another.

This situation can make it difficult for people to build their capability and improve performance. Leaders spend most of their coaching effort on dealing with the impact of the changes rather than improving an individual’s performance.

If you find managers in your team are spending the majority of their time coaching people through change it’s likely a sign that you are trading off rolling out change over coaching performance.

Daily Journal Time Machine

I’ve journaled in various forms over the years 1.

About ten years ago, I migrated my journaling to the Day One app.

I love how Day One is available on my Mac and my iPhone. I post text, photos, audio, links, or quotes as they pop into my head. I tag posts and even store posts into separately themed journals.

A list of my journals in Day One

A list of my journals in Day One.

In the last six months, I’ve added reviewing my journal to my morning ritual.

Each morning, before I knock out the day’s Wordle, I review the posts “on this day” in Day One.

It’s been a delight.

On some days, I have posts dating back ten years. It’s like jumping into a time machine to a previous life. Reading back over challenges at work, holidays we’ve taken, or photos of my family always brings a smile to my dial.

Once a month, I choose a random tag and review the journal posts under that. It’s illuminating to see the evolution of my mood or feelings on a particular topic over time.

Regularly reviewing my posts has made writing posts feel more valuable too.

So yeah, journaling is like a tricked-out DeLorean.


  1. First on paper, then via text or Org mode files stored on Dropbox. ↩︎

Creating Clarity in Complex Conversations

Figures in a spin

Product development is a team sport mostly carried out through meetings and conversations.

Two practical things you can try to help create clarity and reduce chaos in particularly complex conversations:

  1. Consolidate progress with a series of summaries.
  2. Crystallise outcomes in writing.

These might seem obvious, but they don’t happen as often as I’d hoped.

Consolidate progress with a series of summaries

Do you find yourself in meetings with multiple people discussing complex topics?

Does a series of tangents and related issues emerge as the conversation progresses?

In the end, are you unsure of where things are at?

Are you confident that you understand the situation but are uncertain if it’s the same for others?

Taking complex, meandering conversations and providing clear, structured summaries throughout is incredibly valuable 1.

I think of each summary as incrementally locking in consensus or taking a collective step up the ladder of inference, as described in Crucial Conversations.

Sometimes the state of play seems obvious, and therefore it feels unnecessary to summarise. I suggest pushing through this feeling and doing it anyway to ensure alignment and avoid pluralistic ignorance.

Crystallise outcomes in writing

As soon as you leave the conversation, you are immediately misaligned again 2.

You can reduce this misalignment by sharing a written conversation summary including decisions and actions.

Given the volume of conversations leaders tend to be in, capturing the outcomes becomes essential for rebooting context later or remembering which decisions were made and why.


  1. This can be as simple as listing the facts established to date, assumptions, trade-offs available, or agreed actions. ↩︎

  2. It’s ok. This is normal. Read more on autonomy and alignment from Jean-Michel Lemieux↩︎

Come Back to Me When You've Got a Trade-Off to Discuss

It’s not particularly useful debating the value of anything in isolation.

Considering programming languages, tools, processes, or product decisions on their own is a mostly philosophical exercise.

Things start to have meaning when we assess them against other available options.

That’s where the rubber hits the road, and things get spicy, friends.

Blog Rebooted

Welcome to my rebooted blog. 🎉

I had this domain hanging around and liked it better than my old dance.computer.dance domain.

I’ve pulled the posts and links here and the old domain should be redirecting across properly.

I’ve added tags to track the topics of my posts and links.

I’m using Hugo to build the site and Netlify for hosting it. I’ll write a post on how I’ve set up the assets and Hugo build.

So, now I’ve revamped my blog and my photography portfolio it’s time to get shooting and writing 💪

Happy computer

(I will miss my old blog's happy computer logo.)

Twenty-twenty

Wake up, go to the kitchen. Open the fridge: Zuul.

Close the fridge, go to my desk. Open Slack: Zuul.

Close Slack. Open a spreadsheet: Zuul.

Close the spreadsheet. Go back to bed.

Hyper Keys and Mouse Buttons With Karabiner

I’ve got a hankering for keyboard shortcuts.

I’m all about pressing a key without having to worry about which application I’m in and my computer doing something useful.

This noble pursuit has taught me one thing: there’s never enough keys™.

Good old Vim has demonstrated the value of a trusty leader key in the war to get more keys. So, I undertook a holy mission to find the mythical macOS hyper key, and along the way found the deep well of keyboard customisation that is Karabiner-Elements.

Hyper key

I’ve set up Karabiner-Elements so that if I combine the backslash key with other keys, it acts as the hyper key 1.

I use this hyper key as a prefix to bind global shortcuts without having to crush my fingers, and soul, into a ball.

Here’s a selection of the shortcuts I keep behind this hyper key prefix:

  • \+t brings my time tracking app into focus.
  • \+s locks my screen.
  • A bunch of shortcuts move windows around via Moom.
  • A couple of shortcuts switch my audio output between my headphones and speakers via an Alfred workflow.

Mouse buttons

macOS doesn’t natively recognise the extra buttons on my new mouse which sucks because: there’s never enough keys™.

So, I was chuffed to find that Karabiner recognises these extra mouse buttons and can bind them to key sequences.

Here’s a look at my bindings:

My Karabiner-Elements preference

I miss the sideways scrolling of the Magic Mouse, but I’ve set up Karabiner so that if I hold down my scroll wheel button, I can scroll left and right. It works reasonably well and means I don’t need to reach for shift while spinning the scroll wheel to side-scroll.

I map button 4 of my mouse to play and pause my music. The media keys on my keyboard are a chord away, but usually, it’s easier to press a single button instead.

I map button 5 to a shortcut2 assigned to the Meet Mute Chrome extension. This shortcut toggles mute on my current running Google Meet meeting, which is killer.

The config

So there you go. Maybe you’ll find something useful in my Karabiner-Elements config file that you can steal.

And may you never run out of keys.


  1. The hyper key on the Mac is a combination of Ctrl+Command+Option+Shift which is the equivalent of a dragon costume with four people in it, but hey, it does the job. ↩︎

  2. Command+shift+d because the extension doesn’t recognise the hyper key chord for some reason. ↩︎

Lonely underpants

Hello, underpants.

I see you laid there, all alone next to the bus stop. Your wobbly white leg holes frowning beneath your powder blue waistband. How did this happen? Who abandoned you?

Were they the absent-minded type? Did they drop you on their way home from swimming? Perhaps you’re a casualty of a botched trip to the laundromat. It’s ok, you can tell me.

I have to be honest, for a minute there, I was worried you were a victim of something more…biological. It’s what everyone expects when they see undies in your situation. But no, by the looks of it, the worst you’ve seen is perhaps a morning of light decorating followed by a brisk walk to the shops for a celebratory flat white. If it were dysentery, there’d be no mystery.

Maybe it was a bad break up. Maybe—and I’m sorry to say this—you made a pork chop of yourself one too many times. Couldn’t resist sleazing over the top of waistbands at parties, maybe? A fan of an ill-timed slide down butt crack way were we? Look, I get it, you’ve got a hard job. Why shouldn’t you be allowed to let your hair down every once in a while? Some bosses are tough that way. Imagine being Jeff Bezos’s boxers.

Maybe, just maybe, you’re more like a cocoon—discarded in a gleaming moment of triumph. I can see it—your owner’s eyes sparkling as they stand from the bus stop bench, Instagram in hand, resolute in their purchase of new sweat-wicking compression shorts. That is the missing piece you see; they’re going to hit the gym all the time now. They lock their jaw and cast you backwards, over their shoulder as they step onto the bus and into a new life—slow motion flames and explosions blooming behind them. Like when Jerry Maguire walked out of the office with his goldfish and Renée Zellweger.

What can I say, friend? Social media is coming for us all.

Lazily Loading Resized Images on a Hugo Photoblog

I rebuilt A Strange Kind of Madness using Hugo a month or so ago. As with most photoblogs, it has pages with many images on them, and I was inspired by Photo Stream to load these images lazily.

Image resizing

If you want Hugo to resize images when it builds a site, you need to place your images alongside posts, so they are considered page resources. So, I put each post in a folder with its associated image and reference it in a field called, shockingly, image in the post front matter.

$ ls content/posts/2020-03-31-my-post
20200331-4491.jpg
index.md

$ cat content/posts/2020-03-31-my-post/index.md
+++
title = My Post
date = 2020-03-31T10:42:00+08:00
image = 20200331-4491.jpg
+++

Adding Lazyload

Roll-up pages have many thumbnails and benefit most from lazy loading.

First up, add the lazyload javascript library to your site build.

import Lazyload from "lazyload";

// Fire up our lazyloading (just initialising it does the job)
const _lazyload = new Lazyload();

The library’s default configuration targets images with the lazyload class and loads the image stored in the data-src attribute. If you place an image on the standard src attribute, it will be treated as a placeholder.

Placeholder images

I wanted something more interesting than a sea of grey rectangles for placeholder images. I had a look at using BlurHash, but that was going to involve rendering canvas elements for placeholders 1.

I want the front end to be as simple as possible 2, so I abandoned that approach and instead created a single-pixel resize of the source image which provides a simplistic average colour placeholder for each image. It does the trick.

Placeholder images

The markup

All of the necessary resizing code and markup is in a Hugo partial that renders a thumbnail for each post. Be sure that your image tags include width and height attributes so the browser lays them out correctly such that lazy loading is effective.

{{- with .Resources.GetMatch (.Params.image) -}} {{/* Resize to a single pixel
for a placeholder image */}} {{- $placeholder := .Resize "1x1" -}} {{/* Resize
to 800 pixels wide for a thumbnail image */}} {{- $thumbnail := .Resize "800x"
-}}
<img
  src="{{ $placeholder.RelPermalink }}"
  data-src="{{ $thumbnail.RelPermalink }}"
  width="{{ $thumbnail.Width }}"
  height="{{ $thumbnail.Height }}"
  class="lazyload"
/>
{{- end -}}

The main downside to this is that two resizes for each image adds a bunch of time to the site’s build process but that’s a trade off I’m willing to make.

Go forth, and embrace laziness.


  1. Or integrating React components that handle this for you↩︎

  2. The only Javascript it uses is for this lazy loading. ↩︎

A Tour of my Desk

I’ve been working from home full time for four years, and my desk setup has mostly remained the same during that time. But the recent spate of folks sharing their home office setups—this Basecamp post was my favourite—inspired me to spruce up my own.

So, now it’s tour time folks!

My Desk

My work laptop (1) is a 13 inch 2017 MacBook Pro. It’s got the busted keyboard design. I mostly use an external keyboard, so mine still works fine 🤞.

My monitor (2) is a 27 inch Dell that’s six years old. It does the job. I’ll probably wait until it dies before I replace it.

I use a Kinesis Advantage 2 keyboard (3) which lets me hold my hands like a T-Rex while I type. It’s easy on the wrists but beware, it took me six months to type on this thing properly, and those six months were tough going so perhaps not a fun challenge to take up during already potentially stressful quarantine times.

I replaced my trusty stack of too-dry-to-read textbooks with the brutalist styles of this monitor stand by Brateck (4). It has a drawer where I can store my notebook and pens when I’m not using them. But what tickles the organiser in me the most is the cavity beneath it where I can store other things1.

Pictures and plants are, of course, essential (5 & 6).

I used an Apple Magic Mouse for years, but it started acting flaky after I upgraded to Catalina. So, I switched to a Razer Death Adder (7) which is way more comfortable to hold. It doesn’t allow me to side scroll as easily but not recharging batteries regularly is nice. Also, it has disco lights and a USB cable. Actually, let’s talk about cables and wires for a minute.

So, wires. Yeah, they get in the way. Yeah, they can look ugly. But you know what else they are? Reliable. That’s right, like a blue heeler at dusk, they’re always there for you. They mean there’s no more stuttering when you move your mouse. You don’t need to worry about Wi-Fi turbulence when you’ve got an old fashioned Cat 6 cable plugged in baby 2. With that off my chest, let’s get back to the tour.

Next up is the linchpin, the box that brings it all together. A couple of my colleagues recommended the CalDigit TS3 Plus Thunderbolt dock (9) and it’s tops. I plug everything into it, USB devices, my router, my display, my microphone and headset—and it all flows to my laptop via a single Thunderbolt cable (8). The TS3 Plus also serves as a power source for my MacBook so I can leave my power cord in my bag for that wondrous time, someday in the future when I can work outside again.

I spend a large portion of my day on video calls so a reliable audio setup is essential. I have a Jabra headset which is light and comfortable, but I also have an old set of Sennheisers (10) that I enjoy listening to music through. Again, some colleagues tipped me off to the fact that I can frankenstein a microphone onto any headset by using an Antlion ModMic. Its hardware mute button isn’t as low down on the cord as I like, but at least it’s there.

Headsets

Another bonus of wearing this new set up is that I look like a helicopter pilot instead of a call centre worker and who doesn’t want to look like a helicopter pilot, right?

I run my headphones into a Magni 2U headphone amp (11) and plug that and the microphone directly into the TS3 Plus 3. So now, I have my favourite headphones handy when I need to concentrate and want to listen to something from one of my go to playlists (or White Noise).

When I want to listen to music without headphones, I stream it through my fairly ancient Jambox (12). It’s rugged and still trucking though I expect the battery to self combust any day now. I've ordered a pair of Audioengine HD3 speakers to replace it because my ears deserve it.

Update: (2022-05-22) There weren’t any HD3’s in stock so I instead went with a pair of Edifier R1280DB’s. They are cheaper, connect to the TS3 via digital optical cable, sound good, and look alright.

Desk Speaker

I’ve also ordered a Logitech C925E webcam which I’ll mount on my monitor so I’m not always side-eyeing folks from my laptop camera in meetings.

I spent a scandalous amount of money on a Herman Miller Embody (13) when I first set up my home office. I never worry about my chair, so I think I can say that money was worth it 🤷‍♂️.

Finally, there’s my desk (14). It is a standing desk that I put together eight years ago. Back then, it was tricky to find standing desks online, so to save money, I only ordered legs from GeekDesk 4 and attached a bamboo tabletop from Ikea to them. The top could be a bit larger, but maybe I’m just greedy.

Update: (2023-05-14) The motor on my GeekDesk died after ten years so I replaced it with an 1800mm wide Zen Pro Bamboo Sit Stand Desk. The extra width is much appreciated and the preset heights work a treat.

So, there it is—the throne of my weekday castle.

There are a window and couch out of shot to the left. The couch is mostly ornamental because if I allow myself to lie down on it and close my eyes for just one minute, all will be lost.


  1. I guess I could have hollowed out the middle of those old textbooks 🤔. ↩︎

  2. You should try to use an Ethernet cable at least. It can make your video calls more reliable↩︎

  3. I used an Antlion USB adapter for this but found it would end up with static when my laptop woke from sleep, so now I plug in directly, and things seem fine. ↩︎

  4. I ordered the v2 legs at the time and shipped them to Perth from the US for a sum that would make a Nigerian prince blush. ↩︎

Nando

I tune in for another instalment of Nando’s daily dance.

He starts on his patio, checking the lay of the land. From there, he makes his way to the street’s edge, hands clasped behind his back, looking north and south.

Once the coast is clear, he makes his move across the street, all shuffling flannel and white hair.

He doesn’t go from A to B. He moves in curves, sniffing the air, checking the clouds, looking at the letterboxes. This is all foreplay, skirting around the main event—the block where the builders work.

Nando pauses for a tasteful amount of time at the edge of the yellow construction sand. Kicking bits of brick over with his sandalled foot, hands still clasped behind his back, looking around and down at the same time.

He sidles up to the large wire bin to see what’s on offer. No more cloud watching for him now, he’s locked in. He releases a hand and reaches in—like that sideshow game where you drop the claw into the pit of treasure and disappointment. He strains at the edge of the bin as he reaches past rubble and broken buckets. It would be easier if he could bend his back fully, but those days are long gone. Eventually, he rises with a timber off-cut in hand. He tucks it under his arm and returns across the street.

His front yard is where he builds his bower. Plants form a perimeter around piles of timber stacked up against two water tanks. He walks up to a table next to the tanks—I helped him move that table from the curb in front of my house last year—and fusses with the blue tarp on top before tucking his new find underneath it.

Then he returns to the street searching for more tributes—our block’s very own street sweeper.

That was a few weeks ago now.

You see, Nando is smack bang in the sights of the virus. Folks his age don’t fare well if they get it. I imagine his kids imploring him to stay inside. I also imagine him being adamant about still wanting to go outside to find things and take them home. Maybe one of his kids is a lawyer or a nurse or a zookeeper because it seems they’ve managed to convince Nando to lay low.

When I look out my front window, I don’t see him anymore.

And the rubbish is piling up.

Playing a Random Album on Spotify

I still like listening to albums and sometimes want Spotify to play a random album from a playlist of albums I’ve created.

I couldn’t find anything out there that does this so I wrote myself a script to handle it instead.

Here’s a rundown if you want to use it.

First up, you’ll need a playlist with at least one track from each of the albums you want to choose from (here’s mine). Grab the ID of your playlist1, and your username and add them into the script below.

Then, you’ll need to create an app in Spotify and get your client ID and secret, add them to the script below, so you can authorise the script.

Finally, run gem install rspotify in your default ruby2 and you should be off to the races.

Run the script with Spotify desktop app installed and it’ll open up a random album for you to press that sweet, sweet play button on ⏯.

I run the script from an Alfred workflow so I’ve got it close at hand.

Enjoy 🎷🎶

#!/usr/bin/env ruby
#/ Usage: open-random-album
#/ Open a random album in Spotify.
#/

require "rspotify"

class RandomAlbum
  CLIENT_ID = "YOUR CLIENT ID"
  CLIENT_SECRET = "YOUR CLIENT SECRET"
  USERNAME = "YOUR USERNAME"
  PLAYLIST_ID = "YOUR PLAYLIST'S ID"

  def self.fetch
    RSpotify.authenticate(CLIENT_ID, CLIENT_SECRET)
    new.fetch
  end

  # Grab the albums from a playlist and choose one at random
  def fetch
    playlist = RSpotify::Playlist.find(USERNAME, PLAYLIST_ID)
    albums_in_playlist(playlist).sample
  end

  private

  def tracks_in_playlist(playlist)
    limit = 100
    offset = 0

    [].tap do |result|
      loop do
        tracks = playlist.tracks(limit: limit, offset: offset)

        break if tracks.empty?

        result.concat(tracks)
        offset += limit
      end
    end
  end

  def albums_in_playlist(playlist)
    tracks = tracks_in_playlist(playlist)
    tracks.reduce({}) do |acc, track|
      acc[track.album.id] = track.album
      acc
    end.values
  end
end

album = RandomAlbum.fetch

puts "Opening '#{album.name}' in Spotify"
system "open #{album.uri}"

  1. Click on Share -> Copy Spotify URI. The playlist’s ID is the string after the last colon. ↩︎

  2. If this becomes a pain I guess you could look into bundling the script up with its required gems somehow. ↩︎