Eyebrows
If you could keep only one part of a robot face, keep the eyebrows. They carry more emotional
information per pixel than the eyes and the mouth combined, and this lab builds them out of
poly() so they can bend instead of just tilting.
Why a Polygon Instead of a Line
A straight diagonal line reads as an eyebrow, barely. A four-point polygon with a bend in the middle reads as an eyebrow that belongs to somebody. The curve is what does it.
1 2 | |
Every number is an offset from the eyebrow's anchor point, in signed shorts, so negative offsets are allowed — which is what lets the shape be written around a center rather than from a corner. Read the two arrays side by side and you will see the second is the first with its x-values negated: the mirror image, written by hand. Every one of those offsets is also the smartwatch kit's number times 1.5 — they are pixel offsets, not positions, so leaving them at the smaller kit's values would have drawn a brow two-thirds the size of the eye it has to sit over.
The Cost of a Filled Brow
shapes.poly() fills a shape by drawing one horizontal run per row it covers. A brow this size is about thirty rows — thirty runs down the wire, still cheap. Open lib/shapes.py and read the scanline fill if you have not yet.
Sample Program Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
Here's what that program draws on the display:

Look at how much attitude those two shapes add compared with a flat line. Same eyes, same mouth — completely different character.
The Eyebrow Vocabulary
Almost every emotion in this kit is reachable by changing two things about the brows: their tilt and their lift.
| Brow position | Reads as |
|---|---|
| Inner ends angled down toward the nose | Angry, determined |
| Inner ends angled up | Sad, worried, pleading |
| Both raised high and flat | Surprised |
| Both low and flat | Bored, skeptical |
| One up, one down | Doubtful — the single most useful mismatched face |
One Number, Opposite Mood
Flip the sign on the second number of each array — the -18 — and the brows arch the other way. One character in the file, and my whole face changes what it is saying.
Things to Try
- Flip the arch, as in the tip above, and describe the new expression in one word before you look at anything else.
- Draw the brows with
NO_FILL. An outlined brow is a thin wire frame, and the bigger this panel gets, the worse a one-pixel outline looks — the same single row of pixels now has to carry a face half again as wide as the smartwatch kit's. - Give the two brows different shapes by editing one array. A face with mismatched brows reads as skeptical, and it takes exactly one changed number to get there.
- Move the anchor. Change
EYE_Y - 66toEYE_Y - 45so the brows sit closer to the eyes. A low brow crowds the eye and reads as intensity; a high one reads as openness.
References
- Drawing Polygons — the scanline fill that makes a curved brow possible
- Drawing Lines — the straight-line eyebrow, and the rule about which way to tilt
- The Emotion Table — where brow tilt and lift become two columns of data
- Eyebrows (1.2" smartwatch kit) — the same lab at 240×240, where every offset in the polygon arrays is two-thirds this size