This challenge is about gradually shifting hues in an image to make pretty pictures like this:

big Starry Night (original)


Write a program or function that takes in two non-negative integers and an image in any common image file format of your choosing (you can take a path to the image or the raw image data).

We'll call the first integer the cycles and the second integer the offset.

We'll also define floating-point step as 360 times cycles divided by the area of the image, or step = 360 * cycles / (image width * image height).

For each pixel P in the image, moving one row at a time, left-to-right, top-to-bottom (i.e. in reading order if the pixels were letters), do the following:

  1. Increase the hue of P by offset degrees (looping around from 360 to 0 if necessary).

  2. Then increase offset by step.

Save, display, or output raw the resulting image in any common image file format.

This procedure incrementally increases the hue of all the pixels in the image, making cycles full loops around the hue rainbow, starting by initially offsetting the hue by offset.

When cycles is 1 and offset is 0, as in the Starry Night image above, the top and bottom rows of pixels have practically no hue shift but in between there's a full color cycle.


  • Cycles can be any non-negative integer but you may assume that offset is from 0 to 359 inclusive.

  • When cycles is 0, every pixel in the image will have its hue shifted by exactly offset since step must be 0 too. (In this case if offset is 0 then the image doesn't change at all.)

  • You may assume cycles and offset are input as floats if desired (i.e. 1.0 instead of 1). (I realize they needn't be integers at all, it just makes the challenge simpler.)

  • "Hue" refers to the RGB color space version, common in HSL/HSV color models.




Cycles = 1, offset = 0:

river output 1

Cycles = 1, offset = 180:

river output 2



Cycles = 2, offset = 60:

spheres output


(Thanks ArtOfCode.)

Cycles = 1, offset = 120:

sunset output


(Thanks Doorknob.)

Cycles = 1, offset = 0:

doorknob output 1

Cycles = 4, offset = 0:

doorknob output 2

Cycles = 200, offset = 0:

doorknob output 3

Cycles = 30000, offset = 0:

doorknob output 4

(These images may not be pixel perfect due to imgur compressing them.)


The shortest code in bytes wins. Tiebreaker is higher voted answer.

Answers that post their own cool looking test images will get more brownie points from me.

  • 6
    \$\begingroup\$ This looks like Doorknob is smoking some pot. \$\endgroup\$
    – Denker
    Commented May 3, 2016 at 0:52
  • \$\begingroup\$ I presume an array of integers as return value would be included in "or output raw"? \$\endgroup\$
    – Marv
    Commented May 3, 2016 at 1:12
  • 2
    \$\begingroup\$ @Marv No. I mean the raw bytes of the image (in your chosen common format, say ppm) can be piped direct to stdout. \$\endgroup\$ Commented May 3, 2016 at 1:14
  • 2
    \$\begingroup\$ Do outputs have to be identical to your examples? I'm getting slightly different images. \$\endgroup\$
    – DJMcMayhem
    Commented May 3, 2016 at 2:37
  • 1
    \$\begingroup\$ @DrGreenEggsandHamDJ If you can't visually tell a difference then it's probably fine. Pixel perfection is not required (imgur may have lossily compressed my images anyway). \$\endgroup\$ Commented May 3, 2016 at 3:29

3 Answers 3


Pyth, 86 bytes, full program

=N.tE7=Z*6*.n0cEl.n'zMmhtS[0255ss*VG.>+Lc-1.tH1 3[.tH1Kc.tH0@3 2_K)d)3.wmmgk~-NZd'z

Pyth doesn't have built-in color space conversions - this is the real deal.

Takes input in the following format on stdin:


The output image is written to o.png.

This works by rotating the color cube around its diagonal, and then clamping any values outside of the range.

If a is the angle to rotate by, and r, g, b is the input color, we calculate the new color r', g', b' by:

o = cos(a), i = sin(a) / sqrt(3)
n = (1 - o) / 3
m = [n + o, n - i, n + i]
clamp(c) = max(0, min(255, c))
r' = clamp(r*m[0] + g*m[1] + b*m[2])
g' = clamp(r*m[2] + g*m[0] + b*m[1])
b' = clamp(r*m[1] + g*m[2] + b*m[0])

Python, 379 bytes

from PIL.Image import*
from colorsys import*
def f(H,c,I):
 for x,y,z in zip(r.getdata(),g.getdata(),b.getdata()):
  e=255.;h,s,v=rgb_to_hsv(x/e,y/e,z/e);t=hsv_to_rgb(h+H,s,v);H=H+S%1.;x,y,z=[int(x*e)for x in t];R.append(x);G.append(y);B.append(z)
 p=Image.putdata;p(r,R);p(g,G);p(b,B);return merge('RGB',(r,g,b))

This takes a path to a .jpg as input. It will not work with png, although you can change r,g,b=i.split(); to r,g,b=i.split()[:3]; to load a png image.

Here are some images:


enter image description here

Offset: 0, Cycles: 4

enter image description here


enter image description here

Offset 0, 1 cycle:

enter image description here


enter image description here

Offset 0, 2.5 cycles:

enter image description here


Java (Full program), 491 488 bytes (Thanks @Geobits)

import java.awt.*;import java.io.*;import static javax.imageio.ImageIO.*;class Q{public static void main(String[]v)throws Exception{File f=new File(v[2]);java.awt.image.BufferedImage b=read(f);for(int i=0,j,h=b.getHeight(),w=b.getWidth();i<h;i++)for(j=0;j<w;){Color c=new Color(b.getRGB(j,i));float[]a=new float[3];c.RGBtoHSB(c.getRed(),c.getGreen(),c.getBlue(),a);b.setRGB(j++,i,c.HSBtoRGB((a[0]+Float.valueOf(v[1])/360+(i*w+j)*Float.valueOf(v[0])/w/h)%1,a[1],a[2]));}write(b,"png",f);}}


import java.awt.*;
import java.io.*;

import static javax.imageio.ImageIO.*;

class A79200 {
    public static void main(String[] v) throws Exception {
        File file = new File(v[2]);
        java.awt.image.BufferedImage image = read(file);
        for (int i = 0, j, height = image.getHeight(), width = image.getWidth(); i < height; i++)
            for (j = 0; j < width; ) {
                Color color = new Color(image.getRGB(j, i));
                float[] arr = new float[3];
                color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), arr);
                image.setRGB(j++, i, color.HSBtoRGB((arr[0] + Float.valueOf(v[1]) / 360 + (i * width + j) * Float.valueOf(v[0]) / width / height) % 1, arr[1], arr[2]));
        write(image, "png", file);


  • Usage: Pretty straightforward. Compile with java -c Q.java. Run with java Q <cycles> <offset> <imagepath>. Will override the existing image, so be careful.

  • I was gonna make a method only solution at first but I didn't quite know how to handle imports on those, so I figured I'd go full , this is probably not going to win anyways :^)


Image 1: 1 cycle, 0 offset


Image 1: 1 cycle, 180 offset


Image 2: 2 cycles, 60 offset


Image 3: 1 cycle, 120 offset


Image 4: 1 cycle, 0 offset


Image 4: 4 cycles, 0 offset


Image 4: 200 cycles, 0 offset


Bonus: The Starry Night, 1 cycle, 0 offset

enter image description here

  • 1
    \$\begingroup\$ For future reference, you can do imports for method-only answers just the same as you normally would. Just put them outside the method body and count the bytes. You can also fully-qualify classes instead of importing them if you'll only be needing it once, to save a few bytes in some cases. \$\endgroup\$
    – Geobits
    Commented May 3, 2016 at 13:02
  • \$\begingroup\$ Also, is there a reason you're importing java.io.File instead of java.io.* ? \$\endgroup\$
    – Geobits
    Commented May 3, 2016 at 13:02
  • \$\begingroup\$ Thanks, good to know. And second, nope, theres no reason. Good point. \$\endgroup\$
    – Marv
    Commented May 3, 2016 at 16:56
  • \$\begingroup\$ Why import **static**, not just import? \$\endgroup\$ Commented May 3, 2016 at 20:41
  • 1
    \$\begingroup\$ So that I'm able to call ImageIO::read and ImageIO::write without having to prepend ImageIO.: This adds 9 bytes (static .*) but saves 16 (ImageIO. twice). \$\endgroup\$
    – Marv
    Commented May 3, 2016 at 20:51

Your Answer

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

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