Optimizing letter avatar rendering


(Sam Saffron) #1

Letter avatars are our 4th most expensive route globally.

This route is responsible for retrieving and generating unique letter avatars.

For scale of the enormity of the current cost, in the last 24 hours we spent 30 minutes of CPU time in the last 24 hours on meta alone, generating these avatars. At our hosting scale it’s as though we have single CPU dedicated permanently for letter avatar generation.

CPU alone is not the issue, we also have all these pesky web requests to deliver these little images to users.

Instead we would like to investigate another approach here that is more efficient.

Option 1: ship a font and use CSS

The simplest and probably most efficient way would be to pick a pretty font from: Google Fonts and use CSS to render this stuff.

If we were to do this with “font + CSS” we must use a consistent font, otherwise positioning will be hellish and the UX. Centering the letter is not going to be super easy but probably doable.

Pros:

  • Simple to build
  • Will be very fast regardless of avatar count on screen
  • Embed freindly

Cons:

  • Centering in CSS is hard
  • We need to ship an extra icon font
  • CSS for this will be quite long
  • Not RSS friendly (unlikely to be even doable in a sane way)
  • Not Email friendly (will require a fair amount of work to get it to work in email)

Suggested markup:

<div width='100' height='100' class='letter-avatar letter-color-1 background-color-76'>A</div>

Option 2: use a font and Canvas

A slightly more complex option would be to use a font + canvas api to generate an image. This will make it slightly easier to center the letter. It may be slightly easier to integrate as well.

Pros:

  • Centering in canvas is easy

Cons:

  • We need to ship an icon font
  • We need to run a “post processor” in JS on all letter avatars
  • Impossible to do in Email

Option 3: use SVG

We could consider using a 100% SVG solution, ship all the glyphs in SVG. If we were to follow this we would have to have to ship an extra asset, cause I would not want to bloat vendor.js or application.js

Pros:

  • Can be extremely easy on embedding if all SVG is inlined

Cons:

  • Requires and extra asset
  • Very complicated to build
  • Will bloat cooked Markdown for quotes if we inline SVG
  • Unlikely to be doable in Email

Option 4: Free letter avatar service

Have Discourse host a single, “free”, letter avatar service that any Discourse customer can use, wire it in as default.

Pros:

  • Simple to integrate into Discourse
  • Can be default on with a simple off switch
  • Allows us to provide custom “default” avatar styles
  • Can run on Digital Ocean + Cloudflare and have very low running cost.
  • Simple to build
  • We can upgrade the avatar algorithm at will
  • Everyone gets to use 1 cache
  • Can reuse existing code

Cons:

  • We need to run yet another service
  • We still got to maintain the fallback
  • If cloudflare has issues, no letter avatars (need to check on great firewall of China)
  • Some people may have privacy concerns (but they still have an off switch)

Suggested markup:

Options 1, super private

<img src="https://avatars.discourse.org/a/40841852d1bb4460907de74de8b0cfba?width=50&height=50&v=1">

Guid is generated using username + random hidden site setting hash

Option 2, high letter avatar reuse and cache use

<img src="https://avatars.discourse.org/sam_saffron?width=50&height=50&v=1">

Option 3, super high cache reuse, good privacy

<img src="https://avatars.discourse.org/letter/s/100_150_10/50.png&v=1">

(100, 150, 10 are the colors being used)

(I lean toward option 3 here)


The one thing that is clear is that something needs to be done.

After thinking this through I think #4 are the best approach we can take, it leaves people a safety valve and is the least intrusive change code wise.

Also going forward it allows us to provide more “default avatar” styles that people can choose from which can be handy.

##Further work

#3 route cost wise is the standard user avatar route, this happens because we are resizing images on demand (we do keep a cache, but still). We must investigate if there is a more efficient way to size down these images, if the original avatar is already optimized, we may be able to skip the optimization stage altogether. We may also consider adding rmagick so we do not spawn a process each time we resize an avatar.


Defaulting to discourse.org CDN for avatars is a privacy and security risk
Avatar source has changed to Discourse servers on rebuild/update
Plugin idea: Image optimization with WebP
Letter avatars change to html elements (Idea for performance)
Discourse in a closed intranet
Does my Local Install Communicate With Discourse Servers?
UI breaks when letter avatar images don't load
Avatar source has changed to Discourse servers on rebuild/update
(Jeff Atwood) #2

Hmm yeah I don’t see anything simple here… even the SVG solution is “very complicated to build”

Centralized global avatar sharing service is a well known thing (vanillacons, gravatar) and probably best solution for “all site” caching of these assets anyway…

I’m not sure we should do anything here for this release?

How many days do we estimate it would take to build a simple, absolutely-no-features, just-barely-works, Digital Ocean hosted “Discourse avatars” sharing service?


(Sam Saffron) #3

Probably 1 to 2 days, its a pretty simple problem.


(Jeff Atwood) #4

OK then #4 is what you should look into @zogstrip since we could share it across all Discourse instances in the world, potentially. It has the most upside, low risk, relatively easy to build, and quite well understood.

We could build it for our own hosted sites to start as a test, limit the scope, then roll it out for everyone as a gratis service / benefit too.


(Lowell Heddings) #5

Couldn’t you create a new font that incorporated the circle into the font itself? That would remove a lot of the positioning problems and make centering and coloring the avatar very simple and keep the CSS down to a minimum.

And I haven’t looked into it, but it seems like shipping a font is a single HTTP request as opposed to many requests for each image, which would be a good thing.

Plus depending on what characters you choose, you could probably render initially as blank until the font downloads.

People with no avatar don’t deserve to have their icons rendered in RSS or email. You could swap with a standard user icon instead, or just strip it out.


(Jeff Atwood) #6

Fonts can’t really be multiple colors, not reliably. Maybe 5 years from now…

http://pixelambacht.nl/2014/multicolor-fonts/

But you could do a font on top of a font (two rendering passes with alignment).

Edit: that page has a test function, here are the current results:

  • Chrome – (none)
  • Firefox – Microsoft COLR/CPAL, Adobe/Mozilla SVG
  • Edge – Microsoft COLR/CPAL

I assume Safari would support apple SBIX there, but can’t test. Weird that Google Chrome doesn’t seem to support Google CBDT/CBLC!


(Mittineague) #7

I’m not sure all no-custom-avatar members would agree.
In fact I’m not so sure all members are even aware of the Profile setting that only works once they get out of the TL0 sandbox


(Sam Saffron) #8

We can’t really incorporate the circle because fonts are monochrome and circle is actually optional. We could possibly create a font for this that centers much better in css. But creating a font is a huge amount of work.


(Rafael dos Santos Silva) #9

I’m not sure this is the better place to add this discussion but here it goes:

It would be great if with this change we could easily change the default avatar route for users.

At my company, we have a company-wide avatar system in the http://example.com/avatar/username (the same username used in Discourse).

To benefit from cache, and because for company policies the discourse machine can’t download avatars from the service, I wrote a plugin that rewrote avatar_template on rubyland. That wasn’t enough and after that I have patched Discourse itself on javascript avatar_template lib. And after that there was a bug for avatars inside quotes.

If you guys are going a avatar service route, maybe we can help and reduce avatar logic complexity?


(Sam Saffron) #10

Sure, totally support simplifying, keep in mind though a lot of the special sauce and why this looks so good is that stuff is sized on demand, you don’t really want to give that up and have a front page with 5 megabytes of avatars.


(Lowell Heddings) #11

Went to some font site, used dev tools to change the color of two of the letters via CSS like style=“color:blue”

Maybe I’m missing something but it looks the same to me, although obviously the example font is kinda ugly.

I have no idea how hard it is to make a font. I’d think somebody out there could add some circles to some open source font though…


(Rafael dos Santos Silva) #12

Yeah, I know, and we really appreciate that. We have entire offices running with 1mb bandwidth.

At work we use a self hosted Thumbor instance with a imgmin post processing on avatars, so even with all on the same size they are lightweight enough and re-used everywhere.


(Sam Saffron) #13

This is what we generate at the moment, there is no circle.

But getting E, pixel perfect bang in the middle of that box is a very annoying thing to do in CSS especially without flexbox. Also we must use an icon font otherwise E will look different everywhere.


(Lowell Heddings) #14

All I see are circles so I’m not sure what that means.

There are also square fonts that could be optional.

And it’s simple enough to swap out which letter is assigned to which glyph. Icon fonts are just fonts.


(Sam Saffron) #15

yeah … a square font like that could work, that is an option.


(Kane York) #16

It’s an <img> with a border-radius: 50%.


(Lowell Heddings) #17

Yeah, I know. I’m saying that it’s rendered as a circle everywhere on every Discourse forum that I’ve seen. So I don’t know why the square is important in the first place.

And if somebody wanted squares instead, it would just require a separate font that used squares instead of circles.

I mean it’s just 26 glyphs in an icon font when it comes right down to it.


(Mittineague) #18

AFAIK there are some Discourse forums that have opted for square avatars

img.avatar, div.avatar-wrapper { border-radius: 0px; }

(Simon Cossar) #19

With inline svg you would only need one file for each letter. You could color the avatars by assigning a color class to each user.


(Jakob Borg) #20

So the results from this cloud avatar generation service would be cached locally, much like the gravatar service is, right? As it’s conceptually the same thing, just a different generation algorithm than they use…

(I wouldn’t like to add hundreds of requests to a cloud service very far away from my hosting location on each front page load; currently the latency is sort of predictable at least as it all comes from the same place.)