Skip to content

Instantly share code, notes, and snippets.

@0x263b
Last active March 7, 2024 10:09
Show Gist options
  • Star 98 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save 0x263b/2bdd90886c2036a1ad5bcf06d6e6fb37 to your computer and use it in GitHub Desktop.
Save 0x263b/2bdd90886c2036a1ad5bcf06d6e6fb37 to your computer and use it in GitHub Desktop.
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%)
@haywirez
Copy link

Thank you! Could be worth packaging 👾

@MaximeKoitsalu
Copy link

Very nice mate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment