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

ComponentRangeMeaning
H (hue)0–360°Position on the color wheel. 0/360 = red, 120 = green, 240 = blue
S (saturation)0–10 = grey, 1 = full color
L (lightness)0–10 = black, 0.5 = normal, 1 = white
A (alpha)0–10 = 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 yellow

These 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.

FunctionHue rangeNotes
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]:

Δ valueEffect
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

ArgumentDescription
cfsColor configuration array (see below)
pcPrevious color — used when continuing a run
contWhether 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 trace
  • new_conttrue if this is a continuation (slight variation), false if 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:

  1. A uniform random number r ∈ [0, 1) is drawn.
  2. If r < p₁ AND we are already in a continuous run (cont=true): call color_from_color(pc) — slight variation of current color.
  3. 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 contrast

Designing 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 thresholdVisual effect
0.95+Almost uniform — one color dominates the whole image
0.80Smooth runs of 4–8 traces in similar colors, punctuated by jumps
0.60More fragmented — color changes every 2–3 traces on average
0.30Noisy, every trace is likely a different family