WhatsApp animated stickers and synchronisation

Sai Rithwik M / July 15, 2021

5 min read

Some Intro#

WhatsApp is one of the most widely used messaging apps in the world. One of the major reasons why it's popular is because of its simplicity to chat and a simple UI. Their integration with stickers is something that WhatsApp users love. In other apps like Discord, custom emojis used to be an alternative, but one issue with them is the visibility. One could not read the text clearly on the emojis as they were usually 128x128px in size. An alternative would be sharing images, but accessing them from a pile of pictures in a gallery was a tiring task. Stickers solve this problem of conveying your message/reaction and ease of access.

What's the feature?#

One of the most underrated features of animated stickers is their ability to sync. If two people send the same animated sticker, you would observe that both are in sync with each other, ie. both are in the same frame. Here's a small preview of the feature!

Sticker Sync demonstration

Sticker Sync demonstration

How do they manage to be in sync?#

Here was the experiment I tried

  • I have a long animated sticker(~8-10s), lets assume it takes t seconds
  • I send it
  • For suppose we are at frame f1
  • I send the same sticker again
  • Sticker 2 also starts at frame f1

My initial theory for this was that WhatsApp has a clock-based system. The stickers are stored locally. So, the moment you share the same sticker again, the second sticker starts at the time the first sticker currently is at.

An experiment I have tried to validate is by sending a sticker, moving to a different chatbox and going back to this chatbox again and I observed that the sticker starts from the beginning. So the frame sync is somewhat complicated.

But then, the issue is stickers are in sync even if a friend of mine is sending the same sticker. So there might be happening something from the server's end too. But turns out it wasn't this complicated.

I discussed this with a few friends of mine and we did some googling and found WhatsApp Stickers API codebase.

Here's a small piece of code that got our attention

StickerPackLoader.java
 static byte[] fetchStickerAsset(String identifier, String name, ...) throws IOException {
   try (final InputStream inputStream = contentResolver.openInputStream(getStickerAssetUri(identifier, name));
       final ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
       if (inputStream == null) {
           throw new IOException("cannot read sticker asset:" + identifier + "/" + name);
       }
       ...
}

From this piece of code, we can see that there is some sort of identifier for each sticker asset and a name that uniquely identifies a sticker. So this means that the sticker syncs locally and not on a server end.

Here's another function which seemed suspicious

StickerPackListActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    whiteListCheckAsyncTask = new WhiteListCheckAsyncTask(this);
    whiteListCheckAsyncTask.execute(stickerPackList.toArray(new StickerPack[0]));
    setContentView(R.layout.activity_sticker_pack_list);
    packRecyclerView = findViewById(R.id.sticker_pack_list);
    ...
}

So from the first function, we can see that there is a term called savedInstanceState which returns which frame the sticker essentially is in. There are also calls of terms like RecyclerView which could essentially mean that the frames could be reset to the starting frame when the app doesn't notice activity around a specific sticker. This is true as we have observed that on moving off the chatbox and getting back to it we see that it begins from the first frame.

This essentially would be helpful to make some conclusions regarding how it works under the hood. The intricacies of the implementation might be different since this is just the code for the API, but overall this seems to be my rough understanding:

  • There is a cache for each chatbox
  • User sends a sticker in a chatbox
  • The state(frame) of the sticker is checked in cache using it's unique identifier
    • If it is not in cache,
      • Add it to the cache with the timestamp
    • Else
      • Find the differnce in the current timestamp and the cache timestamp
      • Using this find the frame it probably might be in
      • Start from that frame
  • Once the user gets out moves out the view of the sticker on app, the cache is cleared, and hence it starts from the beginning on viewing it back again.

Closing Remarks#

A reason why I wanted to write this blog is because of a new sticker feature that was released on Discord recently. It doesn't support sticker synchronisation which makes the UX a bit odd, and since then I have had some more appreciation for the tiny design decision in WhatsApp.

Discord Sticker

Discord Sticker

Overall, this was a pretty interesting thing to look up. Major credits for this article goes to Vijay, who helped in finding the codebase and the important functions related to it.