Friday, April 15, 2005

Shorter JS Iron Chef!

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!

18 Comments:

At 8:00 AM, Anonymous Henning Koch said...

I think there is a better way to calculate the grayscale value of a color. The human eye is much more receptive for the green component of a color than for the red and blue components, which is why the grayscale value should not be a simple mean. Instead do something like this:

gray = 0.3 * red + 0.59 * green + 0.11 * blue

 
At 3:18 PM, Anonymous Ken Kolano said...

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+")"}

 
At 11:55 PM, Blogger scottandrew said...

The judges accept your entry, Ken, despite that leaving off the semicolons is Just Wrong(TM). Have you no shame? ;)

 
At 9:33 AM, Anonymous And Clover said...

Henning's right. Here's a version with tweakable colour weightings. It's also somewhat smaller due to using integer maths instead of substrings.

function x(c){c=parseInt(c.substr(1),16); c=(c>>16)*0.3+(c>>8&255)*0.5+(c&255)*0.2;
c+=c<100?100:-100;return'rgb('+c+','+c+','+c+')';}

 
At 4:33 PM, Blogger scottandrew said...

Guys, I should have clarified: by "suitably contrasting color" I meant "low-contrast," so we were intentionally going for something that was more difficult to see against the base color. Sorry, should have mentioned that.

Andrew wins with his crazy mathses.

 
At 9:01 PM, Blogger Mark said...

Do you know eric meyers colourBlender?

I need one that produces only 'websmart' colors.

http://www.realsimplcms.com

 
At 2:21 PM, Blogger Chimes & Alarms said...

door bellsLarge selection of door bellsand door bellsavailable for immediate delivery. door bellsExtend your current door chime to ring other places in your home without wiring.

 
At 1:51 AM, Blogger lokokid said...

Hi i am totally blown away with the blogs people have created its so much fun to read alot of good info and you have also one of the best blogs !! Have some time check my link to !!Make money online

 
At 10:54 PM, Blogger Jack Naka said...

Nice blog. Please check out my fashion dress up game site. It is all about fashion dress up game informations.

 
At 2:44 AM, Blogger harvir said...

Veryyyyyyyyy nice blog i kinda like it coz it got alot of information that can be useful for me very informative you go guys and one thing more i got a site that i prefer to everybody maybe you will like it to go have a look in your free timeAffordable home based business opportunity

 
At 2:01 PM, Blogger Basket said...

Bon jour. Le temps amer que je vois.

Chercher le temps et quelques comment terrien ici.

Blog agréable.

Je devrai revenir plus tard.

 
At 5:44 PM, Blogger Hunt and Fish said...

Man there is alot of comment spam I have noticed. Is there any way to remove it from the blogs?

 
At 8:21 AM, Blogger Hunt and Fish said...

Good post

 
At 2:02 AM, Blogger GOOGPLEXMAN said...

I really like your blog. Did you know that you can find all kinds of Making Money On Ebay
online. It's the best place for finding Making Money On Ebay
so tell everyone.

 
At 5:15 PM, Blogger Freddies Blog said...

Last month I made $12,124 with Google Adsense...Click
here for free on how you can do the same!

 
At 7:16 AM, Blogger HiiFii Webservices said...

I wanted to recommend this blog owner to use Adsense on his Blog.
He could have made 100$/Day.
Good Blog
I wanted to show you some superb resourses on the net.
Learn to earn 90000$/Month
For which you may also see my Personal Website
Here.
and for a Personal Education Career Tools
free Study Database.
This site is for seeing the
Hifi Electronics.
And this is for
World Class Gadgets

 
At 10:31 PM, Blogger Bayarealawyers said...

Debt Settlement
Debt Relief can help you reduce your interest burden by charging an interest rate lower than the rate on your existing loans. Debt consolidation loan can also allow you to make small monthly payments by extending the loan period
http://www.debt-consolidation.com

 
At 6:02 PM, Blogger satxxkenn said...

Cool blog - Check out my site if you need help finding a good low interest credit card. Credit Cards

 

Post a Comment

<< Home