Emoji Haikus, and the deepest secrets of the unicode specification

About two and a half years ago, I built Emoji Haiku, a nifty little webpage that generates haikus out of the official unicode descriptions of emojis.

⏰ 🔨
👵🏽 🏐 🐋
🐫 👻

alarm clock hammer
old woman volleyball whale
two-hump camel ghost

A lot has changed about emoji since the initial version of Emoji Haiku! Here’s a few of the more notable things:

  • It’s possible to change the skin color of emoji (👋🏻👋🏼👋🏾👋🏾👋🏿)1!

  • We’ve got a bunch of new jobs 👩‍🚀 and people-doing-things ⛷ emoji, which support gendering as “woman” and “man”, and usually default to a gender-neutral representation.

  • Apple changed the PISTOL 🔫 emoji to a water gun. Microsoft changed theirs to a space blaster, and Twitter and Google followed suit a couple of years later.

  • People started to become more observant of how emojis sound when read out load by automated screen reading software, thanks to CarPlay / Android Auto / Bluetooth functionality taking off more (‘👍🏻’ = “light skin tone thumbs up”), and Kai on Twitter. Spoiler alert – it’s bad:

Emoji are political, because technology is political.

We’re finally starting to understand and observe the impact that technology has upon the power relationships in society, and are realising that the act of making technology is therefore fundamentally political. 2016 was probably the year that this concept entered the mainstream, with conversations about Fake News, the influence of Russia upon the 2016 election, and the impact of tech upon gentrification and marginalisation all significantly present in the political discourse.

Changes to emoji, which had historically been considered by many to be “politically neutral”, turned out to catalyse this discussion. 2016 was the year when Apple changed the pistol emoji to a water gun, which somehow turned out to be super controversial:

In the history of running Emojipedia, I have never seen an emoji change so poorly received… The combination of “Apple” and “Gun” in the same headline seems to have proven irresistible.

Since that change was first made, most other vendors have followed suit.

The response to skin-color modifiers in the same year was mostly positive, but still generated a lot of controversy. There’s plenty of great commentary by more astute individuals than me, so here’s a super brief literature review:

Paige Tutt in the Washington Post notes that it adds race to conversations that didn’t need it and shouldn’t have it, and that the changes are superficial. It’s worth noting that:

  • this is especially the case because many keyboard implementations retain your race preference and you actively have to make an effort to change this once you’ve got it set. I wouldn’t be surprised if many users never figured out how to change it again afterwards, given some of the stories that Joe Clark writes about here.
  • this is exacerbated in the text to speech representation, where ‘👋🏼’ is rendered as ‘waving hand: medium-light skin tone’. This is especially weird given that the user’s intent in choosing a skin color is often not explicit, given the fact that keyboards save it by default.

Andrew McGill in the Atlantic notes that proportionally, white people tend to use color-neutral representations (👩‍🔬) instead of white representations (👩🏻‍🔬), partially because the neutral emoji still look like white people, just with yellow skin, and partially because:

The folks I talked to before writing this story said it felt awkward to use an affirmatively white emoji; at a time when skin-tone modifiers are used to assert racial identity, proclaiming whiteness felt uncomfortably close to displaying “white pride,” with all the baggage of intolerance that carries. At the same time, they said, it feels like co-opting something that doesn’t exactly belong to white people—weren’t skin-tone modifiers designed so people of color would be represented online? 2

Aditya Mukerjee wrote, in Model View Culture, about the fact that he’s unable to write his name in Bengali, his native script, because unicode never got around to including it. He writes:

Perhaps I wouldn’t mind that the emoji world now literally has “colored” people, if it weren’t for the timing. Instead, what could have been a meaningless, empty gesture becomes an outright insult. You can’t write your name in your native language, but at least you can tweet your frustration with an emoji face that’s the same shade of brown as yours!

Fast forward to 2018, where researchers at the University of Edinburgh found:

…the vast majority of skin tone usage matches the color of a user’s profile photo - i.e., tones represent the self, rather than the other. In the few cases where users do use opposite-toned emoji, we find no evidence of negative racial sentiment. Thus, the introduction of skin tones seems to have met the goal of better representing human diversity.

Good news.

Let’s talk about unicode, again

v1 of Emoji Haiku was pretty straightforward to build - I just grabbed an HTML file of emoji names and descriptions3, and worked with that. v2 is signficantly more complicated, and parses the plain text unicode spec files.

The unicode spec is super super complicated. Here’s a few things that I learned along the way:

  • A vast number of emoji (e.g. ✏️ ‘pencil’) come in both text mode (✏) and in emoji (✏️) mode. Some of them default to text, some of them default to emoji. So whenever you see ✏️ written, what your software sees is ✏ (lo-fi text-version pencil) + U+FE0F4 (the invisible “actually, please make that look like an emoji” character).
  • Skin colors work on some emoji, but not others. For example:
    • ⛺️ (tent) obviously doesn’t support skin colors
    • 👋 (waving hand) + 🏽 (medium skin tone) = 👋🏽. Seriously! You just stick the two different characters next to each other and they magically combine ✨.
    • 🏂 (snowboarder) technically does support skin colors, but you can’t see any skin anyway.
    • 🧛🏼‍♀️ Vampires do support skin color, but this is explicitly noted in the specification as ‘unusual default skin tone’, with the implication that your results will probably vary. On Android, their skin is always the same shade of grey, on iOS, they go from light grey to dark grey. Here are vampires in the default + all other skin tones, see how they render on your phone / computer! 🧛‍♂️ 🧛🏻‍♂️ 🧛🏼‍♂️ 🧛🏽‍♂️ 🧛🏾‍♂️ 🧛🏿‍♂️
  • If you do choose a skin color, it automatically implies that you want the character rendered as an emoji instead of text, and, according to the specification, you Should Not™️ add the magic U+FEOF “please make that look like an emoji” character. There is, however, an explicit acknowledgement in the spec that it’s hard to get this right and that everyone’s done it wrong for years, so… I guess we’re stuck with this now?
  • A vast number of emoji, which look like single characters, are actually comprised of other emoji, joined together. We already saw this a little bit with the waving hand example (👋 + 🏽 = 👋🏽), but did you know that a woman astronaut 👩‍🚀 is actually a woman 👩 + a ZWJ + a rocket 🚀? The ZWJ is another invisible character - it stands for ‘Zero-Width Join’, and you can think of it like a thin layer of glue between the characters on either side of it. I find this combing mechanism (woman 👩 + rocket 🚀 = astronaut 👩‍🚀) endearing and poetic5, but it’s a pain to work with in code because the meaning of a character can change based on its context6.
  • There’s two different ways of constructing gendered emoji, and while it looks like they’re similar, they’re really, really not.
    • The aforementioned woman 👩 + rocket 🚀 = woman astronaut 👩‍🚀 approach
    • Or, the person running 🏃‍ + male sign ♂ = man running 🏃‍♂️ approach.
  • Once you consider all of these things together, you can end up with some really complicated sequences. A man pilot: medium skin tone 👨🏽‍✈️ is actually 👨 (man) + 🏽 (medium skin tone) + ZWJ (invisible glue) + ‍✈ (lo-fi airplane) + U+FE0F (please spritz up my lo-fi airplane into a shiny emoji-style one). That is 5 characters, all magically smooshed together. ✨

Unicode and encoding schemes

To make things even more complicated, programmers usually can’t just think about characters - there’s also a bunch of different ways that you can convert those characters into bits and bytes that the computer can read. The method used to convert these characters into bytes is called an encoding scheme, and lots of stuff these days uses one called UTF-8. Encoding schemes are the sort of thing that you don’t notice until something is wrong, but stuff goes wrong frequently enough that you’ll probably know what I’m talking about.

Have you ever mentioned your ‘resumé’ on a website and discovered that it’s been repeated back to you as ‘resumé’? Or copy-pasted some text from Microsoft Word and found that all the apostrophes have been turned to ‘â��’? These are encoding issues - the programmer either forgot to think about it, or took a guess at what the encoding of your input was, and guessed wrong7.

Did you ever notice (back when people paid per SMS) that if you typed certain characters, suddenly your Nokia 3310 would tell you you only had 67 characters to work with instead of the standard 160? Turns out you can check this on an iPhone too! Turn on SettingsMessagesCharacter Count. Start typing an SMS (you need the green bubble instead of the blue one, which is iMessage), add an emoji, and watch the character count change suddenly!

Type 🦄, watch the number of remaining characters drop!

That’s because some characters aren’t representable by GSM-7, the standard SMS encoding, and so your phone switches to UCS-2, a different encoding which can represent more characters, but also uses 2 bytes per character. Under UCS-2, your phone can’t fit as much text into the same amount of data, because each character takes up more space – and so you can’t send as much text per SMS.

Doing the numbers:

  • A normal SMS is 160 characters, using GSM-7, which uses 7 bits per character. There’s 8 bits in a byte, so the size of a normal SMS message is 160 characters × 7 bits per character ÷ 8 bits per byte = 140 bytes.
  • A ✨ Fancy UCS-2-encoded SMS 🦄 is 70 characters, using UCS-2, which is 2 bytes (or 16 bits) per character. The size of a UCS-2-encoded SMS is therefore 70 characters ️️️× 2 bytes = 140 bytes.

To the computer, they’re exactly the same size!

Remember how I said earlier that lots of stuff uses UTF-8 encoding? Well, UTF-8 is even quirkier than the encoding schemes used for SMSes, and different characters can take up different numbers of bytes. For example:

  • The letter ‘A’ requires 1 byte.
  • The letter ‘ü’ requires 2 bytes.
  • The character ‘€’ requires 3 bytes.
  • The character ‘🁗’ (DOMINO-TILE-HORIZONTAL-05-038) requires 4 bytes.

This is called a variable-length encoding. Emoji are pretty late to the unicode specification (and presumably used less frequently than, y’know, letters), so they’re all towards the back of the list and require more bytes to represent. For eaxmple, our man pilot: medium skin tone 👨🏽‍✈️ from before is comprised of the unicode codepoints 1F468 1F3FD 200D 2708 FE0F, which is (4 + 4 + 3 + 3 + 3) bytes = 17 bytes! There are even unicode sequences where less bytes are required to write out the official description than the actual character (‘👩‍⚖️’ is 13 bytes, but the textual description ‘woman judge’ clocks in at only 9). What a world we live in!

Lots of software is still really bad at handling unicode.

Given how complex all of this emoji stuff is, it’s super hard to get these details right. In re-building Emoji Haikus, I’ve discovered and read about bugs in lots of places:

  • The Microsoft’s EdgeHTML Renderer (RIP) had issues with displaying a number of Emoji back in September 2017. What I particularly like about this bug is that it was marked as fixed, and then discovered to be not really fixed - it says something about the complexity of the system or of the implementation that it’s hard to tell whether a bug was actually fixed or not.

  • Chrome on OSX (and probably on other platforms) doesn’t correctly render emoji with ZWJ (‘invisible glue’), unless you set the font to a specific emoji font:

    Google Chrome, shows male and female symbols separated from their host emoji Apple Safari, shows properly rendered emoji

    Google Chrome on left, Apple Safari on right

  • Adding a MEN WITH RABBIT EARS emoji 👯‍♂️to my Twitter handle broke the code that embeds tweets into my website due to an encoding issue. Wow. I don’t even know who is at fault for this. Is it Twitter? Is it the software library I’m using for putting tweets in my blog? In the end, I just removed the emoji until I’d finished writing this article 😅
  • It’s been an absolutely nuisance editing this article with the text editor I’m using, because it treats each character from those 5-character magic glue sequences (our pilot buddy again 👨🏽‍✈️) as separate characters – even though they’re invisible – which means I have to press five times just to skip past him.

Ok wait, so… why a rewrite?

I decided at some point that it’s probably time to incorporate all the new shiny emoji stuff into Emoji Haikus, and after interviewing a candidate who was using Serverless to deploy code to AWS Lambda recently, thought that it could be a good thing to learn about. The initial version was running on AWS Lambda too, but it used a patchwork of hand-rolled stuff and wasn’t really maintainable.

Did this warrant a from-the-ground-up rewrite? … Probably not, but that’s what I did 🤷

So what’s new?

Emoji Haiku 2.0 now allocates:

  • Skin color, to any emoji that supports it
  • Gender, to any emoji that supports it.

This means that, when the text says “ASTRONAUT”, you could get any of 👩🏻‍🚀👩🏼‍🚀👩🏽‍🚀👩🏾‍🚀👩🏿‍🚀👨🏻‍🚀👨🏼‍🚀👨🏽‍🚀👨🏾‍🚀👨🏿‍🚀. Super cool!

By default it does this randomly, but you can also choose specific options on the page and have them apply to everyone. It’s tucked away under ‘Display Options’.

So, go and generate yourself an Emoji Haiku here (they make for super cheap Christmas presents! 🎄🎁🎁) or follow @emojihaikus for one every 6 hours. If you’re a code person, you can examine some of my questionable choices on Github.


  1. I think skin color was actually around when I wrote the first version of Emoji Haiku, but it wasn’t as widely supported at the time. 

  2. I personally find this extremely relateable - we had a similar conversation about the protocol on this at my place of work, and it’s literally only white people still using the yellow emoji - myself included. 

  3. It was an earlier version of this 42.5 MB weapon of a webpage

  4. The U+FEOF format is called a ‘unicode codepoint’ - basically, in unicode, every letter, every symbol, and a bunch of weird non-printable characters is represented by a specific number, defined by the specification. ‘Codepoint’ here is a fancy word for ‘specification-allocated number’. I tend to just use the word ‘character’ in this article – ‘codepoint’ is the more-precise, but less commonly used, name. 

  5. Other great examples include 👱‍♂️ man + 🍳 cooking = man cook 👨‍🍳 (surprise!), and 👩 woman + 🎤 microphone = 👩‍🎤 female david bowie

  6. In particular, the code I wrote was naïve about this, and so I had to spend a bunch of time later untangling that. In the end, I hacked it in, rather than redesigning what I already had, because the hack doesn’t make things too much worse. My old manager at Google (Hi Michael!) had a ‘three hacks’ rule - don’t bother re-engineering or redesigning anything until you’ve already made three quick-and-dirty fixes to the existing code. Waiting until you get to your ‘third hack’ means that you have a better idea of what the code actually needs to do - if you build it after one hack, you’re going to swing a lot in one direction, and then having to swing back a lot next time you change it as well. On the other hand, after you’ve already hacked a few things in, you’re probably not learning much new every time after, and the cost of working in increasingly hacky code is getting higher and higher. 

  7. This is almost always unintentional - it’s only a relatively recent development that programming languages have been designed with attention to this. Now, it’s harder to make these mistakes by accident than it used to be – but still very doable when you’re working across multiple operating systems and multiple programming languages. 

  8. Fun fact - this domino also exists in three other rotations (🁋 🁗 🁽 🂉) and then names for all of them use number formats like ‘03’ and ‘05’, which implies that someone maybe expected one day to add dominos with more than 9 dots on them? To me, the fact that there’s this level of granularity for freakin’ dominos, but not e.g. the aforementioned proper support for Bengali, indicates that we need a broader range of voices working on technical ownership and guidance committees. 


I sit in 13A.

I board the plane to Addis. I am the sixth person on the plane, but I get to my seat (12A) and it is taken.

No problem. I sit in 13A instead. A man sits next to me, he is friendly. His ticket says 13A, and makes no mention of the fact that I’m in his seat. He receives a phone call. Someone comes with the ticket for 13C, and says ‘Excuse me, you’re in my seat’. But 13C is on the phone, and doesn’t speak much English, and most importantly doesn’t care. After all, it’s just a seat on a fifty minute flight. I explain to the new man that nobody cares, and if he doesn’t mind, he’s best off just taking any seat somewhere else. But he insists. He calls the flight attendant.

The flight attendant asks for the ticket of the man in 13C. He produces 13A. She asks me for my ticket. I produce 12A. She asks 12A for his ticket. He produces 12J. Someone is in 12J too, and it appears that she has a very good reason for being in 12J, judging from the length of the conversation.

The flight attendant doesn’t know what to do, and escalates the matter to his colleague.

Meanwhile, the man is all in on 13C. It is rightfully his! It was ordained to him, by a small stub of paper printed only an hour earlier. He allows other passengers through, apologises for being in the way, hovers over the seat, waits, expects the flight attendant to sort it out. My nonplussed friend in 13C is still on the phone.

Now there are two flight attendants trying to convince the Rightful Holder of 13C to sit somewhere else, but now all the good seats are gone. He gets mad - nothing is happening, and nobody feels much sympathy, for anyone except the flight attendants.

And then, the flight attendants solve the puzzle by asking me to move. I agree; it’s just a seat on a fifty minute flight, after all. But at that exact instant, the man has stormed off down the aisle, and now sits grumpily at the back of the plane.

I sit in 13A.


Lofoten // Norway

I went to Lofoten in Norway for a four-night getaway with some friends this week. Here are some photos.

I took all of these on an Olympus EM-10 (original, there’s a newer Mk II now) with an Olympus 9-18 mm f4-5.6 lens, some of them with a polarizing filter.

This was my first time taking photos with such a wide lens – I bought a 25mm (50mm equiv.) prime lens a few months after I bought the EM-10 two years ago, and I’ve used it ever since. I felt like the colours weren’t quite as vivid with this lens, but it was pretty nice to be able to capture some sweeping landscapes. The setup is also ludicrously small and light, which makes it perfect to stick in a backup and scramble up mountains (e.g. Reinebringen).

Getting there, where to stay etc.

I’m not by any means claiming that this is the best way to do it, but it worked really well for us:

  • The southern part of Lofoten is less built up than the north. We stayed at Toppøy Rorbuer (run by the same people as Sakrisøy Rorbuer, linked), and loved the relative isolation. Svolvær, on the other hand, is probably easier to organise / has more tours etc, but it’s definitely a tourist town.
  • Shoulder season was a great time to go. It wasn’t relatively crowded and we managed to book a cabin a month out. You can see the northern lights at night (though, not as strongly as later in the year), and still get 11 hours or so of daylight.
  • Fly into Bodø, organise car rental at the airport, and take it on the ferry to Moskenes. It’s much easier to get around with a car and the roads are amazing fun to drive (we saw a number of buses around, but I’d want to do more research before I relied upon it). Otherwise, car rental is a little more fiddly in the south, and the alternative is to go from Svolvær, which is a two hour drive away from where we were staying.
  • Cook your own meals mostly. Eating out is expensive (NOK 300+ / AUD 50+ / EUR 35+ / USD 35+ for a main meal). But also, try Tørrfisk (Stockfish) and have a glass of Akavit sometime.

Australian Election Map 2016  

I wrapped up at Google last week, but I spent the months before I left working on an Australian Election tracker map, which was completely unlike any project I’d worked on before.

 

We first assembled the team for this about 10 weeks before the election, from a number of teams across Google Sydney. By the time we actually started building anything, it was about six weeks out from the election.

Team photo

Team photos. On the left, you can see me stuffing T-shirts into Barry, the friendly office bean-bag shark.

The website had two main goals - to provide information on where to vote, and to provide live results after the official data feed went live on the day.

Tech stack

  • We used Google App Engine to serve everything. App Engine was super easy to use - being able to write code and have it scale automatically is a huge win to me.
  • The backend was written in Go. In addition to the HTML / JS / CSS for the web app, it served static geographical data, which is publicly available from the Australian Electoral Commission (AEC).
  • For data sources that needed to update live, we used Firebase. This included live election results, and information on which polling booths had sausage sizzles and cake stands (it’s an Australian tradition).
  • We wrote the frontend in Dart, using Angular 2 as the framework to tie everything together.

I spent most of my time working on the geographical data backend and building out the web UI, so in this article I’m going to focus on those aspects.

Serving geospatial data

The representation of election results on a map was seen to be one of the most interesting differentiators between Google’s election map and other coverage. This turned out to be significantly challenging to implement though.

Basically, we wanted to be able to:

  • Serve the shape for each electorate as GeoJSON, or ideally as Google Maps Encoded Polyline, so we could render it using the Google Maps API.
  • Do spatial lookups:
    • Which electorate are in a given bounding box?
    • Which polling places are in a given bounding box?

We were able to make the assumption that geographical data was static - once the electorates and polling places had been defined, they weren’t going to change frequently. This meant that we could build them directly into whatever serving infrastructure we used, which makes scaling easier.

We started by investigating Carto (formerly, CartoDB), and found that it was a lot of extra infrastructure for the small amount that we wanted to use. We basically would’ve ended up falling back to their SQL API for queries, and so we might as well just use PostGIS in a PostgreSQL database and use that instead 1.

PostGIS, however, is a little tricky to get set up. We got it running with the sample AEC data inside a Docker container, with the intention to serve static geographical data from a cluster running on Google Container Engine.

This meant that we’d also have configure App Engine to talk to our PostGIS instances, and scale them separately, which adds another dimension of complexity. One of our engineers started wondering if there was another way, and knocked up a prototype solution directly in the Go app using go-shp (to read the shapefiles from the AEC) and rtreego (for lookups). This turned out to be a significantly less complex solution. Our code loads the shapefiles once on startup, and then serves them from memory for the lifetime of the service.

The AEC shape data worked out to be around 46MB for all of Australia, so we manually ran some preprocessing using Mapshaper to simplify the polygons down to more efficient sizes for different zoom levels. Using this scheme, we managed to keep the page load down to < 500kB.

This turned out to be super performant - one engineer estimated that each instance was capable of roughly 4x the same load as the python servers he’d written for a previous, similar project. We managed to squeeze even better performance through some clever caching design from some of the engineers on our team.

Frontend: Angular2 and Dart

I should preface this section by saying that I don’t have a lot of experience with web frontend development. If I’ve said something that doesn’t sound right to you, please let me know - I’m keen to learn :)

Angular 2

I really enjoyed working with Angular 2 - it seems like the authors have further refined the concepts from Angular 1, and streamlined the whole framework. Once you learn the ropes (which was only a few days of struggling for me) it’s a really fast way to get something up and running. I don’t have a lot of experience with Javascript and web UIs in general, but Angular 2 made development fun.

Hemicycles and half-donuts

Angular isn’t without its frustrations though: there are some things that Angular’s abstractions just don’t work for. I implemented this rather fabulous component:

76 needed for majority 72/150 seats declared

Fun fact - this seating arrangement is officially called a Hemicycle. I didn’t know this until the BBC used it on their Brexit real-time results feed (though this seems like a weird choice for a Yes/No vote with no quota). Our version of the hemicycle is thus known throughout the code as the half-donut component:

Half-donut component

YOLO.

Anyway, using Angular for this got a little gnarly. There’s a lot to this component:

  1. Combine certain parties into representative coalitions
  2. Sort all parties from most to least votes
  3. Allocate them to the left or right of the hemicycle based on whether they’re a “left-leaning” or “right-leaning” party2.
  4. Draw a donut slice (yes, that’s what its called in code) for each party. Each slice has to have the correct starting angle and ending angle.

Rendering is the part where the abstraction felt a little leaky, and Angular felt like more of a toolkit to get the job done rather than a comprehensive solution to “do things right”. As I write this section, I’m starting to wonder if Angular’s actually about as good as it can get here, and the web is just a bit of a mess.

My first rendering algorithm was:

  • The HalfDonutComponent generates a set of DonutSlices, each having a starting angle, an ending angle, and a fill colour
  • The template for the HalfDonutComponent feeds the DonutSlices into a DonutSlicePipe, which generates the appropriate SVG path. Unfortunately, there’s no way to render arcs using angles in SVG - they use X,Y coordinates instead (whyyyyyyyyyy?). This meant that I had to do more trigonometry than I would’ve liked.

This definitely worked, but in the interest of keeping the code simple and maintainable (and at the suggestion of a colleague), I changed it to the version embedded above (if you’re inclined, open the inspector and have a look at the generated SVG).

The new rendering algorithm:

  • The HalfDonutComponent generates a set of DonutSlices, each having an ending angle only.
  • There is a single half-donut path, which is reused multiple times.
  • The component creates multiple copies of this half-donut path, colours them differently for each party, and uses transform="rotate(angle, X-center, Y-center)" to angle them into the right positions (no complicated trigonometry!).
  • Right-aligned parties get rotated with a negative angle instead of a positive angle.
  • Clip the SVG, so that the area under the half-donut isn’t visible.

This produces a pretty nice result for a lot less code:

Dart

Dart felt like it was getting in the way as much as it was helping. Dart’s object streaming framework works really well with live updates from Firebase - it’s possible to just rebind parts of the UI as things change. Dart’s error messages, however, were often hard to understand. Debugging was complicated, because Dart is only natively supported in Dartium, a custom build of Chromium. Builds took a surprisingly long time, and optional typing is very error-prone - we had a hard-to-diagnose JS error related to this that took weeks to find, mostly because we hadn’t enabled the relevant static analysis on build. In my opinion, this shouldn’t be necessary - language behaviour that changes between development and production builds is a bad idea. I think I’d like to try Typescript next time.

See also


  1. I should note that if you were ok with using Carto for web map rendering as well, then it works really well as an integrated suite. That’s the approach that Democracy Sausage were using. 

  2. Australian politics is mostly a two-party affair (but decreasingly so!). The Australian Labor Party is usually represented on the left of the hemicycle and the coalition of Liberal, National, Liberal-National, and Country Liberal parties (‘The Coalition’) is usually represented on the right. We needed to do something with other minor parties that get votes, so we put smaller slices in the middle, but if a party gets less than three votes, it’s aggregated into a ‘Other’ component. 


Embassy Reviews (a Twitter bot)  

There’s a meme in the programming community about people never using a product in the way you’d expect:

Drinking from a Glass

r/ProgrammerHumor: “Accurate depiction of end users”

What are reviews for?

For all user-generated content on the Internet, there’s a signal-to-noise ratio. My theory is that in situations where the abstraction doesn’t work, there’s very little signal – it’s hard to create meaningful content when the context is mostly nonsensical. As it turns out, the noise can be pretty funny:

Reviews of embassies fit the bill for this because reviews were designed to help individuals make comparisons between competitors. Reviews work well for any situation where there’s product differentiation – cafés, restaurants, and software are classic examples, which is why they’re rated so frequently. They work less well for pure commodities – at a petrol station or a supermarket, you’d only write a review if something really bad happened. They work terribly for things that aren’t competitive – for example, bridges (“The supporting cables used to vibrate, then they put supporting supporting cables. Good looking bridge, actually quite pleasant to walk across.”).

To me, embassies are the epitome of non-competitiveness - they’re a requirement to get a visa, but people don’t choose countries to go to based on reviews of embassies. In the words of one particularly frustrated reviewer:

Self-expression within bounded systems

On the other side of the coin, the way people interact with strict systems and handle rules and restrictions is a fundamental act of self-expression.

Indeed, the reason why all kinds of games are interesting – board games, sports, video games, whatever – is because by exploring the restricted problem space, you’re learning more about yourself, and about those who explore with you1.

The language used around the AlphaGo vs Lee Sedol match earlier this year really brought this to the front of the tech community’s mind – Go players knew that there was something inhuman about the very moves that AlphaGo played. Whether this was beautiful, or sad, or just weird is a matter of taste, but there’s no doubt that the moves were fundamentally inhuman.

The review box on Google Maps, like a game, is a bounded system – a review is attached to a place, it has a writing prompt, the user is to give a rating out of five stars.

On these fringes of what the product was originally designed for, and within the confines of the system, people self-express what’s important to them in the weirdest ways:

Presenting Embassy Reviews

So, allow me to introduce Embassy Reviews on Twitter.

Tweets are reviews taken directly from Google Maps, chosen at random from more than 11000 embassies, and 900 cities globally. Tweets are (usually) in English, four times a day, and are unfiltered - though sometimes the reviews are automatically edited down to fit in a tweet.

If you’re interested in how it’s all put together, take a look at the source code on Github.


  1. I have no idea where I got this idea from, but I remember reading that chess is widely perceived as a form of self-expression for this reason. Interviews with Jamin Warren and Robin Hunicke in Offscreen issues #13 and #14 respectively get pretty close to the concept. 

← Older Index