Three things learned developing the badges feature

Published on 2022-06-21


We recently introduced developer badges to allow developers to share their achievements on readmes and websites. The badges are hosted on stardev.io and generated from current data, ensuring they're always up to date. A few interesting things came up during development that we thought would be of interest to others.

Firstly, here is a developer badge:

Check out mastodon's profile on stardev.io

It shows:

  • The developer's Github user name
  • Gravatar
  • Location
  • Overall rank and stars
  • Rank and stars for the developer's top two languages
  • The bottom strip, coloured gold for global ranking, silver for country ranking and bronze for locality ranking

The badges are implemented using SVG, as it's easy to generate them directly in markup without requiring any server side library like imagemagick.

Images in SVG

Using the developer's gravatar would make the badge much more engaging. It is possible use <image> tags inside SVG to refer to an external image:

<svg width="300" height="100" style="border: 1px solid yellow;">
  <image xlink:href="/img/logo.png" width="100" y="25"/>
  <text x="10" y="20" fill="white">An image inside SVG</text>
<svg>

An image inside SVG

However, there's a strong likelihood that browsers will block loading of an image sourced from a different domain due to CSP, meaning that the gravatars would not be shown if these badges were displayed on another website.

<svg width="300" height="100" style="border: 1px solid yellow;">
  <image xlink:href="https://github.com/mastodon.png" width="100" y="25"/>
  <text x="10" y="20" fill="white">A cross-domain image inside SVG</text>
<svg>

A cross-domain image inside SVG

The solution was for the server to request the gravatar from Github and base64 encode it for use as a data: uri:

<image href="data:image/png;base64,<base64 image data goes here>"></image>

Now all the content is served in one request from one domain.

Lazy images

These badges are potentially expensive to generate, as they involve loading all the stats for a developer as well as the call to get the gravatar as mentioned. We want to show a preview of the badges on each developer's profile page, but showing all three (global, country and locality) versions could quadruple the cost of loading the page.

A tiny piece of javascript is used to show the global badge and hide the other two behind tabs, but they are still eagerly fetched by the browser even if the user doesn't see them. We only wanted them to be loaded if the user selected the tab to see them. In the past more javascript would have been required to insert the <img> dynamically into the DOM when the tab was selected but this is no longer necessary thanks to the loading property:

<img loading="lazy"/>

This is supported widely for images and will ensure that images are only loaded when they are in, or near, the viewport.

Ad blocking

After releasing the feature the server logs were tailed to see if the badges were being used. The badges were being served as previews on the developer profile pages but often there was no corresponding activity being reported from client-side analytics. As an estimate, approximately 40-50% of visitors to the site have client-side analytics blocked or disabled. In some ways this was encouraging - twice as many people are using the site than it appeared!