Michael LangeAll projects β†’

Emoji Skin Tone Randomizer

Visit the project at https://github.com/DingoEatingFuzz/chrome-emoji-skin-tone-randomizer
Circa 2016

This was my second Chrome extension. It started with a general distaste for the yellow "neutral" emoji skin tone. As someone who watched The Simpsons a lot growing up, I see the yellow skin tone as a white skin tone in a poor disguise. Sure yellow isn't actually white, and there is also a white skin tone emoji, but if you look at the characters in The Simpsons, there are no white characters and yet there are characters of color. Even if you disagree about yellow being another white option, you can't disagree that it's a way to opt-out of race, which I also think is unfortunate. Human skin tones are variable and can be celebrated instead of conveniently omitted. So, using Cloud to Butt as inspiration, I set to work.

Understanding emoji skin tones

In practical terms, there are five different skin tones (based off the Fitpatrick scale) that can be applied to emoji to change them from yellow to a true skin tone (one of Fitz-1/2, Fitz-3, Fitz-4, Fitz-5, and Fitz-6). in technical terms, any emoji that is "skin tone eligible" has six separate glyphs, for each of the five skin tones and the yellow default presentation. Although a glyph in a typeface typically maps one-to-one with a unicode code point, a glyph can also represent a ligature. Unicode considers ligatures to be a matter of presentation and therefore outside of its jurisdiction. Therefore, any typeface can choose to make a ligature for any combination of letters. Some common pairs, for example, are fi, ff, and ae. This same technical implementation is used to implement skintones. So πŸŽ…πŸΏ is actually the ligature of πŸŽ… and 🏿. Which means that as soon as you place a skintone swatch character next to the Father Christmas emoji, it immediately becomes the skin tone variation of Father Christmas.

This is important to understand, since the extension will have to make the correct text substitions to apply skin tones.

Understanding "skin tone eligibility"

Not all emoji can have skin tones applied to them. This works: πŸ‘Ά + 🏼 = πŸ‘ΆπŸΌ. This does not work: 🍭 + 🏼 = 🍭🏼. This makes intuitive sense, but the details need to be implemented somewhere. This is where the emoji-data.txt file comes in. It is the canonical source of what emoji can pair with what. The stanza of lines of say Emoji_Modifier_Base are the emoji or emoji ranges that support skin tones (and gender) modifiers. This is a format between machine readable and prose that I like to call "machine attemptable". Because why not, I wrote a python script to parse this into a JS const.

This way, the array of all emoji that are skin tone eligible is generated and embedded into the final extension content script.

Understanding nested ligatures

At this point it may seem like all that's left to do is write a regex to replace emoji that aren't followed by a skin tone with an emoji that has a skin tone. I thought this too, but there is a little thing called the Zero-width Joiner. Remember that ligatures happen when a typeface declares a combination of unicode code points should be replaced by a single glyph. As should be expected, life isn't this simple. Sometimes (in emoji and in some languages) it is valid to have two code points next to each other as either a combined glyph or as separate glyphs. Consider this family: πŸ‘¨πŸ‘©πŸ‘¦πŸ‘¦. Now consider the same family as a single glyph: πŸ‘¨β€πŸ‘©β€πŸ‘¦β€πŸ‘¦. The only difference here is in the single glyph example, a zero-width joiner (zwj) has been inserted between each family member. These intricate glyphs can also be decomposed quite elegantly with JavaScript:

const intricateEmoji = 'πŸ‘¨β€πŸ‘©β€πŸ‘¦β€πŸ‘¦'
const allTheParts = [...intricateEmoji] // [ 'πŸ‘¨', '<zwj>', 'πŸ‘©', '<zwj>', 'πŸ‘¦', '<zwj>', 'πŸ‘¦' ]

But what happens when we introduce skin tones? Can we make mixed-race families? The answer is yes: πŸ‘¨πŸ»β€πŸ‘©πŸΏβ€πŸ‘¦πŸΎβ€πŸ‘¦πŸ½. This is achieved by having nested skin tone ligatures within larger family ligatures. The full set of unicode code points is:

const family = [
  "πŸ‘¨", "🏻", // Fitz-1/2 father
  "‍<zwj>",
  "πŸ‘©", "🏿", // Fitz-6 mother
  "‍<zwj>",
  "πŸ‘¦", "🏾", // Fitz-5 son
  "‍<zwj>",
  "πŸ‘¦", "🏽"  // Fitz-4 son
]

So this needs to be handled in the extension to make sure that if the original author of copy intended to have a family glyph, this extension doesn't come in and accidentally mangle it by injecting a skin tone where one isn't compatible

Making a Chrome extension

At this point, all that was left to do was write the JS and regular expressions that actually did the text substitutions and call that code at the write time from within the Chrome extension. The write time to run the code is during the document_end content script hook, and the write regex is this unfortunate thing.

Add the extension from the Chrome web store πŸ‘‰ Emoji Skin Tone Randomizer.

Technology used

  • JavaScript
  • Chrome extensions
  • emoji-data
  • Python
  • Unicode