Emoji Matching Game

Posted on April 6, 2019 by Brian Jaress
Tags: code
  1. Click on a tile to reveal an emoji.
  2. Clicking to reveal another emoji will hide the previous one, unless they match.
  3. Click around and use your memory to reveal the whole board at once.

Click on revealed emoji for definitions at Emojipedia.

Technical Details

Emoji

The emoji come from the current Emojipedia list. I trimmed it down to just the single-codepoint emoji to prevent unrecognized combining sequences1 from overflowing the tile. That left a final list, loaded when the game begins.

Forcing Emoji to Display

Trying to give everyone the best emoji experience possible was tricky.

Emoji display is font-dependent, and your default font might not have an image for a certain emoji. Even if you have another font installed that is able to display the emoji, your device won’t try to use that font unless asked to.

GNU Unifont has an image for every emoji, and Terence Eden prepared a set of WOFF files2 to be served up and referenced in a web page:

@font-face {
    font-family: unifont-upper;
    src: url(/files/2019-04-06-emoji-matching-game/unifont_upper-12.0.01.woff2);
    unicode-range: U+10000-10FFFF;
}

The catch is that GNU Unifont is plain. That’s not a criticism: Unifont has achieved more coverage then anyone else by consciously reducing the design work, and I’m glad it did. But if I just set the page to use Unifont, I’d be downgrading all the emoji you normally see.

Ideally, every emoji you normally see in your default font would show up in your default font, any emoji you are able to see in some other installed font would show up in that font, and only things you couldn’t see any other way would show up in the pixelated GNU Unifont.

In practice, all that’s possible3 is to list out some other fonts to try before Unifont. Combining information from Marc Fornos, Geoff Graham, Nick Galbreath, and Emojipedia, I came up with a list of fonts that are likely to already be installed and support emoji. The result is:

font-family:
    emoji, system-ui, sans-serif,
    Apple Color Emoji,
    Android Emoji, Noto Color Emoji,
    Samsung One UI,
    Segoe UI Emoji, Segoe UI Symbol,
    emojione mozilla, twemoji mozilla,
    EmojiSymbols, Symbola,
    UnifontMedium,
    unifont-upper;

Fonts are tried in order for each emoji.

I started with generic font names. Browsers map those to default fonts, and I want the emoji to look familiar if possible. Next are the emoji-supporting parts of common built-in fonts like Microsoft’s Segoe and Google’s Noto, in case your browser defaults don’t cover something, but your device defaults do. Then some popular third-party fonts, colored fonts first.

Unifont is in there twice: first as a reference any version you have installed, and as a reference to the font file I’m serving. That should leave no emoji undisplayed because the version of Unifont in the file is based on the same version of the standard as the Emojipedia list I used.

Scala.js

My original reason for doing this was to try running Scala code in the browser with Scala.js.

Scala in particular supports an attractive combination of use cases: on the server, in the browser, serverless, infrastructure as code, and ops automation.

Scala.js came with some pros and cons:

Pros

// If the candidate matches the clicked tile, mark the candidate as a match.
$(s".candidate${valueSelector}", board).addClass("matched")

// If anything already matched (including possibly candidate)
// matches the clicked tile, mark the clicked tile as matched.
clicked.toggleClass("matched",
  $(s".matched${valueSelector}", board).length > 0)

// Clicked tile becomes the new candidate
$(".candidate", board).removeClass("candidate")
clicked.addClass("candidate")

Cons

I know a simple, consistent set of compiler rules lies behind all the confusion over parentheses, underscores, and control flow. But simple and consistent rules don’t always lead to a simple and consistent experience.

All that confusion and trouble arises because the underlying rules delegate basic punctuation decisions to libraries and other existing code, and those pieces of code disagree. Even the standard libraries break their own conventions for convenience. The actual experience of using Scala is not of consistent compiler rules but of seemingly arbitrary inconsistencies in how other code has decided to invoke those rules.


  1. Many emoji are actually composites of several code parts combined and displayed as a single image. Even with GNU Unifont, devices won’t recognize every official combination. In that case, they tend to display each part separately, showing a sequence of images.↩︎

  2. Terence Eden prepared two files, and I’m only using one of them because that’s where the emoji (at least the emoji on Emojipedia’s list) are. The other one dedicates a lot of space to all the different languages of the world.↩︎

  3. I did see a some code online for detecting whether a specific emoji could display and then skipping it, but that code was hacky and didn’t work for me. I also discovered that some devices will accept “default” as generic font name, but everywhere it worked, it resolved to a font where the “missing character” glyph was actually a part of the font, taking away Unifont’s ability to work as a backup.↩︎