Skip to main content

Medium colour · shaders · trigonometry

Colour wheel

Angle becomes hue, distance becomes saturation, and the disc turns into a rainbow.

Write wheel(x, y, size). It is called for every pixel of a size by size canvas, x running right and y running down from 0 at the top left. Return a colour [r, g, b] with each channel from 0 to 255.

Work from the pixel centre: dx = x + 0.5 - size / 2 and dy = y + 0.5 - size / 2. Let R = size / 2 and d = Math.hypot(dx, dy). If d > R, return [0, 0, 0].

Otherwise the hue h in degrees is Math.atan2(dy, dx) * 180 / Math.PI, plus 360 if that is negative, and the saturation is s = d / R. Convert with value 1: let k = h / 60, i = Math.floor(k) % 6, f = k - Math.floor(k), p = 1 - s, q = 1 - s * f, t = 1 - s * (1 - f). Then (r, g, b) is (1, t, p) for i = 0, (q, 1, p) for 1, (p, 1, t) for 2, (p, q, 1) for 3, (t, p, 1) for 4 and (1, p, q) for 5. Multiply each by 255 and return them without rounding (the canvas rounds for you).

Example: on a 64 canvas the pixel (63, 31) sits almost on the right edge, hue just under 360 and saturation near 1, so wheel(63, 31, 64) is close to [255, 3.95, 7.76]. Answers are checked to within 0.000001.

Tests you can see

  • wheel(63, 31, 64)[255,3.952754967752652,7.7577125753798555]
  • wheel(0, 0, 64)[0,0,0]
  • wheel(32, 60, 64)[145.2328315186663,255,27.85567703103108]

More tests are hidden. The server runs them all before a time counts.

Run your code and this canvas paints itself.

javascriptwheel()

Tab indents · Cmd/Ctrl+Enter runs