← All Labs
INTERACTIVE APPLET · COMPLEX ANALYSIS · DOMAIN COLOURING

Complex Functions as Domain Colouring

A function of a complex variable eats a point of the plane and spits out another — it is really a map from a 2D input to a 2D output, so its full graph would need four dimensions. Domain colouring squeezes all four onto one canvas: for every input point \(z\), compute \(w=f(z)\), then paint that pixel by the output's phase as hue (which direction \(w\) points) and its magnitude as brightness (how big \(w\) is). Suddenly a formula becomes a landscape. Zeros become dark points where every colour swirls together — vortices. Poles blaze white and the colour wheel spins the other way — sinks. Type any \(f(z)\) below and read its hidden anatomy straight off the plane.

Every pixel is one output value \(f(z)\) — hue = phase, brightness = magnitude

Paint a complex function & read its zeros and poles

Type f(z) · drag to pan · scroll to zoom · hover to inspect a point
WebGL · live GLSL shader

DRIVE IT

hover the canvas to inspect
phase 0 (→ +real) phase 2π/3 phase 4π/3 zero (dark) pole (bright)
The hue wheel wrapping a point tells you it is a zero or a pole: at a simple zero all six hues meet once, spinning counter-clockwise as you circle it; at a simple pole they spin clockwise and the pixel glows bright. Count how many times the colours cycle to read the order of the zero or pole.
f(z) =
ZOOM (view half-height)2.00
Syntax: z, i, pi, e; operators + − * / ^; functions sin cos tan exp log sqrt sinh cosh tanh conj abs re im gamma zeta. Implicit multiplication works: 2z, (z-1)(z+1). Example: (z^2-1)*(z-2-i)^2/(z^2+2+2i).

WALKTHROUGH

  1. Start on the default f(z) = z² − 1. Find the two dark spots at \(z=\pm 1\): those are the zeros. Circle one slowly with your eye — the hue cycles through the full wheel once, so each is a simple (order-1) zero.
  2. Load preset z³ − 1. Now there are three vortices at the cube roots of unity, spaced 120° apart — the picture is the factorisation, no algebra required.
  3. Load 1/z. The origin now glows: a pole. Watch the colours wheel the opposite way compared to a zero — that reversed winding is the signature of a pole.
  4. Toggle Magnitude brightness off to see a pure phase portrait (Wegert style): only hue, so zeros and poles look identical except for winding direction. Toggle Modulus rings on to see brightness contours at every doubling of \(|f|\) — dense rings mean the value is changing fast.
  5. Load zeta(z) and zoom into the strip \(0<\text{Re}(z)<1\) (drag up-left, scroll in). The non-trivial zeros line up on \(\text{Re}(z)=\tfrac12\); the pole at \(z=1\) glows. Then try gamma(z): poles march along the negative integers, and there are no zeros anywhere.

AHA

THE INSIGHT
A complex function is a 4-dimensional object — two dimensions in, two out — yet by trading the two output dimensions for colour (hue = direction, brightness = size) the whole map fits on a flat 2D canvas. The deepest features you'd normally have to calculate — where the function is zero, where it blows up, and to what order — become things you can simply see: a swirl of every colour meeting at a point is a zero or a pole, and the number of times the rainbow repeats around it is the order.

RESEARCH NOTE

The winding you can literally count is the argument principle: for a closed loop \(\gamma\) enclosing no boundary singularities, \(\frac{1}{2\pi i}\oint_\gamma \frac{f'(z)}{f(z)}\,dz = Z - P\), the number of zeros minus poles inside (with multiplicity). Domain colouring turns this integral into a glance. Further reading: Elias Wegert, Visual Complex Functions (phase portraits); Tristan Needham, Visual Complex Analysis; Frank Farris on colour and analytic maps; and the Riemann-lab in this repo for ζ specifically.

EXPLANATION

The trouble with a complex function \(f:\mathbb{C}\to\mathbb{C}\) is dimensional. The input \(z=x+iy\) needs a plane, and so does the output \(w=u+iv\); an honest graph would live in four dimensions, which no sheet of paper can hold. The old workaround was to draw two real surfaces (the real and imaginary parts, or the modulus \(|f|\)) side by side, but that hides the very thing complex analysis cares about — how the input plane is transformed. Domain colouring keeps the input plane exactly as it is and stores the two output dimensions at each pixel, as colour. The standard code, due in its modern form to Frank Farris and refined by Elias Wegert, maps the output's argument \(\arg f(z)\) to hue around the colour wheel and its modulus \(|f(z)|\) to brightness. One glance now shows both the direction and the size of the output everywhere at once.

What makes this more than a pretty picture is that the two most important local features of an analytic function announce themselves visually. Near a zero of order \(m\), the function behaves like \(c\,(z-z_0)^m\), so as you walk a small circle once around \(z_0\), the output angle sweeps around \(m\) full turns: the hue wheel repeats \(m\) times, and because \(|f|\to 0\) the centre is dark. Near a pole of order \(m\), \(f\) behaves like \(c/(z-z_0)^m\); the magnitude blows up so the centre is bright/white, and crucially the argument runs the other way — the hue wheel winds \(m\) times clockwise. So a zero and a pole are both rainbow vortices, distinguished only by which way the colours spin and whether the middle is dark or bright. Counting hue cycles reads off the order directly — you are seeing the argument principle, \(\frac{1}{2\pi i}\oint \frac{f'}{f}\,dz = Z-P\), with your eyes instead of an integral.

The extra shading options sharpen the reading. A pure phase portrait throws away magnitude and paints only hue; it is Wegert's favourite because it is conformally faithful — since analytic maps preserve angles, the coloured regions meet at right angles wherever \(f'\neq 0\), and you can spot saddle points (where \(f'=0\)) as places the checkerboard of colours pinches. Adding modulus rings draws a contour every time \(|f|\) doubles (a level set of \(\log_2|f|\)); tightly packed rings mean the value is changing fast — steep terrain — while wide rings mean a plateau. Phase spokes add rays at evenly spaced arguments, and together rings and spokes form a curvilinear grid that is the image of a polar grid under \(f\): you are literally watching how the function warps space.

Because the recipe only needs a way to evaluate \(f(z)\), it generalises effortlessly, which is why this applet compiles whatever you type into a GPU shader that runs the arithmetic in parallel for every pixel. Polynomials show their roots; Möbius maps \((az+b)/(cz+d)\) show a single zero and single pole trading places as you vary the coefficients; \(\sin\) and \(\exp\) reveal their infinite ladders of zeros; \(e^{1/z}\) exposes the wild colour storm of an essential singularity. Push to \(\Gamma(z)\) and you see a function with poles at every non-positive integer and no zeros at all; push to the Riemann zeta function \(\zeta(z)\) and the trivial zeros at \(-2,-4,-6,\dots\), the lone pole at \(z=1\), and the famous non-trivial zeros hugging the critical line \(\text{Re}(z)=\tfrac12\) all appear in one image — the same phase-portrait technique the Riemann lab in this repo uses, now available for any map you can write down. That is the payoff: a whole branch of mathematics that used to live in the imagination becomes a landscape you can pan, zoom, and read.