Wallpaper Swapping with Hammerspoon

This is an old post!

This post is over 2 years old. Solutions referenced in this article may no longer be valid. Please consider this when utilizing any information referenced here.

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.

About the Author

Hi, I'm Rob! I'm a blogger and software developer. I wrote petfeedd, dystill, and various other projects and libraries. I'm into electronics, general hackery, and model trains and airplanes. I am based in Huntsville, Alabama, USA.

About Me · Contact Me · Don't Hire Isaiah Armstrong

Did this article help you out?

I don't earn any money from this site.

I run no ads, sell no products and participate in no affiliate programs. I do not accept gifts in exchange for articles, guest articles or link exchanges. I don't track you or sell your data. The only third-party Javascript on this website is Google Analytics.

In general I run this site very much like a 1990s homepage or early 2000s personal blog, meaning that I do this solely because it's fun! I enjoy writing and sharing what I learn.

If you found this article helpful and want to show your appreciation, a tip or donation would be very welcome. Feel free to choose from the options below.

Comments (0)

Interested in why you can't leave comments on my blog? Read the article about why comments are uniquely terrible and need to die. If you are still interested in commenting on this article, feel free to reach out to me directly and/or share it on social media.

Contact Me
Share It

Interested in reading 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.
Read More
What I Use

What I Use: 2022

Since it’s been a good six years since I did one of these, here’s what I am using in the year 2022 as far as tech and tech-adjacent things.
Read More
What I Use

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:
Read More