Don’t get me wrong, I love Twitter’s emoji search UI. As a coder, I simply enjoy thinking about how something can be improved. So there isn’t anything in particular wrong with it.

You can follow me on Twitter to get updates on my free coding tutorials, or just check out this page with 📚 my coding books on JavaScript and CSS if you need a copy.

First I want to show you the finished version of the emoji finder I created. Then we’ll go over the problems that were solved. Is it perfect? Of course not. It might not even be better. But it's definitely different and has few advantages.

Here is the emoji finder UI I created in vanilla JavaScript with some CSS.

Vanilla implementation of Emoji Finder UI

Twitter UI is great, however, a few theoretical problems exist:

  1. It's hard to navigate between large groups of emojis. Sometimes clicking on an emoji group tab doesn’t help me navigate to the emoji I am thinking of  —  I still have to do lots of browsing just to find it in an already filtered category.
  2. Words don’t always match proper emojis. Increasing the vocabulary would definitely be one improvement. I type “stars” and all the single star emojis disappear because the plural/singular search is too strict.
  3. Color coding. If I type “yellow,” I want to see an entire list of all yellow emojis. I want to see stars, yellow cars, lemon, etc. If you type “blue,” not all blue items will appear in the filtered search.

Solving these issues isn’t difficult from a technical point of view. The only thing I found to be slightly difficult was to map 3200 emojis to actual JavaScript objects and events. It took a lot of time, but once the mapping is done everything else is a breeze.

Emoji Sprite Sheet With 3200 Items

There are 3200 emojis. To avoid making thousands of HTTP requests, all of them are stored in a single file called a sprite sheet or image atlas. Here is a partial view of the official Twitter emoji sprite sheet:

The official Twitter emoji sprite sheet image is even bigger than this.

The idea behind a sprite sheet is to store all emojis in one file, thus needing only one HTTP request from the server instead of hundreds. Or thousands in this case because there are 3200 Twitter emojis as of March 17, 2020.

Each individual emoji is then displayed in a single DIV element and can be navigated to by changing this property:

By supplying this property with an x and y offset in pixels we can navigate to any Emoji in the set. We just need to memorize the position of each emoji and store it in a JavaScript object.

Not the most fun type of work, but you only have to do it once.

Emoji Tabs

I started with rebuilding the emoji tabs interface. I simply didn’t agree with some icon choices made and the number of categories, so I increased the number of available tabs. There are more specifically meaningful subject groups than the 9 tabs Twitter offers – Nature, Cars, Airplanes, Trains – it could be more specific.

One thing of importance here is that I added the custom attributes data-type and data-title. Like their names suggest, data-type holds the tab name that will be used later to filter the emojis, and data-title stores a simple description of each set.

It’s best to spend more time browsing better categories than hard-to-find emojis. If we don’t merely increase the number of tabs, we can save search time if the tabs themselves are categorized in a more meaningful way.

I separated positive and negative emojis. How many times have you tried to pick from the positive set? It’s hard to draw the line between the two distinct types when all emotions are shown at once, and yet it is a very useful and practical distinction.

My insert_all_emoji function is called once after the DOM is finished loading.

It is responsible for creating each emoji in the main search results view. When it’s executed for the first time in the application's initialization time, it dynamically creates each emoji as a <div> element with display: inline-block. This means each emoji will automatically “wrap” over to the next line within its parent container.

Mapping Unique IDs To Background-Position and Emoji Size

Each emoji has a unique numeric ID ranging from 0 to 3199 (since there are 3200 emojis total). Also it's important to note that, originally, all emojis are set to be visible by default. When tabs are clicked, I use another function to “filter” out unwanted emojis from that category.

Each emoji's position is calculated using the CSS background-position property. This can be a pure nightmare with a set as large as 3200 emojis. So I had to create another script I called Emoji Finder. It basically shows the entire set as an image. Clicking on it with a mouse will yield an x and y background offset to that emoji in the developer console. Once I got those results, I brute-force copied them over into my CSS.

Knowing that each emoji is 48px by 48px, it’s relatively easy to map into the right background position with just some simple math.

Creating Clickable Tabs

In order to create each tab, I simply clicked on the emoji directly on the image and the script gave me accurate offset to it on the entire sprite sheet. I then hard-coded it into my tabs.

It was a painful process, but the results speak for themselves. Besides, what would be a better or faster way of doing that? I got it all favorite emojis mapped in a matter of 15 minutes.

Here is the CSS I ended up with:

It looks a bit convoluted, but there really isn’t much else you can do in order to achieve this functionality. I usually try to go the extra mile on issues that I know, once they're done, I never have to touch again.

Why I love JavaScript's higher-order functions

Higher-order functions are a lifesaver for situations where you have to deal with many items. I can’t imagine writing for-loops for cases like this. HO functions also make my code a lot cleaner and intuitive by means of abstraction!

Remember how in the previous step an entire set of emojis was embedded into the results view by default after DOM finished loading?

Now we need to filter that set. To do this I’ll use a higher-order function called .map. I could have probably used .filter, but in this particular case there is no difference.

filter_emojis is my favorite function from this entire project. It was lots of fun to write. Usually when there is a concrete problem to solve, coding becomes a lot more rewarding. And this was one of those cases!

But how does filter function know which emojis to keep and which to remove from the set? All IDs are stored in individual arrays representing those emojis.

I created a script that, when I click on any emoji in the main view, adds its unique ID to an array. Going around the entire list and clicking on emojis I was able to create lists I thought were associated with any particular tab. One by one, I created each list by simply clicking on emojis. The IDs that were generated were placed into arrays seen above (positive, negative, gestures, relationship, cats, and so on).

What happens now is first I set display to none for the entire set. Then, I use higher-order .map function to set display back to inline-block, but only to all emojis contained with selected set.

The onclick event to this function is tied to each tab using this function:

That’s everything for now. I need for everything I created during last two days to sink in for a while. Then I’ll add search as part II of this tutorial.

The emoji search function will take some time – I have to write a list of keywords describing 3200 emojis with more keywords than Twitter.

This is a necessary step, and will definitely be an improvement! For example, I noticed that Twitter's emoji search is broken in some places. There is no color coding. If you type “blue” you won’t get absolutely all blue objects in the set. But in my opinion, color coding is really important for something like this.


Thanks Cirrus The Cat For Your Everlasting Support.

Thanks everyone for your support. You too, Cirrus the Cat. 😊

You can Follow me on Twitter to get updates on free coding tutorials or just check out this page with 📚 my coding books on JavaScript and CSS if you need a copy.