The challenge: write a JS function that takes a given hex color and returns a suitably-contrasted greyscale shade, in as few lines of JS as possible.
My first pass went like this:
function contrast(c) {
var av = (parseInt(c.substring(1,3),16) +
parseInt(c.substring(3,5),16) +
parseInt(c.substring(5,7),16)) / 3;
av = (av >= 100)? av-100:av+100;
return "rgb(" + av +"," + av + "," + av + ")";
}
A quick overview of what's happening here. c is the 6-character hex color (sans the "#"). The first JS statement uses the substring
function to break the hex color string into its three 2-character hex pairs, which are then passed to parseInt
to convert them to decimal values. Then we find the average by adding them and dividing by 3.
So, FF0000 (pure red) becomes FF+00+00 becomes 255+0+0 becomes 255/3 becomes 85.
The function then looks at this average and, using 100 as its "bias" point, either adds or subtracts 100 (an arbitrary value that produced the best results for our needs). The last line returns a greyscale RGB value (perfectly valid in CSS! No need to convert back to hex!).
Now the fun: I sent this off to another webdev for review and challenged him to make it even shorter. He came back with this:
function f(c,n) {return parseInt(c.substring(n,n+2),16);}
function contrast(c) {
var av=(f(c,1)+f(c,2)+f(c,3))/3;
av+=100*(av>=100)?-1:1;
return "rgb("+av+","+av+","+av+")";
}
Nice, but I pointed out that av
wasn't being calculated correctly without an additional set of parenthesis. Plus, we could shave off a few characters by replacing substring
with the shorter substr
to the same effect. I countered with this:
function f(c,n) {return parseInt(c.substr(n,2),16);}
function x(c) {
var av=(f(c,1)+f(c,3)+f(c,5))/3;
av+=(av>=100)?-100:100;
return "rgb("+av+","+av+","+av+")";
}
Of course, I hadn't thought to remove the whitespace yet :) His riposte:
function f(c,n){return parseInt(c.substr(n,2),16);}
function x(c){var a=(f(c,1)+f(c,3)+f(c,5))/3;c=100;
a+=(a>c)?-c:c;c=",";return "rgb("+a+c+a+c+a+")";}
Bested but not broken by his ingenious re-use of local variables, I nonetheless proclaimed victory by deleting the requirement for such a JS function from the spec, reducing the necessary code to:
Don't ever let it be said I don't think outside the box!