Layers

2022-04-01

The problem

Pressing (not holding) ⌘-tab is useful for swapping between apps. If you ⌘-tab from your editor to your browser, you can then ⌘-tab back to your editor. You can jump back and forth between your editor and browser with just a quick ⌘-tab keystroke. It is a great workflow as long as you're using exactly two apps.

Then you get a Slack message and after giving Slack focus, ⌘-tab takes you back to your browser, the next ⌘-tab sends you back to Slack.

Sigh. OK, so we hold ⌘ and push tab until we're back to our editor and then hold ⌘ and push tab until we're back to our browser and now we can hit ⌘-tab to quickly jump between the two again. Order is restored.

This is mostly a minor nuisance except that this can happen dozens of times throughout the day. Little frictions add up and break flow.

The journey

I tried using Spaces a few times before but the animation was honestly slightly nauseating. If you turn on reduce motion then Spaces changes from a dizzying sliding animation to a more subtle fade animation. The fade animation is pretty tolerable and you can drag whatever applications you want to the proper Spaces and jump around with the keyboard. Success!

Except that somehow the reduce motion setting stopped working for me after a reboot -- it is still checked, it just doesn't do anything and I'm back to the abrasive sliding animation.

Gross. I started looking at other options. yabai looks powerful but requires you to disable System Integrity Protection (permanently, not just during installation) and that's a non-starter for me.

The solution

Taking a step back, what am I really trying to accomplish? I want to be able to press a key and jump to an app (or group of apps). Ideally I don't have to wrangle app the apps into the right workspace like I did with Spaces after every reboot.

Fine. I'll write some AppleScript. After lots of Googling and some optimization, I ended up with a script I'm calling Layers.

How does it work? Create a file in your home directory named .layers. Each line of the file should include a comma-separated list of applications in that layer. Running the layers script will generate compiled AppleScripts to focus applications and spit out a command to bind to a keyboard shortcut.

My .layers file looks like this:

Alacritty
Firefox, Google Chrome, Safari
Slack
Messages, Music, Stream Deck
zoom.us

The layers output is

Add keyboard shortcuts for the following:
ls /Users/ship/.layers_scripts/layer_0*_compiled | xargs -L 1 -P 8 osascript
ls /Users/ship/.layers_scripts/layer_1*_compiled | xargs -L 1 -P 8 osascript
ls /Users/ship/.layers_scripts/layer_2*_compiled | xargs -L 1 -P 8 osascript
ls /Users/ship/.layers_scripts/layer_3*_compiled | xargs -L 1 -P 8 osascript
ls /Users/ship/.layers_scripts/layer_4*_compiled | xargs -L 1 -P 8 osascript

I assign each line starting with ls to a shortcut via an Alfred workflow but you can use any tool that lets you map bash commands to keyboard shortcuts. I'm using ctrl-1 through ctrl-5 since those worked well enough for me with Spaces.

The ls ... command finds all the compiled scripts for a layer and passes them to xargs to run in parallel (up to 8 at a time).

The compilation + parallelization felt like overengineering at first, but it took little time to implement and has a real impact on responsiveness.

Pressing ctrl-2 will bring Firefox, Google Chrome, and Safari to the forefront of my screen. It skips any apps that aren't running. I can press ctrl-1 to get back to Alacritty where my editor is running.

This is better for me than ⌘-tab because it is predictable -- the positions of things never change. This is better for me than Spaces because there's no distracting animations.

There's one downside I can think of when compared to Spaces -- you can't have different windows of an Application mapped to different Layers. I didn't use this functionality in Spaces so I don't miss it.

Why "Layers"? Spaces are effectively distinct virtual screens. Layers don't have this same isolation and apps overlap. This overlap allows for composability and use-cases not allowed by space.

I've been using it a few days now and it feels fantastic. Take it for a spin and let me know how it works for you.

My goofy face

Hi, I'm Jeffrey Chupp.
I solve problems, often with code.