24
\$\begingroup\$

Your challenge is to take input as a line of text and output it like this.

rainbow image

Input / output

The input will be a string that contains only printable ASCII characters. The first or last characters will never be spaces, and there will never be two spaces in a row. It will always be at least two characters long.

Your output should be the same string, converted to rainbow colors as will be described below. The output may be in image form (saved to a file or somehow otherwise made available), or it may simply display the result on the screen (as the reference implementation below does).

Conversion

To determine what color each letter in the string should become, use the following algorithm. Note that each letter is its own individual color. This is not a gradient!

  • If this character is a space:

    • ... it doesn't matter, because spaces can't really... have a color anyway. Simply output a space.
  • Otherwise:

    • Let i = the index of this character in the string (0-based, so for the very first letter, this is 0), not counting spaces. For example, in the string foo bar, this value would be 4 for the a. In other words, this is how many non-spaces have been encountered so far.

    • Let n = the number of non-spaces in the string.

    • The color of this letter can now be expressed, in the HSL cylindrical-coordinate system, as [hue=(i/n)*360°, saturation=100%, lightness=50%].

Note that these directions imply that the output for foo and f oo should be exactly the same, except for an added space after the f. That is, all the letters should retain the same colors.

Further rules for the conversion process are described below, in the Rules section.

Reference implementation

This is written in JavaScript, and you can try it by pressing the "Run code snippet" button.

window.addEventListener('load', function() {
    addRainbow('Your challenge is to take input as a line of text and ' +
        'output it like this.');
});

// append this text rainbow-ified to the argument (document.body by default)
function addRainbow(text, el) {
    (el || document.body).appendChild(makeRainbow(text));
}

// returns a <div> that contains the text in a rainbow font
function makeRainbow(text) {
    var div = document.createElement('div');
    var letterCount = text.replace(/ /g, '').length, spaceCount = 0;
    text.split('').forEach(function(letter, idx) {
        if (letter == ' ') ++spaceCount;
        div.appendChild(makeLetter(letter, (idx - spaceCount) / letterCount));
    });
    return div;
}

// returns a <span> that contains the letter in the specified color
function makeLetter(letter, hue) {
    hue = Math.floor(hue * 360);
    var span = document.createElement('span');
    span.appendChild(document.createTextNode(letter));
    span.style.color = 'hsl(' + hue + ', 100%, 50%)';
    return span;
}

Rules

  • When computing the Hue value of a letter, you will almost certainly get a decimal (non-integer) number. You may round this to the nearest integer, floor it, take the ceiling, or simply not round at all.

  • The font size must be readable. Here, this is defined as a 10pt size font or greater.

  • You may use a fixed-width canvas or "drawing area" to output the text, but it must be able to fit the example given in the very first sentence of this post.

  • Scoring is , so the shortest code in bytes will win.

\$\endgroup\$
5
  • \$\begingroup\$ Can the output be a Data URI? That's the output from the HTML canvas \$\endgroup\$
    – Downgoat
    Aug 21, 2015 at 2:35
  • \$\begingroup\$ @vihan Yes, that qualifies as per the "The output may be in image form (saved to a file or somehow otherwise made available)" rule. \$\endgroup\$
    – Doorknob
    Aug 21, 2015 at 2:50
  • \$\begingroup\$ How do you determine whether a coloration meets the spec? Can you specify precisely what conversion formula one should use if only RGB colors are supported in a language? Also, how many bits of precision per channel are necessary? Presumably 8 would be OK, but how about 4, or 1? \$\endgroup\$
    – feersum
    Aug 21, 2015 at 7:28
  • \$\begingroup\$ @feersum To convert to RGB, you may use a builtin or one of the methods described here. Could you clarify what you mean by your second question? Are you asking about this specifically in the context of HSL to RGB conversion, or in general? \$\endgroup\$
    – Doorknob
    Aug 21, 2015 at 11:08
  • 2
    \$\begingroup\$ Dang, I'm not even going to try with PowerShell ... You only get 16 colors to play with (and they're not even ordered ... rainbow or RGB or otherwise ... just an arbitrary hex value). Reference, with Pictures Really cool challenge, though! \$\endgroup\$ Aug 21, 2015 at 16:00

6 Answers 6

12
\$\begingroup\$

Perl 5.28.1 (webperl) + -p, 88 bytes

@c=unpack C35,"..........vR../012.3-'!..9]........";s|\S|.[38;5;$c[@c*$i++/y/!-~//]m$&|g

Try it online!

Explanation

This script uses an approximation of the colours available to terminal (256 maximum) currently only including a few colour points selected from this list, so it is likely not to spec, but this was fun anyway! I filtered the list to only show colours with S and L values of 100% and 50% respectively, then sorted by hue, packed the numbers into a string and select the colours from that list.

This implementation includes non-printable characters! Stole @edc65's idea of only replacing \S instead of ., simple, but clever!

\$\endgroup\$
10
\$\begingroup\$

Python 2: 240 using PIL and colorsys lib

import PIL,colorsys as c
s=input()
u,a=len(s),255
g=Image.new('RGB',(u*6,13),(a,)*3)
[ImageDraw.Draw(g).text((j*6,0),s[j],fill=tuple(int(h*a)for h in c.hls_to_rgb(1.*(j-s[:j].count(' '))/(u-s.count(' ')),.5,1)))for j in range(u)]
g.show()

Example output:

Output rainbow text example test with spaces

Thanks to @agtoever and @Trang Oul for some golfing tips, and for @Mauris for pointing out the spaces requirement.

To add a true type fonts, font size control, including horizontal offset and color change based on length.

import PIL as P,colorsys as c
s=input()
u=len(s)
a=255
fs=25
f=P.ImageFont.truetype("a.ttf",fs)
sza=f.getsize(s)
oa=f.getoffset(s)
g=P.Image.new('RGB',(sza[0]+fs,2*sza[1]+oa[1]),(a,)*3)
r=fs/4
P.ImageDraw.Draw(g).text((r,0),s,fill=(0,0,0),font=f)
for j in range(u):   
 o=f.getoffset(s[j])
 sz=f.getsize(s[j])   
 r+=o[0]
 P.ImageDraw.Draw(g).text((r,0+fs),s[j],fill=tuple([int(h*a)for h in c.hls_to_rgb(1.*r/sza[0],.5,1)]),font=f)
 r+=sz[0]
g.save('a.png')
g.show()

The font I used is available from here: The result is (the top is just printing the string, the one below is printing per letter):

typewriter

\$\endgroup\$
9
  • 1
    \$\begingroup\$ Drop as P and write out PIL twice and win one character. Change img to i and win 4 more characters. \$\endgroup\$
    – agtoever
    Aug 21, 2015 at 7:31
  • 1
    \$\begingroup\$ Save 255 to a variable, replace all ocurrences and use (a,)*3 as white colour. Replace float(j) by (j+.0). \$\endgroup\$
    – Trang Oul
    Aug 21, 2015 at 7:48
  • 1
    \$\begingroup\$ Also: replace float(j) to 1.*j \$\endgroup\$
    – agtoever
    Aug 21, 2015 at 7:52
  • 1
    \$\begingroup\$ Assign multiple variables simultaneously (e.g. u=len(s) a=255 => u,a=len(s),255). Remove [] brackets from tuple, they're not needed. Replace loop by list comprehension (method will be evaluated as a side effect). \$\endgroup\$
    – Trang Oul
    Aug 21, 2015 at 9:53
  • 1
    \$\begingroup\$ @willem: Take a look at the problem specification (under Conversion); it specifically explains how your program should handle spaces. \$\endgroup\$
    – Lynn
    Aug 22, 2015 at 5:00
5
\$\begingroup\$

Python 3, 131 bytes

Simular to Dom Hastings' answer but implemented in python.

The string '|\x82\x88\x8ejF"#$%\x1f\x19\x137[\x7f~}' was built form the list [124,130,136,142,106,70,34,35,36,37,31,25,19,55,91,127,126,125] is the terminal colour codes to display in order. They have been filtered so they only include colours with saturation 100% and value 50%. The list was then sorted so the correct hues were displayed first.

Takes input from stdin and returns it to stdout.

Your terminal you are using MUST support ANSI escape codes to run this properly.

x=input();u=u'|\82\88\8ejF"#$%\1f\19\137[\7f~}';j=0
for i in x:print('\033[38;5;%dm%s'%(ord(u[j*18//len(x.replace(" ", ""))]),i),end="");j+=i!=" "

Or shortened version with literal byte characters (Didn't paste properly):

x=input();u='|<82><88><8E>jF"#$%^_^Y^S7[^?~}';j=0
for i in x:print('ESC[38;5;%dm%s'%(ord(u[(j*18)//len(x.replace(" ", ""))]),i),end="");j+=i!=" "

Literal hexdump:

783d696e70757428293b753d277c82888e6a46222324251f1913375b7f7e7d273b6a3d300a666f72206920696e20783a7072696e7428271b5b33383b353b25646d25732725286f726428755b286a2a3138292f2f6c656e28782e7265706c616365282220222c20222229295d292c69292c656e643d2222293b6a2b3d69213d2220220a

Thanks @swstephe for saving 9 bytes (and also making me notice my byte counting was ever so slightly very wrong)!

\$\endgroup\$
2
  • \$\begingroup\$ Why not replace "\x82" with the actual character? I wrote that string to a file and read it back into my script in binary. You could replace \033 with a raw escape character, or even \1f (hex). You declare "u", then only use it once. You can save a few characters by moving it down into the expression. You can avoid the int() call by using "//" for integer divide. \$\endgroup\$
    – swstephe
    Aug 27, 2015 at 17:06
  • \$\begingroup\$ oops, \033 is the same as \x1f. \$\endgroup\$
    – swstephe
    Aug 27, 2015 at 17:49
5
\$\begingroup\$

JavaScript (ES6), 114 117 125

Edit2 3 bytes saved thx @Dom Hastings Edit Invalid HTML, but working anyway.

Usual note: test running the snippet on an EcmaScript 6 compliant browser (notably not Chrome not MSIE. I tested on Firefox)

F=s=>document.write(s.replace(/\S/g,c=>`<b style=color:hsl(${i++/s.replace(/ /g,'').length*360},100%,50%>`+c,i=0))
<input value='Your challenge is to take input as a line of text and output it like this.' id=I size=100>
<button onclick='F(I.value)'>-></button>

\$\endgroup\$
6
  • 1
    \$\begingroup\$ No browser is completely ES6 compliant, yet. IE isn't even fully ES5 compliant yet! \$\endgroup\$ Aug 21, 2015 at 12:42
  • \$\begingroup\$ @SuperJedi224 I agree. I'm just saying: don't try it with Chrome or MSIE, while using Firefox it works \$\endgroup\$
    – edc65
    Aug 21, 2015 at 12:47
  • \$\begingroup\$ I feel like I shouldn't even say anything because I've only just got mine under yours, but I think you can omit the ) after your hsl( declaration as that appears to still work for me in Firefox! \$\endgroup\$ Aug 21, 2015 at 15:37
  • \$\begingroup\$ Also, you can change ${c}' to '+c to save 2 more... Better get back on with mine! \$\endgroup\$ Aug 21, 2015 at 15:47
  • \$\begingroup\$ This does not work on Safari 9.0. c: \$\endgroup\$ Oct 29, 2015 at 9:05
2
\$\begingroup\$

PHP, 165 bytes

Run with input as the parameter "s"

The HTML is invalid but it should render in all major browsers (tested in Chrome and Firefox)

<?php $n=preg_match_all("/[^ ]/",$q=$_GET['s']);for($i=$j=0;$j<strlen($q);$j++){if(" "!=$s=$q[$j])$i+=360;echo"<a style='color:hsl(".floor($i/$n).",100%,50%)'>".$s;}
\$\endgroup\$
1
\$\begingroup\$

PHP 4.1, 112 103 102 bytes

I've used @DankMemes' answer as a starting point. From there on, I've implemented a ton of changes, to the point that the code is different.

The implementation is similar, the code is totally different.

foreach(str_split($s)as$c)echo"<a style=color:hsl(",((" "^$c?$i+=360:$i)/strlen($s))|0,",100%,50%>$c";

To use it, simply set a value on a SESSION/GET/POST/COOKIE with the name s.

Result of running this function, on the test sentence:

<a style=color:hsl(4,100%,50%>Y<a style=color:hsl(9,100%,50%>o<a style=color:hsl(14,100%,50%>u<a style=color:hsl(19,100%,50%>r<a style=color:hsl(24,100%,50%> <a style=color:hsl(29,100%,50%>c<a style=color:hsl(34,100%,50%>h<a style=color:hsl(38,100%,50%>a<a style=color:hsl(43,100%,50%>l<a style=color:hsl(48,100%,50%>l<a style=color:hsl(53,100%,50%>e<a style=color:hsl(58,100%,50%>n<a style=color:hsl(63,100%,50%>g<a style=color:hsl(68,100%,50%>e<a style=color:hsl(72,100%,50%> <a style=color:hsl(77,100%,50%>i<a style=color:hsl(82,100%,50%>s<a style=color:hsl(87,100%,50%> <a style=color:hsl(92,100%,50%>t<a style=color:hsl(97,100%,50%>o<a style=color:hsl(102,100%,50%> <a style=color:hsl(107,100%,50%>t<a style=color:hsl(111,100%,50%>a<a style=color:hsl(116,100%,50%>k<a style=color:hsl(121,100%,50%>e<a style=color:hsl(126,100%,50%> <a style=color:hsl(131,100%,50%>i<a style=color:hsl(136,100%,50%>n<a style=color:hsl(141,100%,50%>p<a style=color:hsl(145,100%,50%>u<a style=color:hsl(150,100%,50%>t<a style=color:hsl(155,100%,50%> <a style=color:hsl(160,100%,50%>a<a style=color:hsl(165,100%,50%>s<a style=color:hsl(170,100%,50%> <a style=color:hsl(175,100%,50%>a<a style=color:hsl(180,100%,50%> <a style=color:hsl(184,100%,50%>l<a style=color:hsl(189,100%,50%>i<a style=color:hsl(194,100%,50%>n<a style=color:hsl(199,100%,50%>e<a style=color:hsl(204,100%,50%> <a style=color:hsl(209,100%,50%>o<a style=color:hsl(214,100%,50%>f<a style=color:hsl(218,100%,50%> <a style=color:hsl(223,100%,50%>t<a style=color:hsl(228,100%,50%>e<a style=color:hsl(233,100%,50%>x<a style=color:hsl(238,100%,50%>t<a style=color:hsl(243,100%,50%> <a style=color:hsl(248,100%,50%>a<a style=color:hsl(252,100%,50%>n<a style=color:hsl(257,100%,50%>d<a style=color:hsl(262,100%,50%> <a style=color:hsl(267,100%,50%>o<a style=color:hsl(272,100%,50%>u<a style=color:hsl(277,100%,50%>t<a style=color:hsl(282,100%,50%>p<a style=color:hsl(287,100%,50%>u<a style=color:hsl(291,100%,50%>t<a style=color:hsl(296,100%,50%> <a style=color:hsl(301,100%,50%>i<a style=color:hsl(306,100%,50%>t<a style=color:hsl(311,100%,50%> <a style=color:hsl(316,100%,50%>l<a style=color:hsl(321,100%,50%>i<a style=color:hsl(325,100%,50%>k<a style=color:hsl(330,100%,50%>e<a style=color:hsl(335,100%,50%> <a style=color:hsl(340,100%,50%>t<a style=color:hsl(345,100%,50%>h<a style=color:hsl(350,100%,50%>i<a style=color:hsl(355,100%,50%>s<a style=color:hsl(360,100%,50%>. 

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.