Skip to content

Instantly share code, notes, and snippets.

@0x263b 0x263b/colors.md
Last active Oct 1, 2019

Embed
What would you like to do?
Random color from string in javascript

Random color from string in javascript

Consider a list of strings you need to permanently assign a random color.

First you should turn the string into a hash.

var string = "string"
var hash = 0

for (var i = 0; i < string.length; i++) {
	hash = string.charCodeAt(i) + ((hash << 5) - hash);
	hash = hash & hash;
}

console.log(hash) // -891985903
  • string.charCodeAt(i) returns the UTF-16 code for the character at index i
  • Bit operators work on 32 bits numbers. Any numeric operand in the operation is converted into a 32 bit number.
  • hash << 5 is equivalent to hash * Math.pow(2, 5) (hash * 32), except the bit operator << makes sure our result is a 32 bit number.
  • hash & hash again, makes sure we only return a 32 bit number.

Now we have something to play around with.

RGB

The simplest method is to turn our hash into an RGB string:

String.prototype.toRGB = 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;
    }
    var rgb = [0, 0, 0];
    for (var i = 0; i < 3; i++) {
        var value = (hash >> (i * 8)) & 255;
        rgb[i] = value;
    }
    return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
}

"string".toRGB() // rgb(17, 96, 213)

Or a hexadecimal string (fiddle)

String.prototype.toHex = 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;
    }
    var color = '#';
    for (var i = 0; i < 3; i++) {
        var value = (hash >> (i * 8)) & 255;
        color += ('00' + value.toString(16)).substr(-2);
    }
    return color;
}

"string".toHex() // #1160d5

The issue is this can potentially spit out any color value. Ideally we'd want to filter out values too similar to our background color, and some gray/bland colors.

Color from array

What if we hand pick some colors, and assign each string one of those? (fiddle)

String.prototype.toColor = function() {
	var colors = ["#e51c23", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#5677fc", "#03a9f4", "#00bcd4", "#009688", "#259b24", "#8bc34a", "#afb42b", "#ff9800", "#ff5722", "#795548", "#607d8b"]
	
    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;
    }
    hash = ((hash % colors.length) + colors.length) % colors.length;
    return colors[hash];
}

"string".toColor() // #e91e63

This method is better if we want to be very particular over what colors are allowed, but selecting a large number colors can get tedious.

HSL

How about we use the hash to pick a hue, then hardcode the intensity/lightness. (fiddle)

String.prototype.toHue = 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;
    }
	return hash % 360;
}

"string".toHue() // -223

Note: This can result in a negative value. CSS's hsl() handles values outside of 0..360 perfectly, but not everything does.

For the sake of engineering, lets expand this out a bit more to include range values. (fiddle)

String.prototype.toHSL = function(opts) {
    var h, s, l;
    opts = opts || {};
    opts.hue = opts.hue || [0, 360];
    opts.sat = opts.sat || [75, 100];
    opts.lit = opts.lit || [40, 60];

    var range = function(hash, min, max) {
        var diff = max - min;
        var x = ((hash % diff) + diff) % diff;
        return x + min;
    }

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

    h = range(hash, opts.hue[0], opts.hue[1]);
    s = range(hash, opts.sat[0], opts.sat[1]);
    l = range(hash, opts.lit[0], opts.lit[1]);

    return `hsl(${h}, ${s}%, ${l}%)`;
}

"string".toHSL() // hsl(137, 97%, 57%)

"string".toHSL({
    hue: [-45, 45],
    sat: [75, 95],
    lit: [45, 55]
}) // hsl(2, 92%, 52%)
@Zarel

This comment has been minimized.

Copy link

Zarel commented Sep 6, 2016

Unfortunately, HSL's lightness doesn't tend to correspond well to how light or dark your eyes see colors.

The simple solution is to darken your yellows and cyans and lighten your blues.

The "right" solution is to use a color space that lets you specify luma, saturation, and hue. Something like http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/

@KingAmada

This comment has been minimized.

Copy link

KingAmada commented May 14, 2018

How do you reverse it to bring back original values?

@Yhoko

This comment has been minimized.

Copy link

Yhoko commented Jul 12, 2019

There's a hash function, so: you can't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.