Rainbow Tunnel: Vanishing Colors Illusion

01.08.23    visualization    Python

A couple of years ago, while experimenting with my program Viplex, I have come across an optical phenomenon: When creating what could be best described as a rainbow tunnel, looking at its center, and zooming out the view continuously for a couple of seconds, I got the impression the colors of the rainbow vanished. Instead of colors the whole view was shades of grey!

I have created an animated image of the rainbow tunnel. You can try the vanishing colors illusion for yourself. Fix your gaze at the very center of the animated tunnel below. (If it is not animated with the view slowly zooming out and thus concentric circles moving into the center, your browser does not support the animation format.) After a couple of seconds you should see some effect. The image (or select hues of it) should turn into shades of grey.

Rainbow Tunnel Animation

After blinking or looking at something else the colors return for me. The image becomes normal again. By quickly looking at the center of the zooming tunnel again, the colors could be made to vanish again fast. Sometimes not all hues of the rainbow disappear or return at the same speed. In general the feeling of this illusion is uncanny. It feels quite strange to suddenly see grey where you know there should be colors.

Even after some research I am not aware of any similar illusion. The closest thing I have come across is Silencing. There the viewer stops seeing changes in objects once they are in motion. This is different to this vanishing colors illusion where the viewer stops seeing colors after some time without any specific trigger. If anybody knows about any similar illusion or about (scientific) research into such an effect, please let me know!

I have not yet tried out what components of the rainbow tunnel are strictly necessary for this illusion. Maybe the presence of the whole set of colors is not necessary. Maybe even the animation itself is not mandatory and a still image is enough. I don't know yet; this needs much more analysis. This could be an topic for a follow-up post.

Code

If you want to experiment with this phenomenon, here is the Python code I created to generate the frames of the rainbow tunnel animation:

import math
import colorsys
from PIL import Image


def color_for_pixel(x, y, t) -> tuple[int, int, int]:
    """
    Returns color in RGB [0, 255] for point (x,y) in [-0.5,0.5]^2
    at time t in [0, 1)
    """

    if (x, y) == (0, 0):
        return (0, 0, 0)
    # angle in range [0, 2pi]
    phase = math.atan2(y, -x) + math.pi
    # double phase to complete rainbow twice in one rotation
    phase = (2*phase) % (2*math.pi)
    # https://dlmf.nist.gov/help/vrml/aboutcolor#S2
    q = phase / 2 / math.pi * 4
    if 0 <= q < 1:
        hue_360 = 60 * q
    elif 1 <= q < 2:
        hue_360 = 60 * (2 * q - 1)
    elif 2 <= q < 3:
        hue_360 = 60 * (q + 1)
    else:
        hue_360 = 60 * (2 * q - 2)
    hue = hue_360 / 360

    d = (x*x + y*y)
    value = 0.85 + 0.1 * ((math.log(d, 3) + t) % 1)

    saturation = 0.95

    r, g, b = colorsys.hsv_to_rgb(hue, saturation, value)
    return (int(255*r), int(255*g), int(255*b))


def main():
    width = 400
    height = 400
    count = 20
    center = (width / 2, height / 2)
    for i in range(count):
        image = Image.new("RGB", (width, height))
        for x in range(width):
            for y in range(height):
                position = ((x - center[0]) / width, (y - center[1]) / height)
                color = color_for_pixel(*position, t = i / count)
                image.putpixel((x, y), color)
        image.save(f"out/im_{i}.png", "PNG")
        print(f"Image {i} / {count}  done")


if __name__ == "__main__":
    main()