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.

asked Aug 21, 2015 at 1:12
\$\endgroup\$
5
  • \$\begingroup\$ Can the output be a Data URI? That's the output from the HTML canvas \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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!

answered Aug 21, 2015 at 11:22
\$\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

answered Aug 21, 2015 at 7:22
\$\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\$ Commented 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\$ Commented Aug 21, 2015 at 7:48
  • 1
    \$\begingroup\$ Also: replace float(j) to 1.*j \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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円8円ejF"#$%1円f19円137円[7円f~}';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)!

answered Aug 21, 2015 at 13:03
\$\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 1円f (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\$ Commented Aug 27, 2015 at 17:06
  • \$\begingroup\$ oops, 033円 is the same as \x1f. \$\endgroup\$ Commented 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>

answered Aug 21, 2015 at 10:10
\$\endgroup\$
6
  • 1
    \$\begingroup\$ No browser is completely ES6 compliant, yet. IE isn't even fully ES5 compliant yet! \$\endgroup\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Aug 21, 2015 at 15:47
  • \$\begingroup\$ This does not work on Safari 9.0. c: \$\endgroup\$ Commented 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;}
answered Aug 21, 2015 at 18:45
\$\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%>. 

answered Aug 21, 2015 at 21:23
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.