Science of beautiful color gradients

Have you ever come across designs that contain gradients? Notice how the gradients always look so fresh and clean in the design, but when you implement it in code, it usually doesn’t look as good as you expected it to be. I felt the same and ended up going down the rabbit hole of studying the science of colors and gradients. As a result, I created a Javascript library that always returns beautiful gradients - like the one that you are seeing above.

Intro

Generating beautiful gradients can be a challenging task, especially when we want the computer to automatically generate one for us. It is common for us to come across libraries that produce gradients between two randomly chosen colors, which often provides pretty bad results.

However, by studying the science of colors and gradients, I have developed an algorithm for randomly generating gradients that could consistently produce visually appealing results. So sit tight and grab some popcorn, because it’s time to dive into the beautiful world of colors and gradients.

Color Gradients

Let’s understand how a gradient is rendered. The computer uses RGB (Red, Green, Blue) color mode and calculates the color values for each pixels of the gradient by taking the mathematical average of the RGB values between two provided colors.

For example, a linear gradient from RGB(0, 0, 255) to RGB(255, 255, 0)would involve calculating the intermediate color values based on the averages of the Red, Green, and Blue values between the two colors.

I created a simple utility to visualize the RGB breakdown for every pixel throughout the gradient. Checkout the RGB Gradient slider.

R: 0
G: 0
B: 255

In the gradient above, do you notice that the colors in the middle appear kinda gray? This gray area is called as the "gray dead zone", a term coined by Erik Kennedy and it prevents the gradients from looking fresh. Ugh!

The gray dead zone - why?

The reason we get the gray dead zone in the middle is because colors at the either end of spectrum, for example RGB(0,0,0) andRGB(255, 255,255) meet atRGB(127.5, 127.5, 127.5)in the middle - which is a shade of gray.

Basically, we get the gray area when the gradient is generated between two highly saturated colors. So, a gradient between black and white, blue and yellow, red and green, etc will always have a gray area in the gradient.

Can we avoid the gray dead zone?

In theory, if we mix two colors and keep the saturation consistent - there wouldn’t be any gray area in the gradient. For example, a gradient between red and a lighter shade of red would not have a gray dead zone.

So, yes technically we can avoid the gray dead zone, but how can we accomplish it? This led me to explore alternative color modes such as HSL and LCH which gives us the saturation of colors. Maybe we could use this instead of RGB?

Alternative Color Modes

You already know about the RGB color mode. Similarly, there are many more but to keep this article short, let’s explore HSL and HCL color modes for generating our gradients.

HSL Color Mode

HSL stands for Hue, Saturation and Lightness. This color mode is a representation of how the computer perceives saturation and lightness of each colors. In order to familiarize ourselves with this color mode, I built a color picker that you can play around with.

Saturation
Lightness

Hue

0 deg
.box {  background: hsl(0deg 50% 50%);}

This is what each values in hsl represents:

  • Hue is the angle on the color wheel. It is represented in degrees and the colors are spread around the circle. 0deg is red, 60deg is yellow, and so on.
  • Saturation determines how vibrant the color is.
  • Lightness determines whether the color should be darker or lighter.

I find this representation of colors as very interesting because of the way colors are represented. If you wish to learn more about this color, this article from MDN docs ↗ is a good reference.

So, now that we found a way to control the saturation - let’s see how a gradient in HSL color mode looks like.

H: 240
S: 100
L: 50

This gradient looks much better than the same gradient rendered in RGB isn’t it? But wait, we learned above that computers use RGB color mode for generating gradients, so how did I manage to create a gradient using HSL color mode?

The secret is, I used a library called as chroma-js to generate 20 color stops between blue and yellow in the hsl color mode. As the color stops are so similar to each other, the gradient generated by the computer matches the hsl values and avoids the gray-dead-zone. It’s a simple yet clever and effective workaround.

Now, as you can see, we completely avoided the gray dead zone. But there’s a problem. Humans don’t perceive lightness the same way as computers do. For example, look at the two colors below:

H: 240
S: 100
L: 50

H: 60
S: 100
L: 50

Between the two colors above, Yellow seems to be lighter than blue isn’t it? Yet, the lightness and saturation of both colors are the same.

Apparently, HSL does not take into consideration how humans perceive color. This is a problem while generating gradients because we might unknowingly end up with extremely lighter colored gradients. So, we need to choose a color mode that take human vision into consideration - the LCH Color Mode.

LCH Color Mode

LCH stands for Lightness, Chroma and Hue. It is pretty similar to HSL but is modelled after human vision. Oftentimes, it is referred to as HCL color mode.

HCL or LCH - however you may want to call it, provides a consistent lightness of colors as perceived by human eyes. I prefer this color mode as it enables us to generate gradients with consistent lightness. Here’s an example:

H: 306
C: 134
L: 32

Notice how the lightness of colors in the gradient between blue and yellow is so consistent? For this reasons I chose this color mode as the default for generating the gradients.

  • There is no single “best” color mode that applies for all types of gradient. Instead, it is pretty much a matter of personal preference 🙂
  • Remember, LCH color mode is not supported by all browsers. We need to translate this to a color mode that browsers can render, such as hex, rgb or hsl.

Generating the gradient

Now that we know the different color modes and chose a preferred mode, it’s time to generate gradients.

We already know that we can create gradients of any color mode by adding multiple color stops to the gradient. So, let’s do that:

import chroma from 'chroma-js'

const colorMode   = 'lch';  // Preferred color mode like 'hsl', 'lch', etc.
const colorStops  = 20;

const colorFrom   = 'rgb(0, 0, 255)'; // this can be hex or rgb or hsl
const colorTo     = 'rgb(255, 255, 0)';

const colors      = chroma
                    .scale([colorFrom, colorTo])
                    .mode(colorMode)
                    .colors(colorStops);

We can use the colors variable above to generate a gradient. Let’s see this in action. Use the slider below to increase or decrease the number of color stops between two colors in various color modes.

Color stops:

Linear Gradient:

Color Mode:

Number of colors:

5

I like this snippet above - mainly because it teaches us a very valuable lesson. “We can get what we want if we try hard enough”. Even though the computer generates gradients in RGB format, adding lots of stops between the colors gives us what we need. Pretty cool isn’t it?

Now that we managed to get a beautiful gradient, let’s start making it better by giving it a direction and easing it via bezier curve.

Generating the gradient

A linear gradient accepts direction and percentage values for every color stops. This enables us to use cubic bezier curves as a way to create different flows such as ease, linear, etc in a gradient. Unfortunately I have not created a visualization for bezier curves yet, so use the presets EASE & LINEAR for now, and later I shall send out a message in the newsletter when I update this post with the bezier curve visualization.

Finally, Let’s put all of this together and try out gradients with angles and bezier curves.

Controls

Direction

45°

Color Mode

Bezier Curve Preset

Number of colors

5

Compiling into an Algorithm

Now that we know about gradients, color modes and bezier curve easing, let’s use this knowledge to create an algorithm to generate beautiful gradients, automatically.

  1. Choose random color and store it as color1
  2. Generate color color2 by rotating the hue value of color1 by 45 degrees.
  3. Reduce the lightness of color2
  4. Use chroma js to generate N colors between color1 and color2 using color mode lch
  5. Generate a bezier curve to generate gradient patterns.
  6. Choose a random angle t to be applied to the gradient.
  7. Join colors, pattern and the angle together into a linear-gradient

The Result

I added seeded random generation and a few more bits to the above algorithm. The final result is an automated gradient generation system that takes an input string and returns a linear gradient ✨.

Enter your name in the component below to see what colors get generated for your name.

Enter your name here

Do you like it? This Javascript library can be installed for free at PrivJs NPM package marketplace or from the npm registry. Give it a try!

Since you reached the end of this post, I'd appreciate if you took your time to write me an email (me@prasannamestha.com) and tell me how you felt about reading this post. If you liked it, do subscribe to my newsletter by . I shall see you at the next one!