E-tongue Flavor Painter
An electronic tongue (e-tongue) is a sensor array that detects the five basic taste modalities: sweet, sour, salty, bitter, and umami. Each receptor outputs a signal intensity between 0 and 100, derived from electrochemical responses to dissolved compounds. This project is a software prototype that maps those signals into a generative painting system.
Why build this?
It started with a conversation about colour.
A friend and I share a long-standing fascination with how colours carry meaning, and Japanese culture has a particularly beautiful way of thinking about this. There is a whole tradition of naming colours after fleeting, specific moments: 萌黄 (moegi, the yellow-green of new willow buds pushing through in early spring), 桜鼠 (sakura-nezu, a soft grey tinted with the memory of a cherry blossom), 瓶覗 (kame-nozoki, a blue so pale and clear it looks like open sky reflected in the surface of a dyeing vat). Each name is less a description of a colour and more a description of a moment that happens to have a colour. The idea that a single colour can hold an entire sensory memory, a season, a feeling, a time of day, is something we kept returning to.
One evening we were sitting in a small izakaya, the kind with a low counter and a smoky grill behind it, sharing yakitori and sake. The conversation drifted, as it tends to, and we found ourselves trying to describe the taste of what we were eating. Not just “salty” or “smoky” but the actual character of it: the way the tare glaze on the chicken had a particular depth, the clean cold finish of the sake, the faint char that sat underneath everything. And we hit the same wall that always comes up with taste. It is immediate and vivid and completely resistant to language.
That is when the parallel clicked. Colour and taste are alike in this way. Both are direct sensory experiences that you can gesture at but not fully transfer. You can point to a swatch, you can say “the pink of sakura,” and something is communicated, but the experience itself stays private. Taste is even harder. There is no swatch to point to.
The question we ended up with was: what would a flavour look like if you painted it? Not a chart or a diagram, but a real painting, something generative that accumulated and evolved the way a meal does. If the five basic taste signals each had a colour and a brushstroke character, what image would a bowl of ramen leave behind? What about a glass of wine, or the combination of both?
This is the prototype for that idea. It uses the signal model of a real e-tongue sensor array, maps each receptor to a visual language, and lets you compose a meal from a food library and watch it paint itself onto a canvas.
The colour choices are not arbitrary. Sweet is sakura pink, soft and rounded. Sour is sharp yellow, the colour of citrus zest. Salty is a warm dusty coral, somewhere between sand and sea. Bitter is deep violet with a greenish undertone: the violet comes from how cartoons have always depicted poison, that theatrical purple danger, and the green comes from my friend pointing out that the most bitter things we know are medicinal plants, the ones that are good for you precisely because they are hard to take. Umami is pale lavender (purple + white), quiet and lingering. Together they produce something that feels, at least to us, like it has a flavour.
The sensor model
Each receptor operates on a 0 to 100 scale, representing normalised electrochemical signal intensity:
| Receptor | Primary compound class | Example high-signal food |
|---|---|---|
| Sweet | Sugars (glucose, fructose, sucrose) | Honey (90) |
| Sour | Hydrogen ions (pH / acidity) | Lemon (90) |
| Salty | Sodium chloride and related salts | Soy sauce (95) |
| Bitter | Polyphenols, alkaloids, capsaicin | Espresso (85) |
| Umami | Free glutamate, IMP, GMP | Parmesan (90), Anchovy (88) |
These values are not arbitrary. Each food in the library was assigned receptor intensities by reasoning from its actual chemistry: sugar content maps to sweet, pH maps to sour, free amino acid concentration maps to umami, and so on. Capsaicin and other pungent compounds are mapped to bitter because that is the closest e-tongue analogue for spicy heat.
Color mixing
Each receptor is assigned a base colour that was chosen to feel perceptually natural:
- Sweet: sakura pinkish-white, like cherry blossom
- Sour: sharp saturated yellow
- Salty: dusty warm coral, a blend of orange and cool blue
- Bitter: deep violet (cartoon poison) with a greenish undertone (medicinal plants)
- Umami: soft lavender, a pale purple tending toward white
When multiple receptors fire simultaneously, their colours blend through weighted RGB averaging. Each receptor’s colour channel contribution is proportional to its signal intensity, so a food with sweet=80 and bitter=20 will produce a strongly amber-tinted mix with only a trace of violet.
The blend is recomputed live as you adjust sliders or add and remove foods. The mixed colour is shown in the swatch at the top of the panel, along with its hex value and a labelled description of the dominant taste profile.
The food library
The library contains over 100 foods and drinks across five ingredient categories (Fruits, Drinks, Sweets, Savory, Dairy) and eight national cuisines (Japanese, Italian, Indian, Mexican, French, Chinese, Thai, Korean).
Rather than restricting you to a single preset, the library lets you compose a meal from multiple items. Selecting “Strawberry” and “Dark Chocolate” together produces a blended signal where the sweet-bitter tension plays out visually on the canvas. Adding “Rice” to a spicy Korean dish pulls all five signals toward neutral, which is exactly what rice does to the palate.
Blending formula. When multiple foods are selected, the blended receptor values are computed as an unweighted arithmetic mean:
blended[R] = round( sum(food.values[R] for each selected food) / number of foods )
This is deliberately simple. Every food contributes equally regardless of portion size. A few example blends to illustrate the behaviour:
- Strawberry + Dark Chocolate: sweet 47, sour 26, salty 4, bitter 37, umami 8. The sweet-bitter pull between fruit and chocolate comes through clearly.
- Coffee + Milk: sweet 20, sour 17, salty 9, bitter 40, umami 14. Bitterness drops from 75 to 40, which mirrors how milk softens coffee in practice.
- Pad Thai + Rice: sweet 23, sour 20, salty 29, bitter 4, umami 25. All channels are pulled toward the middle, producing a softer, more diffuse painting.
Foods can be browsed by category or filtered by cuisine. The cuisine filter is useful for exploring regional flavour signatures: Japanese cuisine clusters heavily toward umami, Mexican dishes tend toward bitter and salty from chilli and spice, and French sweets produce some of the highest sweet signals in the library.
The waveform engine
A single static receptor reading would produce a single brushstroke and stop. Real eating and drinking unfold over time: receptors fire on stimulus, adapt during sustained exposure, and decay after the food is gone.
To simulate this, each receptor has an oscillator that moves around its set baseline value:
- The oscillator frequency is biologically motivated. Sour fires fast (sharp acid spikes). Umami fires very slowly (the long glutamate aftertaste). Sweet and salty sit in between.
- The oscillator amplitude is proportional to the baseline intensity. A receptor set to 80 oscillates with much wider swings than one set to 10.
- Deviations above baseline shift the painted colour toward a warmer, more saturated hue. Dips below baseline shift toward the complementary colour.
In Auto-Paint mode, new strokes are committed to the canvas on every oscillator cycle. The Speed slider controls how quickly cycles advance, from a slow contemplative rhythm at speed 1 to a rapid continuous feed at speed 10. The waveform display at the bottom of the canvas shows all five oscillators in real time, colour-coded by receptor.
Paint modes
Organic
Each receptor paints a characteristic soft brushstroke that was designed to feel like the sensation it represents:
- Sweet produces radial petal blooms, soft-edged and symmetrical
- Sour fires sharp starburst spikes, narrow and radiating outward
- Salty scatters crystalline drops, small and dispersed across a local area
- Bitter grows recursive ink tendrils that branch outward like a vine
- Umami spreads wide, diffuse halo rings that fade gradually at the edges
Stroke size and opacity are driven by signal amplitude. A sweet receptor at 90 paints large, opaque blooms; the same receptor at 10 barely registers on the canvas.
Pixel
The same five receptor characters are rendered as crisp pixel patterns: concentric rings, radiating lines, grid-aligned blocks, dendritic branching, and Gaussian clouds. Colours from each receptor layer independently on the canvas through alpha compositing. The result has a more structured, data-visual quality compared to the soft organic mode.
Reaction-Diffusion (work in progress)
A Gray-Scott reaction-diffusion simulation where receptor intensities drive the chemical feed and kill parameters, producing emergent Turing patterns: spots, stripes, labyrinthine structures, and travelling waves. Each taste maps to a distinct region of the Pearson (1993) parameter space, and blended signals shift the morphology continuously. The parameter tuning and visual rendering are still being refined.
Technical notes
The entire application is a single self-contained HTML file included into a Jekyll post via a Liquid tag. There are no external dependencies beyond the browser’s native Canvas 2D API.
Canvas rendering. All drawing happens on a single <canvas> element sized to the available viewport with device-pixel-ratio awareness. The waveform strip is a separate canvas below. Organic and pixel strokes are painted with source-over compositing. The reaction-diffusion output is rendered from a typed Float32Array grid that is written to a pixel buffer via getImageData and putImageData each simulation tick.
Oscillators. Each receptor maintains a phase accumulator that advances by a per-receptor frequency constant every animation frame. The output is a sinusoid with a small noise term added to break the regularity. The baseline (slider value) acts as a DC offset; the oscillator output is the AC component on top of it.
Food library structure. Each entry in FOOD_LIBRARY carries an id, name, cat (ingredient category), an optional cuisines array, and a v object with the five receptor values. Filtering in category mode checks f.cat; filtering in cuisine mode checks f.cuisines.includes(activeCuisine). Search overrides both filters and matches against f.name as a case-insensitive substring.
Color mixing implementation. The swatch colour is computed by iterating over the five receptors, weighting each receptor’s RGB triple by its normalised intensity, summing, and dividing by the total weight. This is equivalent to a centroid in RGB colour space, which has known perceptual limitations (metamerism, non-linearity) but is fast and produces intuitive results for the five-colour palette used here.
Controls
Use the sliders to set receptor intensities directly, or build a meal from the food library. Click Paint Stroke to commit a single reading, or enable Auto-Paint to simulate a continuous live sensor feed. The Speed slider controls how rapidly new strokes are added. Canvas background can be switched between dark and light. The Clear button resets the canvas without clearing the current meal selection.