I am writing several papers that require density plots of complex-valued functions of two variables f:R^2→C, where I'm primarily interested in the complex argument arg(f(x,y)) as a function of the input variables, but I wish to emphasize only the regions where the modulus |f(x,y)| is high.

Because of this, my approach thus far has been to produce density plots where the phase arg(f(x,y)) is encoded as the hue, and the modulus |f(x,y)| is encoded as a HSV chroma blend from white (where the modulus is zero, and arg(f(x,y)) is meaningless) to fully-saturated color at maximal modulus, where the phase is a meaningful variable.

However, this scheme suffers from being "non-orthogonal", in that some hue values (notably yellow and cyan) are much lighter than the baseline set by e.g. red or blue. I am looking for a combination that fixes these shortcomings, by providing:

  • A periodic color scale for the angle-like function value that passes through as many different colors as possible (i.e. at least some six distinct values, with a premium on contrast between them) while still maintaining a constant visual 'strength' throughout that traverse. I care primarily about subjective strength, but lightness is probably a good stand-in.
  • A conjugate color scale that goes from white to fully-'saturated' color that is as 'orthogonal' as possible to the angle variable.

As an example of what I'm looking for, this is a plot of the function f(x,y) = (x+iy)^2 exp(-(x^2+y^2)) using my current plotting conventions:

Mathematica graphics

Mathematica source via Import["http://halirutan.github.io/Mathematica-SE-Tools/decode.m"]["http://i.stack.imgur.com/NFhW1.png"]

Note in particular how you can immediately read the ^2 exponent from the fact that over a round-trip around the origin you do a red→green→blue→back-to-red loop twice, while still being able to clearly tell that the function is concentrated at a ring around the origin between radii ~0.5 and ~1.5. However, there are still perceived 'intensity' gradients azimuthally within this ring (notably the perceived 'lobes' around the blue areas), which I would like to avoid. I would also, if nothing else, like to smoothen the visual contrast around the sharp ray-like features at yellow, cyan and, to a slightly lesser extent, magenta.

A few final notes:

  • This problem arises because I'm attempting the ambitious goal of plotting two-dimensional values on a single density plot. I know that this is an ambitious goal and that it may be impossible to fulfill completely. Nevertheless, I think it is still worth doing as good as possible with it.
  • I have indeed considered other formats to present the same information; you can assume that I've explored them thoroughly and found that they are too cumbersome or otherwise unclear.
  • This approach will obviously only work when printed out in color, and grayscale printing (either on the print journal or when readers print it out at home) will leave most of this information out. You can assume that I've weighed the pros and cons of this and decided that it's still worth including.
  • This is also an accessibility problem for readers with color vision deficiency. This is, I feel, an unfortunate necessity of plotting two-dimensional values on a density plot, and I feel the added clarity for non-CVD readers is a worthwhile tradeoff once the information loss for CVD readers is mitigated via other means.

So, with that said: are there better options for dealing with this problem than the hue-chroma combination I'm currently using?

  • I'm hoping that this is the correct venue for this question; it cuts across fields and the generalized handling of visual information is of relevance, I think to a large swath of academia. If others have interesting suggestions for alternative venues, I'm happy to consider them. – E.P. Aug 5 '18 at 22:47
  • Have you seen the book Visual complex functions by E. Wegert? – Massimo Ortolano Aug 6 '18 at 4:04
  • Have you looked at colorbrewer and viridis to see if you can adapt one of those palettes? – JenB Aug 6 '18 at 7:38
  • 1
    @Massimo Judging by the publicly-available extracts, it looks like an interesting approach to complex analysis, but not quite a solution to this question: (i) the color map seems to be a straight hue map, and it shows the same sharp changes at yellow and cyan as my example; (ii) I'm not dealing with analytical functions, so I can't rely on intuition and convention to convey the amplitude information - it is a proper, independent degree of freedom that also needs to be reported. I'll try to get my hands on a copy, though - it looks like a nice book in any case. – E.P. Aug 6 '18 at 8:08
  • @JenB I have (and they're great), but I don't see any periodic color scales there. In a sense, plotting a periodic variable is incompatible with one of those projects' aims (printing well to grayscale) so it is natural that they don't include one. My goals here are in some ways 'orthogonal' to those projects. – E.P. Aug 6 '18 at 8:51

As you note, there are problems with the typical rainbow colour map.

Kovesi has done some work on perceptually uniform colour maps. Code is available here: https://peterkovesi.com/projects/colourmaps/index.html

In my work, I replaced the rainbow map with the Kovesi's first cyclic colour map for the angle values, and used either saturation or intensity (multiplication) for the modulus. In hindsight, it would probably make sense to map the raw modulus values to saturation / intensity using one of the perceptually uniform greyscale linear maps as well.

enter image description here


I am a fan of the Solarized colour scheme, which amongst others contains eight colours that have:

  • equal perceived brightness,
  • distinct tones.

Using these eight colours for the angle and then whitening (using Solarized’s white) according to the absolute value gives me for your example:

example function

You may still perceive some sharp transitions, but most of these depend on the specific screen in my experience.

Note that this colour scheme is optimized for screen and not for print. In my experience it also works nice with projectors, and I use it for presentations (using the more intense variants for black and white).

Appendix: Code

Here is the Python code I used to generate the above image:

import numpy as np

def rgb_to_numerical(string):
    return np.array([
            for i in range(3)

white   = rgb_to_numerical("#FDF6E3")
colours = [rgb_to_numerical(colour) for colour in [
        "#B58900",  # yellow
        "#CB4B16",  # orange
        "#DC322F",  # red
        "#D33682",  # magenta
        "#6C71C4",  # violet
        "#268BD2",  # blue
        "#2AA198",  # cyan
        "#859900",  # green

segment_size = 2*np.pi/len(colours)

def tone(angle):
    segment_num = int(angle//segment_size)%len(colours)
    stride = (angle%segment_size)/segment_size
    left_colour = colours[segment_num]
    right_colour = colours[(segment_num+1)%len(colours)]
    return left_colour*(1-stride)+right_colour*stride

def bleach(colour,amount):
    amount **= 3
    return colour*(1-amount)+white*amount

f = lambda z: z**2 * np.exp(-np.abs(z)**2)

size = (600,600)
re_range = (-3,3)
im_range = (-3,3)
mod_range = (0.55,0)
rgb_array = np.empty((*size,3),dtype=np.uint8)

for k,x in enumerate(np.linspace(*re_range,size[0])):
    for l,y in enumerate(np.linspace(*im_range,size[1])):
        value = f(x+y*1j)
        modulus = np.abs(value)
        angle = np.angle(value)
        intensity = (modulus-mod_range[0])/(mod_range[1]-mod_range[0])
        rgb_array[k,l] = bleach(tone(angle),intensity)

from PIL import Image

In general a good colour map is perceptually uniform (avoiding how the yellow and magenta stand out in your original), works when printed in black and white, and works for at least the majority of colour-blind people.

I'm not aware of any cyclic maps that manage black and white well (it's not really possible, because you can't have a monotonic progression of lightness and get back to where you started), and I'm not sure how well any of them do for colour-blindness, but perceptually uniform is achievable.

The other answers are good in pointing out the theory and offering some examples. The "Phase" map from CM-OCEAN is another example, and is available in "ready made" form for Python and Matlab.

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