Kai Wedekind
codeburst
Published in
8 min readSep 5, 2018

--

Accessibility is the practice of making your websites usable by as many people as possible

Giving users the ability to customize their account color scheme can have devastating effects on the accessibility of your application. When you allow users to alter their color palette, you introduce the possibility of implementing a color contrast ratio that is inaccessible.

Thus, special considerations must be made to ensure the color of text content in your UI has a strong enough color contrast ratio to be accessible.

Black and white provide the strongest color contrast ratios, however in their purest state (#ffffff and #000000), they can have a negative impact on users’s experience — they can be too harsh on the user’s eyes. Thus, it’s recommended to use off-black, #101010 and off-white, #f0f0f0. For simplicity sake, I’m going to use the purest states in my examples.

So how can you programmatically determine which text color to use based on a user’s color palette? There are two equations which work well to determine the appropriate text color: the 50 percent equation and the YIQ equation. Both are easy to implement and produce similar results.

50% Equation

The 50% equation compares the background color to the hex value 50% of the way between pure black and pure white (the median hex value).

  • If the background color value is less than the 50% mark, it’s on the darker side of the spectrum and returns white for the text color.
  • If the background color value is greater than the 50% mark, it’s on the lighter side of the spectrum and returns black for the text color.

Below is the corresponding function for calculating the accessible text color. It converts the six-character hex background value into an integer and compares it to 50% of the value for pure white.

It doesn’t get much simpler than that!

The 50% equation in action can be found here:

YIQ Equation

The YIQ equation converts the RGB color (0 to 255) into a YIQ color space. YIQ is the standard formula for calculating the perceived brightness of a color, and is recommended by the World Wide Web Consortium (W3C).

YIQ Equation
((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000

Below is the function for calculating the YIQ color space. I have chosen to use a brightness value of 128, which is considered bright.

  1. First, break down the hex value into separate RGB values.
  2. Once everything is scaled and normalized, it will contain an integer within the range 0 to 255.
  3. Similar to the 50% function, we must check if the input color value is higher or lower than the 50% mark between black and white. This will return either black or white depending upon which value produces the most optimal color contrast.

That’s it — two simple contrast equations which work well to determine the best font color for optimal readability. The majority of the time, these two contrast algorithms produce the same result. There are some instances where the results conflict, but generally you can use whichever equation you prefer.

The YIQ equation in action can be found here:

Now let’s solve the color contrast problem with machine learning and a neural network; we only need a few lines of code. We won’t delve into the details of machine learning and neural networks, however we can use them to solve the problem of selecting the optimal text color for maximum readability.

Neural Networks

Neural networks (NN) are one of the approaches used in machine learning. With neural networks we can better model complex relationships between inputs and outputs to find patterns in data.

Brain.js

Brain.js is a great library for implementing machine learning approaches within web applications. Brain.js is very useful and can add a lot of value to JavaScript/Node applications. Its ease-of-use allows anyone to get started with neural networks.

Installing the machine learning library brain.js is very easy.

yarn add brain.js

Now, import brain.js module into you project.

const brain = require('brain.js')

Now, we can solve the color contrast problem in three steps:

  1. Create a neural network:
    let network = new brain.NeuralNetwork();
  2. Train the network with training data:
    network.train(data, { log: true });
  3. Run the network with real data:
    network.run();

You train your network by simply giving it an array of values where each array input has two attributes: an input and an output attribute. The network is trained with historical data, where the input and output values are already known.

Brain.js expects values between 0 and 1. Inputs and Outputs can have one or more values. We will train our color contrast network by formatting the inputs as RGB colors and the outputs to be either black or white.

Now, lets start training our network.

  1. Pick a random color
  2. Normalize the RGB value to a value between 0 and 1 by dividing by 255
  3. Add this value as an input to the training data
  4. Give your input value a respective output value of either black or white
  5. Repeat step 1 – 4

With the training data, you can now run your network and let it calculate the probability distribution of the color with the highest contrast.

Brain.js is going to train the network and is going to predict if the text color should be black or white. You should get something similar to this:

...
iterations: 530, training error: 0.006119754857500322
iterations: 540, training error: 0.005870729613810573
iterations: 550, training error: 0.005638886768098001
iterations: 560, training error: 0.005422631733361282
iterations: 570, training error: 0.0052205261001664685
iterations: 580, training error: 0.005031314834044752
result { white: 0.059708621352910995, black: 0.9404082298278809 }

Brain.js predicted that the color contrast is light with an accuracy of 0.94 (94%). I have used a training set of four input values which is extremely low for a machine learning model, but it already gives me relatively good results.

A network becomes more accurate as you add more historical data to train. A neural network is only as good as the quality and amount of its data points; data is the most valuable element in Machine Learning.

If you want to specify how you want your machine learning model to be trained, you can specify the options as a second argument in the train function.

The network will train until the training error has fallen below the error threshold (default 0.005) or the max number of iterations (default 20000) has been reached, whichever comes first.

By default, training won’t let you know how its doing until the end, but if you set log to true to get periodic updates on the current training error of the network. The training error should decrease each time. The updates will be printed to console. If you set log to a function, this function will be called with the updates instead of printing to the console.

The learning rate is a parameter that influences how quickly the network trains. It’s a number from 0 to 1. If the learning rate is close to 0 it will take longer to train. If the learning rate is closer to 1 it will train faster but it's in danger of training to a local minimum and performing badly on new data. The default learning rate is 0.3.

In the color-prediction.js above, I used network.run() and I received the probability values of black and white. With this, I can check whether the probability value of white is larger than the probability value of black; if it is, the color should be white. But brain.js has also a method brain.likely(rgb, network) which can do that for us. If we want the output to be either white or black, we can add the following code:

const result = brain.likely({ r: 0, g: 1, b: 0.65 }, network);
console.log(result);

Now, if you find a color contrast which is not correct, instead of adding more edge cases to your traditional algorithm, you just have to add another dataset to your training data to train your model a little bit better.

This way it is much closer to the way in which I would expect it to work. So instead of solving for some strange edge cases, you can just add another data point to the training data.

Instead of solving for edge cases I try to find spots in the data and train the model more effectively. Within moments we have been able to solve this problem. It would have been very frustrating to solve this in code.

You can let your users decide which color contrast they find the best for their eyes. You can create a random RGB generator and let your users vote. Each time the user votes, the input, which is the RGB combination data, and the color with the highest contrast are going to be converted into inputs and outputs.

In this way, crowd-sourcing can help you to accumulate your training data. You can additionally just ask your designers to give you valid RGB data points with their expected contrast color.

The neural network with brain.js in action can be found here:

If you are interested in solving the color contrast issue with neural networks and Tensorflow.js, then you can find my solution here:

There are many other non traditional methods which you can improve the accessibility of your applications, such as using image recognition for automated image descriptions to help blind people understand your content more effectively, or machine learning which can be used to create automated text summaries in order to help people with learning disabilities, real-time translation or speech recognition for automated subtitles.

Conclusion

Color contrast is an important topic. It is important to make the contrast between colors as high as possible, as it makes it easier for users to read content, increases accessibility and is generally just easier on the eyes.

The non-traditional ways discussed above are only a few ways that have emerged thanks to machine learning and neural networks. Such solutions are already finding their way into assistive technologies.

As creative people add those solutions to new products and applications, it will close the gaps experienced by people with disabilities. These are exciting times and this is only the beginning.

There are plenty of other ways out and it doesn’t really matter if you are using traditional or non-traditional ways to solve machine learning. What is important is that you pick one method and implement it within your application.

✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.

--

--