Constructing Color Contrasts and Harmony (Itten)
Learning objectives
- Learner can construct Itten's 12-hue circle and locate the seven contrasts within it
- Learner can apply hue, light-dark, cold-warm, complementary, and saturation contrasts intentionally in a composition
- Learner can build harmonious dyads/triads/tetrads via geometric figures and test them against the neutral-gray rule
Capstone — one whole task that evidences the objectives
Design and render a small abstract composition in cut paper (or code) that deliberately deploys at least four of Itten's seven contrasts, using a geometrically-derived harmonic chord, and verify the chord by showing that its constituents mix to a neutral gray.
Prerequisite modules
When you live-code visuals for an AV set, the palette is your harmony section: a shader that hits the projector with an arbitrary RGB triple is the visual equivalent of mashing random notes. This module builds the whole task of composing a palette on purpose — choosing hues the way a musician chooses a chord, then deploying contrast the way a player deploys dynamics. The capstone is that task in miniature: one abstract composition, four deliberate contrasts, a chord derived geometrically from the 12-hue circle, and an audit — do its constituents mix to neutral gray?
The arc starts fully supported. First, construct the circle itself and drill it until, as Itten insists, you know its 12 hues like a chromatic scale; the atoms on the 12-hue circle and the seven contrasts are your map. Next, work the contrasts one at a time in tiny studies: undiluted hue triads first (no mixing needed), then light-dark using the natural brilliance of hues, then cold-warm and complementary pairs, then the four ways of desaturating a color. The quality-vs-quantity distinction is the JIT tool that keeps these studies clean — hold brilliance constant when studying hue, and vice versa. Simultaneous contrast is not re-taught here; it arrives already trained from the prerequisite module on color relativity, ready to be spotted when it shows up in your studies. Finally, inscribe triangles and squares in the circle to generate chords, and test them with the neutral-gray rule before composing unsupported.
Every required atom is a gate: you cannot derive the chord without the circle and its geometric figures, deploy four contrasts without knowing each one, or verify gray without the harmony rule. The supporting atoms enrich the practice — additive-vs-subtractive mixing matters if you verify gray in code rather than paper, and palette transposition and master studies extend the skill into ongoing repertoire-building.
Walkthrough
A palette is harmony; contrast is dynamics. We’ll build both in code — each of Itten’s contrasts as a tiny p5 study — using HSB colour, where a hue is just an angle on the 12-hue circle (like a note on a chromatic scale). Paste each sketch into the p5 web editor and press Run. Each step is a complete, standalone sketch.
1 — the 12-hue circle. In colorMode(HSB, 360, 100, 100) a hue is a degree 0–360. Lay the 12 hues in a ring and you have Itten’s colour circle — your keyboard ([[itten-12-hue-color-circle]]).
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(0, 0, 12);
translate(200, 200);
for (let i = 0; i < 12; i++) {
fill(i * 30, 90, 95);
let a = (i / 12) * TWO_PI - HALF_PI;
ellipse(cos(a) * 130, sin(a) * 130, 54, 54);
}
}
2 — contrast of hue. The simplest, loudest contrast: undiluted hues far apart on the circle, held at equal brightness so only hue differs ([[contrast-of-hue]]).
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(0, 0, 12);
const hues = [0, 120, 240]; // red, green, blue — a triad
for (let i = 0; i < 3; i++) { fill(hues[i], 90, 95); rect(i * 133, 0, 134, 400); }
}
3 — light–dark contrast. Hold hue and saturation constant, vary only brightness — a value ladder. This is the contrast that carries readability on a projector ([[light-dark-contrast]]).
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(0, 0, 12);
for (let i = 0; i < 8; i++) { fill(40, 60, i / 7 * 100); rect(i * 50, 0, 50, 400); }
}
4 — cold–warm contrast. Split the circle: warms (reds→yellows) advance, cools (blues→greens) recede. Pairing them makes depth ([[cold-warm-contrast]]).
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(210, 40, 70); // cool field
fill(30, 90, 100); // warm form advances
ellipse(200, 200, 200, 200);
}
5 — complementary contrast (and the gray test). Opposites on the circle (here ~30° and ~210°) intensify each other. Itten’s harmony rule: a true complementary pair mixes to neutral gray — the swatch on the right is their average, and it should read as grey, verifying the chord ([[complementary-contrast]]).
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(0, 0, 12);
fill(30, 90, 95); rect(0, 0, 160, 400); // orange
fill(210, 90, 95); rect(160, 0, 160, 400); // blue — complement
// average of the two in RGB ≈ neutral grey (the harmony audit)
colorMode(RGB, 255);
fill(128, 128, 128); rect(320, 0, 80, 400);
colorMode(HSB, 360, 100, 100);
}
6 — a composition with four contrasts + a geometric chord (the capstone). Inscribe a triangle in the circle (three hues 120° apart) for a balanced chord, then deploy several contrasts at once: hue (the three colours), light–dark (bright forms on a dark field), cold–warm (the blue against the orange/red), and contrast of extension (a small dot of the loudest hue against large calm areas):
function setup() { createCanvas(400, 400); colorMode(HSB, 360, 100, 100); noStroke(); }
function draw() {
background(220, 30, 18); // dark cool field (light-dark + cold-warm)
fill(30, 85, 95); rect(40, 120, 200, 160); // large warm orange (triad hue 1)
fill(150, 70, 85); ellipse(280, 110, 120, 120); // green (triad hue 2)
fill(270, 80, 95); ellipse(300, 300, 70, 70); // violet (triad hue 3)
fill(55, 100, 100); ellipse(120, 320, 26, 26); // tiny loud yellow — contrast of extension
}
What good looks like. A composition that reads deliberately, not accidentally: the hues come from one geometric chord (so they belong together), and the contrasts do specific jobs — value carries the read, extension gives one accent punch, warm/cool gives depth. If it looks muddy, your hues aren’t far enough apart or everything’s the same brightness (push the light–dark). If it looks garish, too many colours at full saturation and equal size — shrink the loud one (extension) and calm the rest. The gray test is your harmony audit: complements that average to grey are balanced. (Skill map: live-visualist Domain B2 — colour, restraint, palette.)
Now make it yours. Rotate the triad chord (add the same offset to all three hues) to transpose the palette. Swap the triangle for a square chord (four hues 90° apart). Make the accent dot the complement of the field. Animate the field hue slowly with frameCount.
Runnable examples
Generated from the context/ instrument corpus by concept (redistributable idioms only). Do not edit — regenerate with gen-module-examples.mjs.
gamma-correction
col = pow(col, vec3(1.0/2.2));
glsl-0021 · public-domain
pow ([lo,mid,hi]) 0.4545 >> rgb
punctual-0034 · CC0-1.0
vignette
smoothstep [0.3,1.2] (1 - fr) >> mul
punctual-0029 · CC0-1.0
col *= smoothstep(1.2, 0.3, length(uv));
glsl-0035 · public-domain
color-temperature
col *= vec3(1.1, 1.0, 0.85);
glsl-0116 · public-domain
value-contrast
stroke(255); fill(0); rect(0, 0, w, h)
p5live-0027 · CC0-1.0
Atoms in this module
Required — these gate the capstone
Supporting — enrichment, not gating
Part of curricula
- Live Visualist — zero to performing live-coded & generative visuals — Generative canvas — colour, motion, and Hydra live-coding required
- Shader Artist — real-time GPU craft to a demoscene-grade visual — The fragment shader as a per-pixel instrument recommended
- VJ — visual performance with projection, light & video — See, source & mix: your first clip set optional
Unlocks — modules that require this one