💡

I wrote this 7 years ago, but it is standing my test of time: I continue using it daily and have updated it to support Unicode 15 emojis 🪼.

The snippets below have evolved in time; I have left them as originally written, but you can find their up-to-date version on GitHub.

I use the awesome Hammerspoon to automate things on macOS. Today we’ll see how to create an interactive emoji search-engine to pick emojis by using their name or a few keywords:

emoji-chooser Smile and say cheese!

# Preliminary setup

To begin, download this archive and unzip into your ~/.hammerspoon directory. The archive contains a few thousands emojis in PNG format and a JSON file encoding their details. I generated it through this script.

# Hammerspoon setup

Now, to Hammerspoon to load the emojis and build the search engine. We’ll use hs.chooser to create a Spotlight-like window that allows filtering and selecting data. We’ll populate it with emojis; once selected, the emoji will be copied to clipboard and “typed” in the focused application.

Copy and paste the following snippet in your init.lua file:

-- Build the list of emojis to be displayed.
local choices = {}
for _, emoji in ipairs(hs.json.decode(io.open("emojis/emojis.json"):read())) do
    table.insert(choices,
        {text=emoji['name'],
            subText=table.concat(emoji['kwds'], ", "),
            image=hs.image.imageFromPath("emojis/" .. emoji['id'] .. ".png"),
            chars=emoji['chars']
        })
end

-- Focus the last used window.
local function focusLastFocused()
    local wf = hs.window.filter
    local lastFocused = wf.defaultCurrentSpace:getWindows(wf.sortByFocusedLast)
    if #lastFocused > 0 then lastFocused[1]:focus() end
end

-- Create the chooser.
-- On selection, copy the emoji and type it into the focused application.
local chooser = hs.chooser.new(function(choice)
    if not choice then focusLastFocused(); return end
    hs.pasteboard.setContents(choice["chars"])
    focusLastFocused()
    hs.eventtap.keyStrokes(hs.pasteboard.getContents())
end)

chooser:searchSubText(true)
chooser:choices(choices)

If you want, you can also customize the appearance of the chooser:

chooser:rows(5)
chooser:bgDark(true)

Lastly, bind the chooser to any key you like:

hs.hotkey.bind({"cmd", "alt"}, "E", function() chooser:show() end)

# Full result

You’ll find:

  • The complete result here.
  • My full Hammerspoon configuration here.