i've been doing a lot of studying on building form with just values
this means that i've been pushing myself to draw a LOT with as little linework as possible
i've been trying to rely on linework less and less
instead, i've been building form with shading and contrast, focusing on the different values in a reference
the process pretty much goes like: desaturate the reference in your head, locate, then anchor where the darkest darks and lightest lights are, then address the midtones in between as i draw (the path reveals itself as it goes kind of vibe)
this always starts out with outlining those value zones that i'm training my eye to see
values in drawing
the difference between tones (values) is what builds form. and the number of zones isn't fixed — some images have three natural breaks, some have eight
the subjectivity lives in the midtones! how granular do you wanna go? which shades do you let bleed together, which subtle differences do you preserve?
discrepancies in the midtones, whether intentional, or happy accidents, lead to stylized works
the key idea: sigma is gaze distance
when you stand close to a reference, you see every detail because your eyesight narrows
this means that your vision is tunneled to exactly what you're focused on
when you back away, those details blur. the granular value differences average into their neighbors and collapse into broader zones. from far enough away, you only see the foundational contrasts (and how those enable the form of the object)
the smoothing parameter in the algorithm — sigma — is a mathematical model of that perceptual process.
low sigma is close up, hyper-focused, many granular zones. high sigma is backed away, softened gaze, fewer broad zones
it's a gaze distance parameter. the math is simulating how the visual system collapses detail when attention widens
the pipeline, briefly
- convert to grayscale using perceptual weights
- flatten the 2d pixel grid into a 1d array
- build a histogram across 256 bins
- apply gaussian smoothing
- find local minimums in the smoothed curve (the zone boundaries)
- assign every pixel a zone based on where it falls between boundaries
- compute the mean luminosity of each zone (the value palette)
sigma controls the width of that bell. wider bell = more aggressive smoothing = fewer zones
pragmatism over capability
at first the slider went past 40 values which was pretty useless LOL
an artist's palette typically is comprised of 3, 5, 7, or 9 values. nobody is drawing from a 40-tone palette
some clarity
it's cool how math and drawing are doing the same thing -- both kind of representing the human experience in interpreting the information that we receive. both are attempts to capture how we experience the world
getting this thing live and usable (deleting the backend that i built)
posterization was SLOWWWWWW; the biggest bottleneck to an enjoyable experience was the speed. every action had a noticeable lag
the math was sub-millisecond. the entire round-trip was the cost. browser → vercel proxy → fly machine → fastapi → pipeline → png re-encode → base64 → json → back through every hop. on a warm machine that floor is around 600ms. on a cold fly machine — auto_stop_machines = "stop", free idle, no minimum running — it's 2 to 10 seconds on the first action.
the algorithm itself was overkill for a server in the first place, so i deleted the backend
what's next
-
perceptual weighting — weight zones by their importance to visual perception. a small zone might be more perceptually significant than a large one.
-
personal taste training — let the model learn from how a specific artist sees using labeled data from the artist's own zone mappings
what i learned
i love drawing and learning and drawing and learning and building and drawing and learning and realizing that nothing is truly disjointed from one another
stack: next.js, typescript, web workers, deployed via vercel. no backend. repo: github.com/welcomeneil/tones