Color System
Colors in this project are HSLA values (hue, saturation, lightness, alpha). A probabilistic state machine drives how color evolves across the rotational traces that make up each artwork.
HSLA quick reference
| Component | Range | Meaning |
|---|---|---|
| H (hue) | 0–360° | Position on the color wheel. 0/360 = red, 120 = green, 240 = blue |
| S (saturation) | 0–1 | 0 = grey, 1 = full color |
| L (lightness) | 0–1 | 0 = black, 0.5 = normal, 1 = white |
| A (alpha) | 0–1 | 0 = transparent, 1 = opaque |
Preset named colors
Three colors are provided as starting points:
chinese_red = HSLA(11, 0.70, 0.39, 1.0) # warm dark red
cadmium_orange = HSLA(28, 0.84, 0.55, 1.0) # bright orange
naples = HSLA(48, 0.94, 0.67, 1.0) # soft warm yellowThese are useful as the initial color passed to drawZθfunc4b.
Color family generators
Each function returns a random HSLA color sampled from a specific region of color space. Calling the same function twice gives different results.
| Function | Hue range | Notes |
|---|---|---|
ryellow() | 45–58° | High saturation (0.8–1.0), mid-to-high lightness |
rorange() | 26–32° | Moderate saturation and lightness |
rred() | −11–11° | Wraps around 0°; includes warm reds and red-oranges |
rgreen() | 80–160° | High saturation, low-to-mid lightness — vivid greens |
rblue() | 190–260° | High saturation, mid lightness — blue to violet |
ablack() | — | Always exactly black: HSLA(0, 0, 0, 1) |
awhite() | — | Always exactly white: HSLA(0, 0, 1, 1) |
color_from_color — smooth variation
color_from_color(c; Δ=[0.1, 0.1, 0.1, 0.1])Takes an existing color and returns a slightly different one. Each HSLA component is perturbed by a uniform random value in [−Δ/2, +Δ/2]. S, L, and A are clamped to [0, 1]; H can wrap freely.
The Δ parameter is a 4-element vector [ΔH, ΔS, ΔL, ΔA]:
Δ value | Effect |
|---|---|
| Small (0.02) | Barely perceptible shift — color drifts very slowly |
| Default (0.1) | Subtle variation — clearly the same family but visibly different |
| Large (0.5) | Wild jumps — effectively a random color each time |
This function is called internally by mycolor during "continuous" runs. Calling it in a loop produces a gradual color drift that feels organic.
mycolor — the color state machine
c, cont = mycolor(cfs; pc=current_color, cont=cont_flag)This is the engine that drives color evolution across all rotational traces. Each call makes a probabilistic decision: either continue the current color run (smooth variation) or jump to a new color family (sharp transition).
Arguments
| Argument | Description |
|---|---|
cfs | Color configuration array (see below) |
pc | Previous color — used when continuing a run |
cont | Whether the previous step was a continuation (true) or a jump (false) |
Return values
Returns (new_color, new_cont):
new_color— the HSLA color to use for this tracenew_cont—trueif this is a continuation (slight variation),falseif it was a jump to a new family
The returned new_cont should be passed back as cont on the next call.
Color configuration arrays (cfs)
A cfs array defines the probability distribution over what mycolor does on each call. It is a Vector of (threshold, color_fn) pairs where the thresholds are cumulative probabilities summing to 1.
Structure:
cfs = [
(p₁, color_fn₁), # first entry: probability of a continuous run
(p₂, color_fn₂), # subsequent: discrete color family jumps
(p₃, color_fn₃),
...
(1.0, color_fnₙ), # last threshold must be 1.0
]How it works:
- A uniform random number
r ∈ [0, 1)is drawn. - If
r < p₁AND we are already in a continuous run (cont=true): callcolor_from_color(pc)— slight variation of current color. - Otherwise: scan through entries 2, 3, … and return the color function for the first threshold
r < pᵢ.
The first entry's threshold is therefore the probability of staying in a continuous color run. Higher values make the artwork smoother; lower values make it more fragmented.
The four preset schemes
cfs1 = [(0.80, rgreen), (0.87, rblue), (0.93, ryellow), (1.0, awhite)]
# Greens dominate; occasional blue/yellow/white jumps
cfs2 = [(0.80, ryellow), (0.87, rorange), (0.93, rred), (1.0, awhite)]
# Warm yellows; drifts toward orange and red
cfs3 = [(0.80, rred), (0.82, ablack), (0.87, rgreen), (0.95, rblue), (1.0, awhite)]
# Mostly red, with a small black accent and occasional green/blue/white jumps
cfs4 = [(0.80, rorange), (0.85, rblue), (0.90, rred), (0.95, ablack), (1.0, awhite)]
# Orange-dominant with blue contrastDesigning your own cfs
The thresholds are cumulative, so the probability of each jump is the difference between consecutive thresholds:
# 70% continuous, 20% jump to blue, 10% jump to white
my_cfs = [(0.70, rblue), (0.90, rblue), (1.0, awhite)]Rules:
- The last threshold must be
1.0. - Probabilities must be strictly increasing.
- Any of the seven color functions — or your own
() -> HSLA(...)lambda — can appear as a color function. - The first entry's color function is used for the initial color when
pc=nothing.
Effect of the continuation probability (first threshold)
| First threshold | Visual effect |
|---|---|
| 0.95+ | Almost uniform — one color dominates the whole image |
| 0.80 | Smooth runs of 4–8 traces in similar colors, punctuated by jumps |
| 0.60 | More fragmented — color changes every 2–3 traces on average |
| 0.30 | Noisy, every trace is likely a different family |