Wallpaper Swapping with Hammerspoon

Hammerspoon is a pretty nifty tool. It’s kind of difficult to explain what it does, but the best I can do is that it allows you to use Lua to script actions on your Mac and, crucially, respond to events.

For instance, I use Hammerspoon to lauch all my applications when I get to work and lay them out on the screen in the order that I like. I can do this because I was able to attach a location listener to work’s location, and execute Lua code on arrival. The amount of things that you can do with this tool is pretty stunning. It’s become an indespensible part of my macOS experience.

Today, I want to talk about swapping your wallpapers around. It serves as a useful visual reference for where you are if you have a dictinct wallpaper that you have at work vs. home.

Since changing the wallpaper is not natively supported in Hammerspoon, I first started looking around for a command line tool that would allow me to change the macOS wallpaper. It turns out you can do this with AppleScript using the osascript command. Many solutions I found were based around this approach, but it has some problems.

  1. There are some security hoops you have to jump through to give your script permission through universal access.

  2. If you have more than one desktop, the script devolves into forcing keystrokes to change to each desktop and then change the wallpaper, which sucks.

In general, it “works” but it’s a pretty poor solution.

But then I stumbled on this Go program that was the eureka moment. It turns out that macOS stores the data about wallpapers in a SQLite database! And, you can literally update this database and change the wallpapers.

Try it out yourself!

$ sqlite3  ~/Library/Application\ Support/Dock/desktoppicture.db
sqlite> select * from data;

There it is! A list of every background for every desktop and screen. Exploring around inside this database is interesting but a subject for another time. Right now, we want to change our background. Which you can now totally do with SQLite.

WARNING: the command below will set all your wallpapers to the default.

sqlite> update data set value = "";

You might have noticed that nothing changed. Unfortunately, the database isn’t read in realtime, and the way you can force it to read … is by killing Dock. Which makes total sense and is certainly graceful.

sqlite> .exit
$ killall Dock

And now your wallpaper is the default macOS wallpaper.

So, now that we know how to change the wallpaper using SQLite, back to Hammerspoon. Hammerspoon, of course, has SQLite support exposed through the Lua language, which you can access using hs.sqlite3. Using this, it’s pretty easy to construct a couple of functions that can get and set the wallpapers.

function setWallpaper(file)
    local connection = hs.sqlite3.open(os.getenv("HOME") .. "/Library/Application Support/Dock/desktoppicture.db")
    local dock = hs.appfinder.appFromName("Dock")
    connection:exec("update data set value = '" .. file .. "'")
    hs.alert.show("Wallpaper Changed")

function getWallpaper()
    local connection = hs.sqlite3.open(os.getenv("HOME") .. "/Library/Application Support/Dock/desktoppicture.db")
    for a in connection:rows("select value from data limit 1") do
        return a[1];

Now, you can do fun things like this:

local wallpaper = ""
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
    wallpaper = getWallpaper()

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "H", function()

So now when you press command + control + option

  • W, you wallpaper changes to the default. And pressing command + control + option + H sets it back again.

With these two functions, we can now get and set the wallpapers. Note, that I’m just blindly setting all the screens to the same wallpaper. You could make these functions smarter by storing the individual wallpaper per screen and restoring that. But this is good enough for my purposes.

With this knowledge, it would also be pretty easy to write a small script to update it from the command line. The possibilities are endless.

Did something I wrote help you out?

That's great! I don't earn any money from this site - I run no ads, sell no products and participate in no affiliate programs. I do this solely because it's fun; I enjoy writing and sharing what I learn.

COVID-19 has taken the world by storm and left a lot of brokenness in its wake. A lot of people are suffering. If you feel so inclined, please make a donation to your local food bank or medical charity. Order take-out from your local Chinese restaurant. Help buy groceries for an unemployed friend. Help people make it through to the other side.

But if you found this article helpful and you really feel like donating to me specifically, you can do so below.

Read More

Securing static resources with cookies, nginx, and Lua

I’ve been working with one of my clients the last month on migrating his iron- based architecture to a cloud-based provider. In this transition, we are going from one or two physical servers to multiple cloud servers and separating out parts to better scale each individual service. As part of this, we are moving a significant library of images and videos away from being served off the same web server as the application and to a server tuned to handle requests for these static assets. The problem is that a lot of these assets (the videos and full-size images) are for paying members only. We need a way to secure those resources across physical servers.

What I use: 2016

Since it’s been awhile since I wrote a post about what I use in regards to software, hardware, etc. Perhaps it’s time that I did that again. So here’s a list of what I’m using in 2016: