What this document is
A practical and theoretical reference for the Sensory Index — a deterministic visual language that turns a structured cupping profile into three complementary diagrams: Flavor (what it tastes of), Structure (how it feels in the mouth), and Character (where it comes from).
Two cuppers anywhere in the world, given the same profile and seed, will produce byte-identical SVGs. The system is fully reproducible: every random offset is derived from a 32-bit seed via the mulberry32 PRNG, and every visual parameter is a pure function of the input numbers. Nothing is hand-tuned after the fact.
Read this document linearly the first time. After that, jump directly to the section of the diagram you are about to publish — each chapter is self-contained and follows the same template (Purpose → Anatomy → Inputs → Math → Worked example → How to read → Pitfalls).
The Index at a glance
Every report produces three maps. They are designed to be read together, never in isolation.
Flavor
A polar bloom drawn around ten flavor families. Bulges grow toward the families that lead the cup; concentric rings encode intensity bands.
Structure
A Clifford strange attractor whose four coefficients are driven by acidity, body, sweetness and finish. Clarity controls how cleanly the lines render.
Character
A dithered field whose mark shape encodes the process, density encodes roast, and rotation encodes mood. A texture portrait of the coffee's origin story.
Foundational principles
Five non-negotiable rules that govern every diagram in the Index.
- 1 · Determinism. The same profile + seed always produces the same SVG. The seed string is hashed with FNV-1a into a 32-bit integer and fed to
mulberry32. NoMath.random()calls anywhere in the render path. - 2 · Separation of concerns. Flavor describes what, Structure describes how it feels, Character describes where it comes from. Never mix these axes within a single map.
- 3 · Three type sizes only. Every label is rendered with
tg.sm,tg.mdortg.lg. The user controls all three values; the renderer never injects a fourth. - 4 · Three colors only.
ink,bg, andaccent. Inversion swaps ink↔bg; one-color mode forces accent = ink. There is no fourth color. - 5 · Editable output. Every export is a vector SVG with the source profile JSON embedded in a
<metadata>block, so any diagram can be re-loaded into the generator and re-edited.
Polar bloom — what the cup tastes of
The Flavor Map is a radial graph drawn around ten flavor families positioned 36° apart on a circle. Two of those families — primary and secondary — receive Gaussian weight that pushes the bloom outward in their direction.
A · Purpose
The Flavor Map answers a single question: what flavor families lead this cup, and how loud are they? A small bloom with a single bulge says “quiet, focused”. A wide bloom with two opposing bulges and many wavy contours says “loud, layered, complex”.
B · Anatomy
- 1Family spoke — one of ten flavor families, 36° apart
- 2Primary anchor — drawn at the primaryFamily angle (floral = 0° → right). Accent dot + 'PRIMARY' label
- 3Bloom contour — radial samples smoothed into Bézier curves
- 4Core dot — anchors the polar coordinate origin
- 5Intensity ring — quartile of bloom radius (1–4)
- 6Legend strip — title, profile summary, reading hint
C · Inputs
| Variable | Type | Range | Meaning |
|---|---|---|---|
| primaryFamily | FlavorFamily | 1 of 10 | Dominant flavor center; pulls the bloom outward at its angle. |
| secondaryFamily | FlavorFamily | 1 of 10 | Supporting center; smaller Gaussian weight, slightly tighter spread. |
| intensity | integer | 1–5 | Loudness of the bloom — drives both primary and secondary Gaussian weights. |
| complexity | integer | 1–5 | Number of harmonics, contour count, and angular spread σ. |
| notes[] | string[] | 0–N | Free-text descriptors placed as labeled nodes near the relevant anchor. |
D · Mathematical model
Each flavor family sits at angle . The bloom is built as a sum of Gaussian peaks plus a small set of harmonic and noise modes:
where the angular Gaussian uses wrapped angular distance :
The intensity and complexity sliders feed the equation through linear remaps:
Each contour is sampled at 96 angular positions, low-pass filtered with a circular 1‑2‑1 kernel, then converted to a closed cubic Bézier path via uniform Catmull–Rom interpolation (control handles offset by , equivalent to tension ). This is what produces the silky, never-spiky outlines.
E · Worked example
Take Tropical Anaerobic (PRESETS[3]): intensity 5, complexity 5, primary tropical, secondary sweet. Substituting: , , , , . The result below shows a maximally bloomed, wide-spread map — exactly what the equation predicts.
F · How to read it
For enthusiasts
Look at where the bloom bulges outward — those are the flavors that lead the cup. More wavy rings means the flavor has more layers. A small, round bloom means a quiet, focused coffee.
For experts
Angular barycenter approximates the flavor centroid; radial variance is a proxy for complexity; spoke-weight asymmetry encodes the primary/secondary balance. Note nodes are placed at .
G · Common pitfalls
- High complexity does not mean spiky. Noise modes are bounded and low-pass filtered.
- Intensity rings are quartiles of this bloom's radius — they are not absolute cup-score bands.
- If primary and secondary are 180° apart (e.g.
floral+sweet), the bloom looks lobed; this is correct, not a bug.
Clifford attractor — how the cup feels
The Structure Map is a phase-space portrait of a Clifford strange attractor whose four coefficients are driven by acidity, body, sweetness and finish.
A · Purpose
Where Flavor describes the cup's vocabulary, Structure describes its grammar — the balance between brightness and weight, the duration of the finish, the cleanliness of the texture. Two coffees with identical flavor notes can have radically different attractor shapes.
B · Anatomy
- 1Dense lobe — body 4 raises the point count to ~6 900 inside the seed's anchor basin
- 2Outer filament — the anchor (chosen by seed HUIL-2204) sets the overall topology
- 3Filament thickness — clarity = 4 yields jitter j ≈ 12 px (near-clean lines)
- 4Legend strip — full structure scores at a glance
C · Inputs
| Variable | Type | Range | Meaning |
|---|---|---|---|
| acidity | integer | 1–5 | Drifts coefficient a within the chosen anchor's basin (cross-coupled with body). |
| body | integer | 1–5 | Drifts coefficient b and scales total point count (1→0.7×, 5→1.3×). |
| sweetness | integer | 1–5 | Drifts coefficient c (cross-coupled with finish) — modulates lobe folding. |
| finish | integer | 1–5 | Drifts coefficient d (cross-coupled with acidity) — controls envelope wrap. |
| clarity | integer | 1–5 | Inverse jitter — clarity 5 produces sharp filaments, clarity 1 a soft cloud. |
| seed | string | — | Selects the anchor tuple from the curated atlas (deterministic). |
D · Mathematical model
Naïvely mapping the five sliders to raw Clifford coefficients lands too often on point/line attractors — Clifford parameter space is famously "tetchy" and large regions collapse to a single fixed point. The Sensory Index uses a deterministic atlas-and-drift scheme instead: the seed picks a vetted anchor, and the structure sliders apply a small bounded drift inside its known-good basin.
Each cupping score is first centered to :
The seed deterministically selects an anchor from a curated atlas of 10 tuples sourced from Paul Bourke, HoloViz/Datashader and Pickover — each known to produce a rich, non-degenerate attractor:
Coefficients are then drifted inside the anchor's basin with cross-coupled slider mixes (so identical sliders don't align and collapse symmetry), bounded by , plus a tiny seed jitter :
Two safety projections then run before iteration. Basin repulsion keeps every coefficient at least 0.08 away from zero (the trivial fixed point):
Symmetry breaking: if all share sign and lie within 0.25 of each other (the all-equal point-attractor basin), is pushed outward by 0.35 to escape it.
The Clifford map is then iterated:
We discard the first 1000 iterations as transient and render points (range 4 200 – 7 800). The same iteration count is used everywhere — generator preview, guidelines diagrams, combined report and SVG export — so the same profile + seed always yields a byte-identical Structure SVG no matter where it is rendered.
Health-check fallback. Two collapse signals are checked on the rendered point cloud: the bounding box (either span below 0.6 = point/line collapse) and the occupancy of a coarse 64×64 grid over that bounding box (fewer than 64 distinct cells = short periodic cycle masquerading as a wide attractor). If either fires, the renderer deterministically walks to the next anchor in and retries — same inputs always yield the same fallback, preserving reproducibility.
Finally, clarity drives a per-point integer-hash jitter (no RNG in the hot loop, so renders are flicker-free and identical across runs):
with and .
E · Worked example — body & clarity contrast
Both diagrams below use the same Bold Roasted profile and the same seed. Only body and claritychange, so the comparison isolates density and filament softness without changing the attractor anchor.
F · How to read it
For enthusiasts
Tighter, denser shapes mean a fuller body. Crisper lines mean a cleaner cup. A wide, stretched envelope means a long finish.
For experts
The visible topology is the seed's anchor; the sliders modulate within a single basin. Comparing two coffees with different seeds compares different basins — that's the point. Comparing two coffees with the same seed isolates the structural delta cleanly.
G · Common pitfalls
- Don't compare the absolute size of two attractors — every render is auto-scaled to fit its canvas.
- Topology is dominated by the seed (anchor choice). Slider-only differences stay inside one basin and look like variations on a theme, not radical reshapes.
- If you ever see a near-empty render in the wild, the health-check has exhausted the atlas — report it; the atlas needs a new anchor.
- Setting clarity = 1 with body = 5 produces a cloud rather than filaments. That is the intended visual for a heavy, muddy cup.
Dither field — where the cup comes from
The Character Map is a thresholded continuous field rendered with a process-specific dithering algorithm. It tells the story of the bean's processing and roast, not its taste.
A · Purpose
Where Flavor describes what and Structure describes how it feels, Character describes where it comes from: how the cherry was processed, how dark it was roasted, and the emotional register the roaster intends.
B · Anatomy
- 1Mark shape — square / dot / cluster / line, set by process
- 2Grid density — base density set by roast level
- 3Field rotation — angle set by mood (0°–135°)
- 4Accent inversion — appears when complexity ≥ 4
- 5Legend strip — process · roast · mood, plus clarity & complexity
C · Inputs
| Variable | Type | Range | Meaning |
|---|---|---|---|
| process | Process | washed | natural | honey | anaerobic | Selects mark shape and threshold algorithm. |
| roast | Roast | light | medium-light | medium | medium-dark | Sets the base field density (0.28 → 0.72). |
| mood | Mood | 1 of 12 | Rotates the underlying field by 0°–135°. |
| clarity | integer | 1–5 | Sharpens the dither threshold (more clarity = more contrast). |
| complexity | integer | 1–5 | Adds higher-frequency field harmonics; ≥ 4 enables accent inversion. |
D · Mathematical model
The continuous field at each grid cell is built in rotated coordinates:
Higher complexity adds successive harmonics, averaged in:
The cell is then thresholded against a process-specific function:
with
and per-process activation rules — Bayer ordered dither (washed), uniform random (natural), distance-to-anchor (honey), and wave-interference + random (anaerobic). See §9 for the full table.
E · Worked example — same cup, four processes
We hold the entire profile constant and only change process. The reader can immediately see how dither algorithm alone changes the texture's voice.
washed
natural
honey
anaerobic
F · How to read it
For enthusiasts
The texture tells you how the bean was processed. Square dots = washed (clean, regular). Round dots = natural (organic, scattered). Clusters = honey (sticky, grouped). Lines = anaerobic (interference, experimental).
For experts
The dither algorithm itself is the metaphor: ordered Bayer is the deterministic, lab-clean output of washed processing; uniform random mirrors the wild variance of natural ferment; cluster distance encodes honey's localized stickiness; wave interference encodes anaerobic's temperature-controlled standing waves.
G · Common pitfalls
- Mood is visual rotation only. A "funky" map is not lower quality than a "clean" one — it is rotated 135° vs 0°.
- Density is roast, not strength. A medium-dark roast is denser than a light roast even at identical scores.
- Accent dots only appear when complexity ≥ 4. Don't be surprised by their absence on a complexity-3 map.
Reading three maps as one
The combined report places Flavor, Structure and Character side by side on a 3300×1200 canvas — the canonical layout for client-facing publication.
A diagnostic checklist for cross-reading:
- If Flavor leans citrus + Structure has tight high-acidity lobes + Character is washed Bayer at light density → expect a clean, bright, Ethiopian-style cup.
- If Flavor leans tropical + Structure shows wide-stretched lobes + Character shows anaerobic line interference → expect an experimental, fermented, fruit-forward cup.
- If Flavor leans nutty/cocoa + Structure is dense and round + Character is honey clusters at medium density → expect a comforting, syrupy, Central-American cup.
Six profiles, six fingerprints
The default catalog. Each card pairs the combined diagram with a short interpretive paragraph and a brand-voice cue.
Floral Citrus
Ethiopia · Heirloom
GUJI-4821- Process
- washed
- Roast
- light
- Mood
- vibrant
- Intensity
- 4/5
Floral Citrus is the catalog's archetype of clean brightness. Intensity 4 + complexity 4 produce a wide, harmonically rich bloom centered between floral and citrus; clarity 5 renders the Structure attractor — built from seed GUJI-4821's atlas anchor and a small drift from acidity 4, body 2, sweetness 4, finish 4 — as razor-thin filaments; the washed light roast paints a sparse Bayer-square Character field. Everything in this cup says 'precision'.
Evokes — morning light in a porcelain cup; jasmine, white nectarine, lemon zest.
Berry Natural
Ethiopia · 74158
YIRG-3310- Process
- natural
- Roast
- light
- Mood
- juicy
- Intensity
- 5/5
Berry Natural pushes intensity to 5 while keeping complexity at 4. The bloom dominates the berry hemisphere; the natural process trades the orderly Bayer grid for scattered round dots; lower clarity (3) softens the attractor jitter into a denser cloud. Seed YIRG-3310 picks a different atlas anchor than Floral Citrus, so the topology — not just the density — is genuinely different. Loud, wild, unmistakably fermented-fruit-forward.
Evokes — summer jam straight from the jar; strawberry, blueberry, vanilla.
Chocolate Nut
Colombia · Castillo
HUIL-2204- Process
- washed
- Roast
- medium
- Mood
- comforting
- Intensity
- 3/5
Chocolate Nut is the comfort end of the spectrum. Lower acidity (2) and higher body (4) drift the Structure coefficients toward the round, dense side of the seed's basin; the body multiplier raises the point count to ~6 900; the medium roast lifts character density to 0.58; honey-process clusters give the texture a sticky, grouped feel. Everything reads 'comforting' without being heavy.
Evokes — after-dinner espresso; cocoa nibs, toasted almond, brown sugar.
Tropical Anaerobic
Costa Rica · Geisha
COST-7711- Process
- anaerobic
- Roast
- medium-light
- Mood
- funky
- Intensity
- 5/5
Tropical Anaerobic is the most extreme profile in the catalog — every flavor slider at 5, complexity 5, clarity 2. The bloom reaches the canvas edge; the attractor is sampled at body-4 density (~6 900 points) with strong jitter (j = 36 px, ≈3.6% of the canvas); the anaerobic interference pattern sprinkles dashed lines through the Character field. Read this as a cup that demands attention.
Evokes — experimental tasting flight; pineapple, mango, rum-soaked candy.
Tea-Like Delicate
Panama · Geisha
PANA-5050- Process
- washed
- Roast
- light
- Mood
- delicate
- Intensity
- 3/5
Tea-Like Delicate inverts everything Tropical Anaerobic does. Body 1 + clarity 5 produce the sparsest attractor in the catalog (~4 200 points, zero jitter); complexity 3 keeps the bloom modest; light washed roast yields a faint, regular Bayer field. The whole report whispers.
Evokes — an early-morning pour-over for one; chamomile, lemon peel, honey.
Bold Roasted
Brazil · Bourbon
BRAS-9090- Process
- washed
- Roast
- medium-dark
- Mood
- bold
- Intensity
- 4/5
Bold Roasted lives at the opposite pole. Body 5 drifts coefficient b to the upper bound of its ±0.28 window inside seed BRAS-9090's atlas anchor and pushes the point count to its ceiling (~7 800); medium-dark roast lifts character density to 0.72; the primary 'roasted-earthy' family pulls the bloom into the lower hemisphere. The diagram should feel like weight on the page.
Evokes — dark cafés and rainy afternoons; dark chocolate, toasted nut, molasses.
Rules of use
The Index is a brand surface. Treat these rules as you would a logo lockup.
Color contract
Three tokens, applied in this order: ink on bg, accent for emphasis. Inversion swaps ink and bg as a single atomic operation; one-color mode collapses accent into ink. Never introduce a fourth color.
Typography contract
Three sizes only: tg.sm, tg.md, tg.lg. All three are user-controlled. The renderer never injects a size outside these tokens and never overrides the user's font family. If a label feels too small, change the token globally — never patch a single label.
Canvas contract
Every individual map is exactly 1000 × 1000. The combined report is exactly 3300 × 1200. These dimensions are inviolable — they exist so reports are guaranteed to print and tile cleanly across formats.
One renderer, one source of truth
Every diagram on this page — heroes, anatomies, worked examples, process variants and case studies — is produced by the same buildFlavorSVG, buildStructureSVG and buildCharacterSVG functions the generator calls. There is no second rendering path. The generator is the source of truth; this document is its interpretation. If you ever spot a divergence between a guideline figure and the generator output for the same profile + seed, it is a bug in the documentation, not the renderer.
Do / Don't
- Do publish the combined report rather than a single map whenever possible. The three maps make sense as a set.
- Do keep labels visible on client-facing reports — the legend is the contract that makes the diagram legible.
- Do embed the full profile JSON in the SVG metadata so the file remains editable.
- Don't recolor exported SVGs by hand — re-render with new style tokens instead.
- Don't combine one-color mode with complexity ≥ 4 character maps; the accent inversion that makes those maps interesting becomes invisible.
- Don't crop the canvas. The padding zone is reserved for the legend.
Glossary & formula recap
A single-page reference. Pin or print this section.
Glossary
- Acidity
- Perceived brightness. Drifts coefficient a within the Structure anchor's basin.
- Anchor
- A vetted (a, b, c, d) tuple from the Structure atlas (Bourke / HoloViz / Pickover) selected by seed.
- Atlas
- The curated set of 10 known-good Clifford coefficient tuples used by the Structure Map.
- Body
- Perceived weight. Drifts coefficient b and scales the Structure point count (0.7×–1.3×).
- Character
- The diagram describing process, roast and mood — i.e. origin story.
- Clarity
- Inverse of jitter. Higher clarity = sharper Structure lines, sharper Character dither.
- Complexity
- Number of harmonic modes layered into the Flavor and Character fields.
- Drift
- Bounded slider-driven offset (±0.28) applied inside an anchor's basin.
- Finish
- Aftertaste duration. Drifts coefficient d within the Structure anchor's basin.
- Flavor family
- One of ten canonical descriptors (floral, citrus, berry, …) at fixed 36° intervals.
- Health-check
- Bounding-box test on the rendered attractor; if it collapses, the renderer walks to the next atlas anchor.
- Mood
- An emotional descriptor used purely as a Character-field rotation angle.
- Process
- Post-harvest treatment of the cherry. Selects the Character dither algorithm.
- Roast
- Roast level. Sets the Character field's base density (0.28 → 0.72).
- Seed
- A short string hashed into mulberry32; selects the Structure anchor and seeds all jitter.
- Sweetness
- Perceived sugar. Drifts coefficient c within the Structure anchor's basin.
Per-process activation rules (Character Map)
| Variable | Type | Range | Meaning |
|---|---|---|---|
| washed | Bayer 4×4 | ordered | Cell is on if T > B[gy mod 4][gx mod 4] · s. Mark = square. |
| natural | uniform random | [0.05, 0.95] | Cell is on if T > U(0.05, 0.95). Mark = dot. |
| honey | anchor distance | 4-cell grid | Cell is on if dist to nearest 4-cell anchor < T·6. Mark = cluster. |
| anaerobic | wave interference | ± random | Cell is on if T > U(0,0.6) + I·0.6 − 0.2; 30% of marks become lines. |