Project B: The P300 speller
A grid of letters flashes row by row and column by column. The one you are counting produces a bump 300 milliseconds later. Average, classify, and the computer types it. The 1988 design that started the field, built in a weekend.
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.
Look at a six-by-six grid of letters and silently count the flashes of the one you want. Rows and columns flash in random order. Whenever your letter’s row or column flashes, your brain produces a P300P300A positive bump about 300 milliseconds after a rare or meaningful stimulus, the basis of the classic BCI speller. Glossary entry: a positive bump over the top and back of the head about three hundred milliseconds later, because that flash was the rare, meaningful event you were waiting for. Average the responses to each row and each column, find the row and the column with the bump, and their intersection is your letter. Farwell and Donchin built this in 1988. It still works, and building it teaches averaging, classification, and honest evaluation in one project.
A single flash of the target row produces a 5 µV P300 in 20 µV of noise. How many flashes must you average before the P300 is twice the noise?
About 64. Noise shrinks as the square root of the number of trials: after N flashes the noise is 20/√N. For the 5 µV signal to be twice the noise, 20/√N = 2.5, so √N = 8 and N = 64. Real spellers get away with fewer, around 10 to 15 per row, because a classifier can use the whole waveform rather than one peak and because it only needs to rank the six rows, not measure any one cleanly. But the arithmetic is why P300 spellers are slower than SSVEP ones.
The stimulus
A 6×6 grid (26 letters plus digits and a few commands). Each “sequence” flashes all six rows and six columns once each, in random order: 12 flashes, each 100 ms on, 75 ms off, so a sequence takes about two seconds. Repeat 10 to 15 sequences per character. Push an LSL marker at each flash saying which row or column it was. The user counts flashes of their letter; the counting is what makes the P300 large.
The classifier
Epoch from 0 to 800 ms after each flash, at Pz, Cz, PO7, PO8, Oz and a few others. Downsample to about 20 Hz, so each epoch is 16 samples per channel, and concatenate channels into one feature vector. Train a linear discriminant analysisLinear discriminant analysis (LDA)A simple, robust classifier that draws a straight boundary between classes; the workhorse of EEG decoding for two decades. Glossary entry classifier (or the stepwise version, SWLDA, which the original literature uses) on labelled epochs: target flashes versus non-target flashes, from a calibration run in which you spell a known word.
To select a character, apply the classifier to each flash’s epoch to get a score, sum the scores over all sequences for each row and each column, pick the row and column with the largest sums. Summing scores is the averaging, done after classification instead of before.
xDAWN spatial filtering before LDA, or a Riemannian classifier on the epoch covariances, improves things noticeably and is what current systems use. Start with LDA so you can see what the improvement is.
Evaluation
Accuracy per character as a function of the number of sequences; the curve rises and saturates. Information transfer rate at each point, so you can pick the number of sequences that maximizes bits per minute rather than accuracy. And the honest split: train on the calibration word, test on a different word spelled later in the session, then on a different day.
- Write the PsychoPy grid with row/column flashing and LSL markers.
- Record a calibration run: spell a ten-character word with 15 sequences per character. Epoch and average target versus non-target epochs at Pz. Confirm the P300 is visible in the average; if not, check timing with the photodiode and check that you are actually counting.
- Train LDA on the calibration epochs. Cross-validate by character (train on 8 characters, test on 2), not by epoch.
- Spell a test word. Plot accuracy and ITR against number of sequences.
- Add xDAWN or a Riemannian classifier and repeat. Note the improvement.
- Test on another person, on a different day. Report all numbers.