202

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
  • 12,994
  • 6
  • 41
  • 47
  • 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

17 Answers17

256

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

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;
}

Usage:

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

Example:

http://jsfiddle.net/sUK45/

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

Martin Tournoij
  • 24,971
  • 24
  • 101
  • 136
Joe Freeman
  • 3,064
  • 2
  • 19
  • 15
  • 5
    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
  • 1
    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
229

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
  • 29,155
  • 10
  • 55
  • 62
  • 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
  • 10
    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
59

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.

function colorByHashCode(value) {
    return "<span style='color:" + value.getHashCode().intToHSL() + "'>" + value + "</span>";
}
String.prototype.getHashCode = function() {
    var hash = 0;
    if (this.length == 0) return hash;
    for (var i = 0; i < this.length; i++) {
        hash = this.charCodeAt(i) + ((hash << 5) - hash);
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
};
Number.prototype.intToHSL = function() {
    var shortened = this % 360;
    return "hsl(" + shortened + ",100%,30%)";
};

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,187
  • 1
  • 31
  • 46
24

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,060
  • 12
  • 23
Aslam
  • 7,851
  • 3
  • 33
  • 49
16

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,315
  • 4
  • 47
  • 77
10

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
  • 8,765
  • 14
  • 80
  • 84
  • 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
8

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
  • 7,475
  • 3
  • 28
  • 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

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,657
  • 17
  • 24
6

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
  • 21,251
  • 2
  • 85
  • 65
5

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
  • 261
  • 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
5

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,060
  • 12
  • 23
2

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
  • 50,277
  • 31
  • 143
  • 186
2

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,054
  • 12
  • 26
2

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
  • 1,754
  • 1
  • 13
  • 19
0

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('')}`;
}
0

I convert this for Java.

Tanks 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
  • 2,512
  • 23
  • 24
-3

I convert this in one line for Python

import hashlib

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

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