DEV Community

Cover image for Auto generate unique Avatar colors randomly
Piyush Kumar Baliyan for AdmitKard

Posted on

Auto generate unique Avatar colors randomly

JIRA Avatar colors

This is how JIRA shows each user avatar

Think for a moment about when you visit online discussion forums. Many of the users don't have any profile pic uploaded. Instead, there are just initials and background colors for the User Avatar.

And anytime you refresh, the background color remains the same.
Now take a second to think about how is this done.


TLDR; Using string hash

There can be two straightforward ways to do it.

  1. You can store a background color, for each user in DB, and fetch every time with other user details OR
  2. Generate a unique (but consistent) background color based on some unique user attribute (like email, username, or userID).

Let's start on #2.

Generate random color

Let's first learn some basics of CSS Colors in the webpage. And we will follow the following journey to do this.

  • Learn about CSS color formats
  • Choose a color format
  • Basics of string hashing
  • Normalize hash
  • Generate unique CSS color using Hash

CSS color Formats

From this MDN page, we can figure out the common CSS color formats:

  • Named Colors
    • red, green, blue, aqua
  • RGB Hex
    • #RRGGBB or #RGB
    • #3b49df is blue, #333 is dark-grey
  • rgb() function
    • rgb(255, 255, 255) is white
    • rgba() for additional alpha (transparency) value
  • hsl() function
    • hsl is Hue-Saturation-Lightness
    • Hue - is for choosing color using the degree
    • Saturation - less saturation is grey, more saturation is bright color
    • Lightness - less lightness is black, more lightness is white
    • hsla(), for additional alpha value HSL Color wheel There are some others, but we will stop here.

Choosing color format

Now, we can go with RGB and HSL both, but for the task at hand, HSL is comparatively easier to work with. ¯\_(ツ)_/¯ Why, you ask? Trust me...
And, you will find in end of article.

Creating string hash

Now we need to reduce a string to a simple number so that we can use that number to generate our color.
I'll dive deep into hashing in a different article (I mean, come-on... Computer Science community have dedicated huge time to create better hashing functions). But let's take an overview of what hash is.

From educative.io

Hashing is the process of converting a given key into another value. A hash function is used to generate the new value according to a mathematical algorithm. The result of a hash function is known as a hash value or simply, a hash.
Hash function block diagram

Let's understand by an example and taking a string: Piyush Kumar Baliyan. Don't take this to be a definitive article about hashing. I am still learning about hashing and only know the basics.

Attempt 1

const getHashOfString = (str: string) => {
 return str.length;
}
console.log(getHashOfString('Piyush Kumar Baliyan')); //20
Enter fullscreen mode Exit fullscreen mode

This is a super simple hash function, but it has very high chances of a conflict. (Conflict is when two different strings can generate the same hash).

Attempt 2

Lets modify the function to calculate the total of character code of each character in the string.

const getHashOfString = (str) => {
  const charArray = Array.from(str);
  return charArray.reduce((total, _char, index) => {
    return total += str.charCodeAt(index);
  }, 0);
}
console.log(getHashOfString('Piyush Kumar Baliyan')); // 1922
Enter fullscreen mode Exit fullscreen mode

This is better hashing as it has fewer chances of conflict, but any string that has the same characters will result in conflict.

Attempt 3

Now, lets take total of charCode*index.

const getHashOfString = (str) => {
  const charArray = Array.from(str);
  return charArray.reduce((total, _char, index) => {
    return total += (str.charCodeAt(index) * index);
   }, 0);
  }
}
console.log(getHashOfString('Piyush Kumar Baliyan')); // 18329
Enter fullscreen mode Exit fullscreen mode

This is better and has fewer chances of a conflict.

But this becomes a problem as this number (charCode*index) can become very large for large strings, and can still conflict Maths (╯°□°)╯︵ ┻━┻.

Attempt 4

Attempt 5

.

.

.

Attempt x-1

Little better, but still no-where near to the actual hashing algos like md5 and sha.

const getHashOfString = (str: string) => {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  hash = Math.abs(hash);
  return hash;
};
console.log(getHashOfString('Piyush Kumar Baliyan')); // 3206952792
Enter fullscreen mode Exit fullscreen mode

Normalize Hash

Now we have a hashing algo, but it returns any number, and we need numbers like these:

  • Hue 0-360
  • Saturation 0-100
  • Lightness 0-100

So let's create a function normalize to get the hash number to within our range.

const normalizeHash = (hash: number, min: number, max: number) => {
  return Math.floor((hash % (max - min)) + min);
};

const h = normalizeHash(myHash, 0, 360);
const s = normalizeHash(myHash, 0, 100);
const l = normalizeHash(myHash, 0, 100);
Enter fullscreen mode Exit fullscreen mode

Generate Unique Color

Now, we simply create a string using our h,s,l values.

const hRange = [0, 360];
const sRange = [0, 100];
const lRange = [0, 100];

const generateHSL = (name: string): HSL => {
  const hash = getHashOfString(name);
  const h = normalizeHash(hash, hRange[0], hRange[1]);
  const s = normalizeHash(hash, sRange[0], sRange[1]);
  const l = normalizeHash(hash, lRange[0], lRange[1]);
  return [h, s, l];
};

const HSLtoString = (hsl: HSL) => {
  return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
};
Enter fullscreen mode Exit fullscreen mode

Here is what the output looks like:

Avatar generator output

That's it!?


.......

.......

.......

NO!NO!NO! Not so Fast

Hold it

If you see this can generate colors that are too harsh (saturated), or just too grey. And can generate colors that are too white or just too dark.
Avatar Generated Colors range issue

And this was the reason to use HSL. In RGB, the individual values or r,g,b makes the illuminition (lightness) of a color, and it is little difficult to control.

Just do this to get better color generation.

const hRange = [0, 360];
const sRange = [50, 75];
const lRange = [25, 60];
Enter fullscreen mode Exit fullscreen mode

Now, play around with the below JsFiddle, and see how the saturation and lightness ranges affect the color generation.

  • Use the light/dark theme to see which color ranges look good on respective theme.
  • Drag the saturation to left, see how colors turn grey
  • Drag the lightness to right, see how the colors become white (and reverse - black).
  • Use range, to find out the best saturation and lightness for your brand.

Bonus tip

Use the hue limiter to generate colors like all green tones, or all purple tones, to better match with your brand colors.

Next Time

I am gonna write about how to generate User Avatars... NFT style.
NFT Avatar


Read Next

Top comments (1)

Collapse
 
lifeofram86 profile image
Ramanand Chitravelu

Awesome stuff thanks for sharing