Project D: Eyes as a joystick
Electrooculography. Two electrodes beside the eyes, a near-DC amplifier, and a cursor that follows your gaze. Weirdly simple, and the first step of an assistive speller.
You are skimming: the title, the first figure, and the short version. Switch to Read in the header for the full page, or Deep to open every deep dive.
The eyeball is a battery. The front of the eye is a few millivolts positive relative to the back, so when you look left, the skin beside your left eye becomes slightly more positive and the skin beside your right eye slightly more negative. Two electrodes at the outer corners of the eyes pick up about 15 microvolts per degree of gaze. Amplify it, read it on an Arduino, and a dot on the screen follows your eyes. This is electrooculographyElectrooculography (EOG)Recording eye position from the standing voltage across the eyeball, which moves with the eyes; also the main source of blink artifacts in EEG. Glossary entry. It is also why blinks wreck EEG, and it is the first stage of the speller you might build for someone with ALS in Phase 5.
You look 30 degrees to the left and hold your gaze there. What does the EOG trace do?
A step up that stays while you hold. EOG is a position signal, not a velocity signal, which is what makes it usable as a joystick. It is also why the amplifier must pass very low frequencies: a high-pass at 0.5 Hz would turn the step back into a blip within a couple of seconds.
Parts
| Part | Where | Qty | Approx. |
|---|---|---|---|
| 9 V batteries and snap connectors, ×4 Two batteries give you a ±9 V split supply. Never power anything touching skin from the wall or from a laptop USB port. | Any store | 1 | $10 |
| Arduino Nano Every or Uno (10-bit ADC, USB serial) A clone works. You need a real analog input; the Nano Every and Uno are the simplest path to the Web Serial plotter. | Arduino store, Amazon, Micro Center | 1 | $15 |
| Gold cup EEG electrodes, set of 5 with leads Reusable. Clean them after each use. Touch-proof (DIN 1.5 mm) connectors are the standard. | Amazon, OpenBCI shop, medical suppliers | 1 | $30 |
| INA128 instrumentation amplifier (DIP-8), ×2 The AD620 or INA118 are interchangeable for our purposes. Get the through-hole DIP version for breadboards. | Digi-Key, Mouser | 2 | $18 |
| Resistor and capacitor assortment 1% metal-film resistors matter for the instrumentation amplifier's balance. Include 0.1 µF ceramics and a few 1 to 10 µF film or ceramic caps. | Amazon, Adafruit | 1 | $15 |
| Ten20 conductive paste, one jar Holds the cups on and lowers impedance. Nuprep skin gel helps too. | Amazon, medical suppliers | 1 | $12 |
| Total (prices drift; treat as a ceiling) | $100 | ||
The circuit
The Project B amplifier with the high-pass lowered as far as it will go. Replace the 1 µF and 330 kΩ high-pass with 10 µF and 1 MΩ, for a corner at about 0.016 Hz, which holds a step for a minute before it decays. Keep the gain at 2000: a 30° look is about 500 µV, which becomes 1 V at the output, a comfortable swing for the Arduino. Keep the 35 Hz low-pass. For a second, vertical channel, build a second copy of the amplifier or use the AD8232 breakout (its high-pass is too high for clean EOG but it will show you the deflections).
Electrodes
Horizontal channel: one gold cup at the outer corner of each eye, about a centimetre from the eye on the temple bone. Vertical channel, if you have it: one above the eyebrow and one below the eye on the cheekbone, same eye. Reference and DRL on the forehead between the eyebrows, or on an earlobe.
Software
Read the channel, subtract a baseline captured while looking at the centre of the screen, scale to pixels, move a dot. Drift is the whole problem: the electrode offset wanders by hundreds of microvolts over minutes, which looks exactly like a slow gaze shift. Add a key that recentres the baseline, and later a rule that slowly pulls the baseline toward the current value whenever the eyes have been still for a while.
A minimal Python listener, with pyserial and pygame:
import serial, pygame
ser = serial.Serial('/dev/ttyUSB0', 115200)
pygame.init(); screen = pygame.display.set_mode((800, 600))
base = None; gain = 0.8
while True:
line = ser.readline().decode(errors='ignore').strip()
if not line: continue
h = float(line.split(',')[0])
if base is None: base = h
for e in pygame.event.get():
if e.type == pygame.KEYDOWN and e.key == pygame.K_SPACE: base = h # recentre
x = int(400 + gain * (h - base))
screen.fill((16, 21, 18)); pygame.draw.circle(screen, (124, 255, 178), (x, 300), 12); pygame.display.flip()
- Modify the high-pass and confirm on the plotter that the trace holds a step: touch an input briefly to a 9 V battery through a 1 MΩ and 100 Ω divider (about 900 µV) and watch the output step and hold for tens of seconds.
- Apply the horizontal electrodes and the reference. Look centre, left, centre, right. Watch the plotter. You should see clean steps of opposite sign.
- Blink. Notice the blink is a sharp spike on the horizontal channel and a large deflection on the vertical channel. File that away for the artifact bestiary.
- Run the listener. Recentre with the spacebar while looking at the centre. Look around.
- Save a plot of left-centre-right-centre with the step sizes in microvolts noted, and measure your microvolts-per-degree by looking at two marks a known angle apart.
Why this project matters
Eye-tracking is the most reliable channel a person with advanced ALS has, until the eyes fail too. Commercial eye-gaze systems are cameras and cost thousands of dollars. EOG is a different sensing principle that works with the eyes closed, in the dark, and for a few dollars, and it is the foundation of the hybrid speller in Phase 5. The drift problem you just met is the entire reason that project is hard.