JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

js: create '3d' colour palette

<p>While working on my animal genetic site, there was a looming problem: using accurate colours for all 30+ animal types. The pigments in hairs, feathers and scales come in many, <b>many</b> colours and shades, in different areas of the body.</p>

<p>At first, I tried to create one huge 'universal palette' of all possible colours, to cover any body area on any animal, with any particular mutation(s). Simple black, white and various shades of grey and pink can be re-used across many species, for example. Other common browns, yellows and creams could fit too.</p>

There were some problems with this approach.
<ul>
    <li>It required a crazy number of if/else statements, to choose which colour to assign to every body part. "If diluted AND chocolate AND caramel, then..." What a chore!</li>
    <li>It wasn't very specific. Each species has its own visual palette. The basic red-brown phaeomelanin of a bay horse doesn't necessarily look right on a hamster or cow. Then we get to dilutions and such... there's a subtle but important difference between a champagne (Ch/-), a hetero cream (Cr/cr), a homo cream (Cr/Cr), a pearl (prl/prl) and a combination horse (cream or pearl plus champagne). Need at least a dozen pale smoky/creamy/ambery/goldish colours to pick from. Which ones, exactly, should then be used for other animals' dilutions? Beige chinchilla, fallow budgie, yellow dog? Would <i>any</i> of those horse colours fit properly?</li>
    <li>Even if all colours managed to look 'acceptably realistic', the animals lacked any variation. A real 'red' rabbit can vary a lot, from dusty tan to deep rich red, almost scarlet or mahogany. I'd like to include modifier numbers so the user can see how animals look with different shades and richness of pigment. But that'll mean an even bigger palette and more code!</li>
</ul>

<p>I started wondering about 'dynamically generating' colours instead of choosing from a premade palette. Say, you start with...

CSS

.swatch {
    display: inline-block;
    width: 70px;
    text-align: center;
}
.swatch_col {
    width: 70px;
    height: 25px;
}

JavaScript

class Hex {

    /*
    * Take a hex code like "ffcc33" or "09f".
    * Return object with r, g, b number values.
    */
    static hexToRGB(hex){
	    hex = hex.toUpperCase().replace(/[\#\(]*/i,'');
        let col = hex;
	    if(col.length == 3){
	    	let a = col.substr(0,1);
	    	let b = col.substr(1,1);
	    	let c = col.substr(2,1);
	    	col = a + a + b + b + c + c;
        }
	    let num = [
            col.substr(0,2),
            col.substr(2,2),
            col.substr(4,2)
        ];
        return {
            r: parseInt(num[0],16)*1,
            g: parseInt(num[1],16)*1,
            b: parseInt(num[2],16)*1,
            hex: hex
        };
    }

    /*
    * And the opposite. Construct hex code.
    */
    static rgbToHex(r,g,b){
        let c = {
           r: r, g: g, b: b,
        };
        c.hex = this.getHexCode([r,g,b]);
        return c;
    }

    /*
    * Take object with r,b,g values.
    * Return hex code.
    */
    static getHexCode(c){
	    let result = "";
        c.forEach(function(v){
            let val = Math.round(v / 1);
	    	let piece = val.toString(16);
	    	if(piece.length < 2){piece = "0" + piece;}
		    result += piece;
        });
	    result = result.toUpperCase();
	    return result;
    }

   
    /*
    * Take 2 objects, generate colours between.
    */
    static getPalette(c1,c2,s){
        let steps = {
            r: (c2.r - c1.r) / s,
	        g: (c2.g - c1.g) / s,
	        b: (c2.b - c1.b) / s
        };
        let palette = [c1.hex];
	    for(let i = 1; i <= s; i++){
	    	let r = (c1.r + (steps.r * i));
	    	let g = (c1.g + (steps.g * i));
	    	let b = (c1.b + (steps.b * i));
		    let col = this.rgbToHex(r,g,b);
            palette.push(col.hex);
	    }
        return palette;
    }
   
    /*
    * Whole process, starting with two objects.
    * Draw the 10 colours in #palette div.
    */
    static blendColours(c1,c2,stages){
        let s = parseInt(stages) + 1;
        let palette =...