Making of discord banners

Story of development service that provides discord user profile's as svg images

I had an idea what if we could generate images from Discord user profiles and use them anywhere, like in a GitHub README?

This would make it easy to showcase dynamic profile banners, stats, or custom visuals without any manual updates.

The first version of the working application looked something like this -

  1. User sending request to express server with discord's user id
  2. Discord.js requesting user's data by user id
  3. Rendering user's data using node-canvas to PNG image

But overall, it worked well. However, it was somewhat unoptimized because, for example, if we make 100 requests for a banner on an Express server, it will fetch the user's data and render 100 times!

Since I removed the old cache system, I eventually realized I still needed caching but in a more structured way. So, I decided to use Redis as my cache storage.

The first version of the cache system was simple:

[userId]: [banner image]

This worked well at first, but when I introduced customizable banners, I needed a more flexible approach. So, I built a full abstraction for caching banners, and the structure evolved into this:

[userId @ username @ (customize options in base64)]: [banner image]

This allowed me to cache different banner variations based on user settings while still keeping everything efficient! ๐Ÿš€

Researching on how I can optimize my image renderer, I found out that node-canvas support's not only rendering PNG's but also SVGs!

I also refactored my entire rendering system. Now, I have an abstraction for the canvas, with everything divided into layers. I also implemented a percentage based measurement system that helps create responsive images. This allows the canvas height to adjust dynamically based on user data, and everything adapts automatically. And on top of that, I can now add animated pictures, like avatar decorations and profile effects!

Even after switching the rendering system to SVG and refactoring my entire banner rendering code, the average response time was still around 3 seconds which felt quite high. That got me thinking: What could possibly be taking so long?

I started debugging and found that most of the time was spent loading user data from Discord. Each request fetched data directly from Discord's servers, which was inefficient. To fix this, I switched to retrieving user data from the Discord bot's cache instead. This change alone cut response time almost in half!

The second major bottleneck was loading external images like the userโ€™s profile picture, banner, and profile effects which took up the other half of the time. To solve this, I implemented a lightweight in-memory cache for external images. Since this required a new way to handle images, I created my own Image abstraction. Fortunately, I already had a canvas abstraction, so I simply leveraged its internal methods to handle images efficiently.

Now, even the most complex banners (including animated ones) render in under 300ms! ๐Ÿš€

After optimizing everything, I started wondering if my average response time is already under 300ms, even in the most complex cases, do I really need a caching system at all?

So, I decided to remove the banner cache system, and now everything works like a charm! The cache was occasionally serving expired or outdated data, which caused inconsistencies. Without it, the server now always provides fresh and up-to-date banners without any noticeable performance loss.

After all these changes, I ended up with a lot of code and it was quite a mess. So, I decided to switch to Nest.js! Now, my code is not only optimized in terms of performance but also well-structured with a solid architectural approach. โœจ

Right now the project is working quite stably and fast, although I still got some bugs to solve and some features to add, but you can already get your own banner [here]

Discord banner