237

I want to create a function that will accept any old string (will usually be a single word) and from that somehow generate a hexadecimal value between #000000 and #FFFFFF, so I can use it as a colour for a HTML element.

Maybe even a shorthand hex value (e.g: #FFF) if that's less complicated. In fact, a colour from a 'web-safe' palette would be ideal.

Darragh Enright
  • 13,676
  • 7
  • 41
  • 48
  • 2
    Could give some sample input and/or links to the similar questions? – qw3n Aug 06 '10 at 17:59
  • 2
    Not an answer, but you may find the following useful: To convert a hexadecimal to an integer, use `parseInt(hexstr, 10)`. To convert an integer to a hexadecimal, use `n.toString(16)`, where n is a integer. – Cristian Sanchez Aug 06 '10 at 18:00
  • @qw3n - sample input: just short, plain old text strings... like 'Medicine', 'Surgery', 'Neurology', 'General Practice' etc. Ranging between 3 and say, 20 characters... can't find the other one but here's the java question: http://stackoverflow.com/questions/2464745/compute-hex-color-code-for-an-arbitrary-string @Daniel - Thanks. I need to sit down and have another serious go at this. could be useful. – Darragh Enright Aug 06 '10 at 18:42

19 Answers19

309

Here's an adaptation of CD Sanchez' answer that consistently returns a 6-digit colour code:

const stringToColour = (str: string) => {
  let hash = 0;
  str.split('').forEach(char => {
    hash = char.charCodeAt(0) + ((hash << 5) - hash)
  })
  let colour = '#'
  for (let i = 0; i < 3; i++) {
    const value = (hash >> (i * 8)) & 0xff
    colour += value.toString(16).padStart(2, '0')
  }
  return colour
}

If you are using Eslint, you want to wrap this function in the

/* eslint-disable no-bitwise */
>> stringToColour func. definition here" <<
/* eslint-enable no-bitwise */

Usage:

stringToColour("greenish");
// -> #9bc63b

Example:

http://jsfiddle.net/sUK45/

(An alternative/simpler solution might involve returning an 'rgb(...)'-style colour code.)

Liglo App
  • 3,719
  • 4
  • 30
  • 54
Joe Freeman
  • 3,586
  • 2
  • 21
  • 16
  • 6
    This code works awesome in conjunction with NoSQL auto-generated ID's, your colour will be the same every time for the same user. – deviavir Jun 26 '14 at 20:57
  • 1
    I needed the alpha channel for transparency in my hex codes as well. This helped (adding two digits for the alpha channel at the end of my hex code): https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4 – Husterknupp Oct 28 '18 at 20:10
  • @Tjorriemorrie Upvoted for pointing out that it's colour and not color. Yes, yes, it's not really on topic but it's something that's important to me (in fact when typing it originally I spelt it 'colour' both times!). Thank you. – Pryftan Mar 13 '19 at 16:43
  • Interesting that the colour is different for the same string on different browsers/oss - e.g. Chrome+Windows and Chrome+Android - my e-mail=>colour is blue on one and green on the other. Any idea why? – avenmore Aug 11 '19 at 18:51
  • 2
    Thanks! an adaptation to this to generate pastel / bright colors from a string only https://stackoverflow.com/a/64490863/403372 – Joviano Dias Oct 22 '20 at 21:37
  • colour += x.substring(x.length - 2); replace with this, it you do not want to use .substr since it is deprecated – Rajesh Kanna Dec 20 '22 at 03:50
  • Few things: "var" shouldn't be use, please use "let". As for 2023 `substr` function is depreciated and bitwise is showing warning in eslint checks. We want to use Typescript in all examples of JS Stackoverflow even newbies should jump right on that. I will edit the answer to match current standards. – Sebastian Voráč MSc. Jun 22 '23 at 22:19
239

Just porting over the Java from Compute hex color code for an arbitrary string to Javascript:

function hashCode(str) { // java String#hashCode
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
       hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    return hash;
} 

function intToRGB(i){
    var c = (i & 0x00FFFFFF)
        .toString(16)
        .toUpperCase();

    return "00000".substring(0, 6 - c.length) + c;
}

To convert you would do:

intToRGB(hashCode(your_string))
Community
  • 1
  • 1
Cristian Sanchez
  • 31,171
  • 11
  • 57
  • 63
  • It needs to pad the hex strings, such as: `("00" + ((this >> 24) & 0xFF).toString(16)).slice(-2) + ("00" + ((this >> 16) & 0xFF).toString(16)).slice(-2) + ("00" + ((this >> 8) & 0xFF).toString(16)).slice(-2) + ("00" + (this & 0xFF).toString(16)).slice(-2);` – Thymine Feb 06 '14 at 22:38
  • 4
    I'm converting a bunch of music genre tags to background colors and this saved me a lot of time. – Kyle Pennell Feb 25 '16 at 05:18
  • I wish I could convert this to php. – Nimitz E. Jun 11 '16 at 10:17
  • 11
    I have some problems with almost same colours for similar strings, for example: `intToRGB(hashCode('hello1')) -> "3A019F"` `intToRGB(hashCode('hello2')) -> "3A01A0"` And I enhance your code by adding multiplication for final hash value: `return 100 * hash;` – SirWojtek Aug 24 '17 at 01:43
  • Don't forget to add '#' like return "#" + "00000".substring(0, 6 - c.length) + c; – Cedric Arnould Mar 21 '19 at 19:51
73

I wanted similar richness in colors for HTML elements, I was surprised to find that CSS now supports hsl() colors, so a full solution for me is below:

Also see How to automatically generate N "distinct" colors? for more alternatives more similar to this.

Edit: updating based on @zei's version (with american spelling)

var stringToColor = (string, saturation = 100, lightness = 75) => {
  let hash = 0;
  for (let i = 0; i < string.length; i++) {
hash = string.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
  }
  return `hsl(${(hash % 360)}, ${saturation}%, ${lightness}%)`;
}


// For the sample on stackoverflow
function colorByHashCode(value) {
return "<span style='color:" + stringToColor(value) + "'>" + value + "</span>";
}

document.body.innerHTML = [
  "javascript",
  "is",
  "nice",
].map(colorByHashCode).join("<br/>");
span {
  font-size: 50px;
  font-weight: 800;
}

In HSL its Hue, Saturation, Lightness. So the hue between 0-359 will get all colors, saturation is how rich you want the color, 100% works for me. And Lightness determines the deepness, 50% is normal, 25% is dark colors, 75% is pastel. I have 30% because it fit with my color scheme best.

Thymine
  • 8,775
  • 2
  • 35
  • 47
38

Here is my 2021 version with Reduce Function and HSL Color.

function getBackgroundColor(stringInput) {
    let stringUniqueHash = [...stringInput].reduce((acc, char) => {
        return char.charCodeAt(0) + ((acc << 5) - acc);
    }, 0);
    return `hsl(${stringUniqueHash % 360}, 95%, 35%)`;
}
Viraj Singh
  • 1,951
  • 1
  • 17
  • 27
Aslam
  • 9,204
  • 4
  • 35
  • 51
18

Using the hashCode as in Cristian Sanchez's answer with hsl and modern javascript, you can create a color picker with good contrast like this:

function hashCode(str) {
  let hash = 0;
  for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
}

function pickColor(str) {
  return `hsl(${hashCode(str) % 360}, 100%, 80%)`;
}

one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
  padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>

Since it's hsl, you can scale luminance to get the contrast you're looking for.

function hashCode(str) {
  let hash = 0;
  for (var i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
}

function pickColor(str) {
  // Note the last value here is now 50% instead of 80%
  return `hsl(${hashCode(str) % 360}, 100%, 50%)`;
}

one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
  color: white;
  padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>
Kyle Kelley
  • 13,804
  • 8
  • 49
  • 78
9

I find that generating random colors tends to create colors that do not have enough contrast for my taste. The easiest way I have found to get around that is to pre-populate a list of very different colors. For every new string, assign the next color in the list:

// Takes any string and converts it into a #RRGGBB color.
var StringToColor = (function(){
    var instance = null;

    return {
    next: function stringToColor(str) {
        if(instance === null) {
            instance = {};
            instance.stringToColorHash = {};
            instance.nextVeryDifferntColorIdx = 0;
            instance.veryDifferentColors = ["#000000","#00FF00","#0000FF","#FF0000","#01FFFE","#FFA6FE","#FFDB66","#006401","#010067","#95003A","#007DB5","#FF00F6","#FFEEE8","#774D00","#90FB92","#0076FF","#D5FF00","#FF937E","#6A826C","#FF029D","#FE8900","#7A4782","#7E2DD2","#85A900","#FF0056","#A42400","#00AE7E","#683D3B","#BDC6FF","#263400","#BDD393","#00B917","#9E008E","#001544","#C28C9F","#FF74A3","#01D0FF","#004754","#E56FFE","#788231","#0E4CA1","#91D0CB","#BE9970","#968AE8","#BB8800","#43002C","#DEFF74","#00FFC6","#FFE502","#620E00","#008F9C","#98FF52","#7544B1","#B500FF","#00FF78","#FF6E41","#005F39","#6B6882","#5FAD4E","#A75740","#A5FFD2","#FFB167","#009BFF","#E85EBE"];
        }

        if(!instance.stringToColorHash[str])
            instance.stringToColorHash[str] = instance.veryDifferentColors[instance.nextVeryDifferntColorIdx++];

            return instance.stringToColorHash[str];
        }
    }
})();

// Get a new color for each string
StringToColor.next("get first color");
StringToColor.next("get second color");

// Will return the same color as the first time
StringToColor.next("get first color");

While this has a limit to only 64 colors, I find most humans can't really tell the difference after that anyway. I suppose you could always add more colors.

While this code uses hard-coded colors, you are at least guaranteed to know during development exactly how much contrast you will see between colors in production.

Color list has been lifted from this SO answer, there are other lists with more colors.

Community
  • 1
  • 1
Rick Smith
  • 9,031
  • 15
  • 81
  • 85
  • Fwiw there's an algorithm out there to determine contrast. I wrote something with it years ago (but in C). Too much going on to worry about it and it's an old answer anyway but figured I'd point out that there is a way to determine contrast. – Pryftan Mar 13 '19 at 16:46
  • supplement: to wrap colours back to first after all are used, replace the 2nd if with: `if(!instance.stringToColorHash[str]) { instance.nextVeryDifferntColorIdx++; instance.nextVeryDifferntColorIdx %= instance.veryDifferentColors.length; instance.stringToColorHash[str] = instance.veryDifferentColors[instance.nextVeryDifferntColorIdx]; }` – MoonLite Nov 09 '20 at 17:08
7

If your inputs are not different enough for a simple hash to use the entire color spectrum, you can use a seeded random number generator instead of a hash function.

I'm using the color coder from Joe Freeman's answer, and David Bau's seeded random number generator.

function stringToColour(str) {
    Math.seedrandom(str);
    var rand = Math.random() * Math.pow(255,3);
    Math.seedrandom(); // don't leave a non-random seed in the generator
    for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((rand >> i++ * 8) & 0xFF).toString(16)).slice(-2));
    return colour;
}
Nathan
  • 1,675
  • 17
  • 25
7

I have opened a pull request to Please.js that allows generating a color from a hash.

You can map the string to a color like so:

const color = Please.make_color({
    from_hash: "any string goes here"
});

For example, "any string goes here" will return as "#47291b"
and "another!" returns as "#1f0c3d"

Josue Alexander Ibarra
  • 8,269
  • 3
  • 30
  • 37
  • Really cool thanks for adding that. Hi wanting to generate circles with letters in based on a name like Google inbox does:) – marcus7777 Nov 18 '16 at 10:22
  • when I saw this answer, I thought, perfect, now I have to think about the color schema so it doesn't generate very random colors, then I read about the Please.js make_color options and it put a beautiful smile in my face. – panchicore Feb 22 '19 at 23:57
7

Javascript Solution inspired by Aslam's solution but returns a color in hex color code

/**
 * 
 * @param {String} - stringInput - 'xyz'
 * @returns {String} - color in hex color code - '#ae6204'
 */
function getBackgroundColor(stringInput) {
    const h = [...stringInput].reduce((acc, char) => {
        return char.charCodeAt(0) + ((acc << 5) - acc);
    }, 0);
    const s = 95, l = 35 / 100;
    const a = s * Math.min(l, 1 - l) / 100;
    const f = n => {
        const k = (n + h / 30) % 12;
        const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
        return Math.round(255 * color).toString(16).padStart(2, '0');   // convert to Hex and prefix "0" if needed
    };
    return `#${f(0)}${f(8)}${f(4)}`;
}
Viraj Singh
  • 1,951
  • 1
  • 17
  • 27
  • This is really nice. May I use this code? – unobatbayar Mar 30 '22 at 10:58
  • 1
    of course, you can. – Viraj Singh Mar 31 '22 at 11:07
  • For those you encounter that some hashes turn negative and the color is always gray. It turns our js converts to [int32 and stuff, see SA](https://stackoverflow.com/a/23879780/8488653). So I have changes the line from `return char.charCodeAt(0) + ((acc << 5) - acc);` to `return char.charCodeAt(0) + ((acc << 5 >>> 0) - acc);` – Leon Jul 03 '22 at 17:49
5

Yet another solution for random colors:

function colorize(str) {
    for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
    color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16);
    return '#' + Array(6 - color.length + 1).join('0') + color;
}

It's a mixed of things that does the job for me. I used JFreeman Hash function (also an answer in this thread) and Asykäri pseudo random function from here and some padding and math from myself.

I doubt the function produces evenly distributed colors, though it looks nice and does that what it should do.

Community
  • 1
  • 1
estani
  • 24,254
  • 2
  • 93
  • 76
4

Here's a solution I came up with to generate aesthetically pleasing pastel colours based on an input string. It uses the first two chars of the string as a random seed, then generates R/G/B based on that seed.

It could be easily extended so that the seed is the XOR of all chars in the string, rather than just the first two.

Inspired by David Crow's answer here: Algorithm to randomly generate an aesthetically-pleasing color palette

//magic to convert strings to a nice pastel colour based on first two chars
//
// every string with the same first two chars will generate the same pastel colour
function pastel_colour(input_str) {

    //TODO: adjust base colour values below based on theme
    var baseRed = 128;
    var baseGreen = 128;
    var baseBlue = 128;

    //lazy seeded random hack to get values from 0 - 256
    //for seed just take bitwise XOR of first two chars
    var seed = input_str.charCodeAt(0) ^ input_str.charCodeAt(1);
    var rand_1 = Math.abs((Math.sin(seed++) * 10000)) % 256;
    var rand_2 = Math.abs((Math.sin(seed++) * 10000)) % 256;
    var rand_3 = Math.abs((Math.sin(seed++) * 10000)) % 256;

    //build colour
    var red = Math.round((rand_1 + baseRed) / 2);
    var green = Math.round((rand_2 + baseGreen) / 2);
    var blue = Math.round((rand_3 + baseBlue) / 2);

    return { red: red, green: green, blue: blue };
}

GIST is here: https://gist.github.com/ro-sharp/49fd46a071a267d9e5dd

Community
  • 1
  • 1
Robert Sharp
  • 259
  • 4
  • 5
  • I must say that this is a really odd way of doing it. It kind of works but there aren't very many colors available. XOR of the first two colors makes no distinction of order so there are just combinations of letters. A simple addition I did to increase the number of colors was var seed = 0; for (var i in input_str) { seed ^= i; } – Gussoh Sep 30 '15 at 12:59
  • Yes, it really depends how many colours you'd like to generate. I recall in this instance I was creating different panes in a UI and wanted a limited number of colours rather than a rainbow :) – Robert Sharp Oct 19 '18 at 04:49
1

Here is another try:

function stringToColor(str){
  var hash = 0;
  for(var i=0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 3) - hash);
  }
  var color = Math.abs(hash).toString(16).substring(0, 6);

  return "#" + '000000'.substring(0, 6 - color.length) + color;
}
kikito
  • 51,734
  • 32
  • 149
  • 189
1

All you really need is a good hash function. On node, I just use

const crypto = require('crypto');
function strToColor(str) {
    return '#' + crypto.createHash('md5').update(str).digest('hex').substr(0, 6);
}
pulsejet
  • 1,101
  • 13
  • 27
1

After having a look at the rather code intensive and rather old answers, I thought I'd review this issue from a 2021 standpoint just for fun, hope it is of use to anyone. Having the HSL color model and the crypto API implemented in pretty much all browsers (except IE of course) today, it could be solved as simple as that:

async function getColor(text, minLightness = 40, maxLightness = 80, minSaturation = 30, maxSaturation = 100) {
  let hash = await window.crypto.subtle.digest("SHA-1", new TextEncoder().encode(text));
  hash = new Uint8Array(hash).join("").slice(16);

  return "hsl(" + (hash % 360) + ", " + (hash % (maxSaturation - minSaturation) + minSaturation) + "%, " + (hash % (maxLightness - minLightness) + minLightness) + "%)";
}

function generateColor() {
  getColor(document.getElementById("text-input").value).then(color => document.querySelector(".swatch").style.backgroundColor = color);
}
input {
  padding: 5px;
}

.swatch {
  margin-left: 10px;
  width: 28px;
  height: 28px;
  background-color: white;
  border: 1px solid gray;
}

.flex {
  display: flex;
}
<html>

<body>
  <div class="flex">
    <form>
      <input id="text-input" type="text" onInput="generateColor()" placeholder="Type here"></input>
    </form>
    <div class="swatch"></div>
  </div>
</body>

</html>

This should be way faster than generating hashes manually, and also offers a way to define saturation and lightness in case you don't want colors that are too flat or too bright or too dark (e.g. if you want to write text on those colors).

Lupinity Labs
  • 2,099
  • 1
  • 15
  • 23
1

2023 version plain and simple TypeScript arrow function that returns HSL color.

const stringToColor = (value: string) => {
  let hash = 0;
  for (let i = 0; i < value.length; i++) {
    hash = value.charCodeAt(i) + ((hash << 5) - hash);
  }

  return `hsl(${hash % 360}, 85%, 35%)`;
};

marko424
  • 3,839
  • 5
  • 17
  • 27
-1

This function does the trick. It's an adaptation of this, fairly longer implementation this repo ..

const color = (str) => {
    let rgb = [];
    // Changing non-hexadecimal characters to 0
    str = [...str].map(c => (/[0-9A-Fa-f]/g.test(c)) ? c : 0).join('');
    // Padding string with zeroes until it adds up to 3
    while (str.length % 3) str += '0';

    // Dividing string into 3 equally large arrays
    for (i = 0; i < str.length; i += str.length / 3)
        rgb.push(str.slice(i, i + str.length / 3));

    // Formatting a hex color from the first two letters of each portion
    return `#${rgb.map(string => string.slice(0, 2)).join('')}`;
}
-1

my code is for Java.

Thanks for all.

public static int getColorFromText(String text)
    {
        if(text == null || text.length() < 1)
            return Color.BLACK;

        int hash = 0;

        for (int i = 0; i < text.length(); i++)
        {
            hash = text.charAt(i) + ((hash << 5) - hash);
        }

        int c = (hash & 0x00FFFFFF);
        c = c - 16777216;

        return c;
    }
Ali Bagheri
  • 3,068
  • 27
  • 28
-1

I have a situation where i want to display a background based on the username of the user and display the username's first letter on top. i used the belove code for that it worked well for me

var stringToColour = function (str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
  hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
var colour = '#';
for (var i = 0; i < 3; i++) {
  var value = (hash >> (i * 8)) & 0xff;
  colour += ('00' + value.toString(16)).substr(-2);
}
return colour;}

To find the appropriate colour you can use

function lightOrDark(color) {
// Check the format of the color, HEX or RGB?
if (color.match(/^rgb/)) {
  // If HEX --> store the red, green, blue values in separate variables
  color = color.match(
    /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/,
  );

  var r = color[1];
  var g = color[2];
  var b = color[3];
} else {
  // If RGB --> Convert it to HEX: http://gist.github.com/983661
  color = +(
    '0x' + color.slice(1).replace(color.length < 5 && /./g, '$&$&')
  );

  r = color >> 16;
  g = (color >> 8) & 255;
  b = color & 255;
}

// HSP equation from http://alienryderflex.com/hsp.html
var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));

// Using the HSP value, determine whether the color is light or dark
if (hsp > 127.5) {
  return 'light';
} else {
  return 'dark';
}

}

Rashid
  • 1
  • 1
-5

I convert this in one line for Python

import hashlib

hash = hashlib.sha1(b'user@email.com').hexdigest()

print("#" + hash[0:6])
foad
  • 6
  • 3