You can't tell. Neither can I.
You use machine learning every single day. Your phone unlocks when it sees your face. Your keyboard finishes your words. Some app decides which video you see next, and it is usually right — and you have probably never been told how.
This page opens up one of those programs all the way. Not a huge one. A tiny one. It has 1,338 numbers inside it — small enough that we can look at every single part and understand what each one does. The job we gave it is strange and interesting: a person lies still with 64 sensors on their head, imagining squeezing one fist, and we ask a computer which one. Then we find out how it tries — and catch it doing something nobody expected.
Every section has the same shape: a question you can't answer yet → a short answer with a picture in your head → what that really is, with the real name and numbers → where the picture lies to you → check yourself, where you predict before you're told.
That fourth part matters most. Anybody can hand you a nice comparison. Almost nobody tells you where it stops being true. Keep the comparison but not its limits, and you've learned a story about the thing, not the thing.
64 microphones outside a stadium
What is actually being measured, and by what?
Picture 64 tiny microphones taped to the outside of a football stadium, thick walls in between. Eighty thousand people yelling inside. You can't hear any one person — just a muffled roar, louder on some sides. Your job: work out which end just scored, from the roar alone. That is EEG. The "microphones" are metal discs on a scalp, the "crowd" is millions of brain cells, and the "wall" is a skull.
EEG means electro-encephalo-graphy — "electricity, brain, writing." Brain cells talk in tiny electrical pulses. One cell is far too faint to detect through a skull, but when millions near each other pulse in time, their voltages add up into something readable at the skin. The data here is real and public: the PhysioNet EEG Motor Movement/Imagery Database.
| People | 109 |
| Sensors on each head | 64 |
| Tries ("trials") per person | 45 for most |
| The task | imagine squeezing one fist |
| The two answers | left fist · right fist |
Two honest details. Not everyone had 45 — 96 of the 109 did; the rest have between 36 and 57. And each person gets their own model, not one shared across all 109. Brains differ enormously — skull thickness, fold shape, exactly where the sensors landed — so a model trained on you would do badly on your friend.
Microphones hear air pressure; electrodes read voltage — different physics, nothing here is sound. You can never "zoom in" on one person in the crowd: the blur through the skull is not a flaw to fix later, it's the situation. Every sensor hears a mixture of everything at once — and that is the reason the very next stage of the program exists.
Throw away what can't be brain
Can we feed the raw recording straight into the program?
No — for the same reason you'd clean a recording before finding the melody. There's rumble at the bottom and hiss at the top, and neither is music. Four steps, in this exact order:
- Keep only 1–40 Hz. Below 1 Hz is slow drift (sweat, the gel drying); above 40 is mostly building hum and muscle noise. Keeping the middle is a band-pass filter, built so it doesn't shift the wiggles in time.
- Make every recording the same speed. Most people were recorded at 160 measurements per second; three (subjects 88, 92, 100) at 128, and nobody wrote it on the box. All three get resampled up to 160 to match.
- Cut it into 4-second pieces. 4 s × 160 = 640 numbers per sensor, per trial.
- Put every sensor on the same scale (z-scoring) — using training trials only to work out each sensor's typical value, so the test never leaks backwards into the setup.
"Cleaning" makes it sound like dirt vs. music. It isn't that tidy. There is real brain activity above 40 Hz — we drop it because it's outnumbered by noise up there. That's a bet, not a cleanup: whatever we lose above 40 Hz costs us less than the noise would. Hold onto that — in Module 11 the bet turns out to matter a lot.
40,960 numbers hiding one bit
How much data is one trial, really?
Forty thousand numbers in. One yes-or-no out. Everything the program does from here is the work of getting from one to the other, and every stage throws things away. That's not the program being lazy — that is the job.
Calling it a "picture" — 64 lines on a screen — makes it feel like there's something to see if you look hard enough. There isn't. The layout is a choice made so humans have something to look at. To the program it's a grid of numbers with no top, bottom, left, or right.
Send one trial through, stage by stage
Here is the whole machine as one interactive. Pick which fist the person is imagining, press New trial for a fresh signal, and click each stage to watch 40,960 numbers become 8 tracks, become 304 loudness values, become one decision. The next five sections then take each stage apart.
This runs the real operations live in your browser on a representative signal — the same math the sections below describe. The reported study accuracies come later, and they're the real ones.
Eight sensors that don't exist
64 sensors, all hearing blurry mixtures of each other. What do you do with that?
Think of a music mixing board: 64 microphones plugged in, 8 sliders on the front. Each slider blends all 64 at once into one new track — a lot of mic 12, a little of mic 30, negative mic 7. You end up with 8 tracks, not by picking favourites but by recombining everything eight different ways. And negative mic 7 is the clever part: subtracting one sensor from another cancels whatever they both heard, so you can build a sensor that doesn't exist and delete shared noise with it.
| Inputs | 64 sensors |
| Outputs | 8 virtual channels |
| Numbers to learn | 8 × 64 = 512 |
Those 512 are the first chunk of the model's 1,338. This is not new. Brain researchers have built spatial filters by hand since the 1990s — the best-known is CSP (Common Spatial Patterns), which solves an equation directly. This model does the same kind of thing, but finds its numbers by trial and error. We'll race the two in Module 10.
A human sets a mixing board's sliders on purpose. Nobody set these — they started random and got nudged toward whatever made the answers come out right. Nobody decided what the eight tracks would mean, and nobody knows until they go and look. Also: "64 down to 8" sounds like the win is less data. It isn't — that's a side effect. The win is that the 8 tracks are meant to be less redundant than the 64 sensors were.
Eight equalizers, one per track
We have 8 tracks, each still 640 numbers wiggling through time. Now what?
Each track gets its own equalizer — the bass-and-treble control on a speaker. Turn the bass up, slow rumbles get louder; turn the treble down, fast hiss fades. Eight tracks, eight separate equalizers, each deciding which speeds of wiggle to keep.
Mechanically, each filter slides a window of 25 measurements along its track. At 160 per second that's 156 milliseconds — about a sixth of a second. Is that sensible? The brain's mu rhythm over the motor areas wiggles 8–13 times a second, so one full wiggle takes 77–125 ms. 156 ms is wide enough to fit a whole one inside; a 40 ms window would only ever see a fragment.
| Filters | 8, one per track |
| Window | 25 samples = 156 ms |
| Numbers to learn | 8 × 25 = 200 |
| Running total | 512 + 200 = 712 |
Why keep the eight filters separate instead of mixing them? It's cheaper (200 numbers, not 1,600), and each one can be pulled out and plotted on its own to see what it kept. Keeping things separable so you can inspect them later is a design choice — this project made it on purpose.
Conv2d(8, 8, kernel_size=(1,25), groups=8, bias=False). groups=8 is what makes it depthwise. It's followed by BatchNorm2d(8) — 16 more numbers (a scale and shift per track). Running total 728.A real equalizer is labelled — "bass," "mid," "treble" — by someone. These eight have no labels; nobody picked their frequencies, so you have to plot one to find out. And an equalizer only turns things up and down, while these can also shift when things appear. "Equalizer" is the easy case, not the whole story.
Where the picture becomes a number
It's also the one that makes everything after it easy. Read it slowly. Three small operations, and by the end the data has stopped being a picture and become a list of quantities.
We still have 8 tracks of 640 wiggles. But the answer is one word: left or right. How do you get from a wiggle to a word?
You stop asking "what shape is this?" and start asking "how much is going on here?" Three steps do it: square (throw away direction, keep size), average (over about half a second, ask how busy it was), log (squash the range so a straight-line rule can work). Together, they measure loudness.
Do it by hand
Take six numbers off one track and run them through:
Notice what squaring did: the −5 became the biggest number in the set. Loud moments now dominate quiet ones — that's the reweighting, not just a sign flip.
The three together — square, average, log — are a standard, very old recipe called band power estimation: how much energy is in this signal, in this frequency range, over this stretch of time? The surprise: the 1990s method, CSP, computes the exact same quantity. This is not a neural network doing something mysterious and new — it's a network rediscovering, by trial and error, what engineers worked out by hand thirty years ago.
| Square window (pool) | 75 samples = 469 ms |
| Step (hop) | 15 samples = 94 ms |
| Windows that fit in 640 | (640−75)/15 + 1 = 38 |
| Per track → all 8 tracks | 8 × 38 = 304 |
That number — 304 — is load-bearing. It shows up in the diagram, the narration, and the final stage. Change the window, the step, or the clip length and 304 becomes something else and every explanation built on it goes wrong. The project's export script refuses to run if the number isn't 304.
Squaring is not "making everything positive." It also makes big things enormously bigger — a 10 becomes 100. That's a deliberate trade. Averaging destroys when inside the window something happened — permanently, not compressed. And the log adds no information at all; it only reshapes the numbers so the next step can be a straight line through them.
log(clamp(x, min=1e-6)) — clamping stops the log of exactly zero (negative infinity) from poisoning training. An alternative 3.0-second window was tested across all 109 people and was not significantly better (p = 0.29), so 4.0 seconds and n = 304 is an evidenced choice, not a convenience.After all that, the answer is addition
304 numbers go in. One word comes out. How?
Imagine 304 judges. Each looked at one thing — one track, during one half-second — and has an opinion strength for "left" and for "right." Add up all 304 left-votes, add up all 304 right-votes, higher total wins. That's the last stage: after all that machinery, the final decision is addition.
| Stage | What it does | Numbers |
|---|---|---|
| Spatial filter | 64 sensors → 8 tracks | 512 |
| Rhythm filter | 8 equalizers | 200 |
| Normalizer | keeps values in range | 16 |
| Square / average / log | wiggles → 304 loudness values | 0 |
| The vote | 304 → 2 (then percentages) | 610 |
| Total | 1,338 |
Look at the zero. The energy stage — the hardest one to understand — has nothing to learn. Squaring is squaring; it never trains. The stage that reorganizes the entire problem contains not a single learned number. For scale: the models behind your phone's apps have millions to billions. This one is deliberately tiny — small enough to hold in your head at once.
The two scores come out as bare numbers, then softmax turns them into two percentages that add to 100 — so the model says something like "86% left." Do not read that as "right 86% of the time." It's a number the model makes about its own answer, and it can be confidently wrong. Module 12 shows one.
Dropout(0.5) randomly silences half the 304 values each pass — like studying with random pages torn out. It's switched off when the model is used, and the code raises an error rather than exporting anything computed in training mode.The judges don't confer. Real judges argue and change each other's minds. These 304 contributions are added up independently — nothing interacts. That's exactly what "linear" means, and it's why the final step is so simple: all the clever combining already happened upstream.
The exclusions, printed on the box
Every comparison on this page is a tool with a warranty — and every warranty has exclusions. Here they all are in one place. Flip each card to see exactly where the nice picture stops being true. If you remember only the picture and not its limit, you've learned a story about the model, not the model.
Guess, get corrected, adjust, repeat
Nobody programmed those 1,338 numbers by hand. So where did they come from?
They started as random noise. The model saw a trial, guessed, was told the answer, and every number got nudged a little toward whatever would've made the guess better. Then again — about four hundred times. For one person, subject S032, the 45 trials split three ways: 27 to learn from (homework you can redo), 9 to check progress (a practice quiz), and 9 kept for the final score (the real exam, looked at once).
Homework error
lower = memorizing the 27Practice-quiz score
the only honest signal> not >=), which means the least-overfitted model at that score. A tiny scoring set makes ties the normal case, and how you break them quietly becomes part of your method.The same model scored 33% and 80%
Tested on 9 new trials, the model got 3 right — 33%, worse than guessing. So it failed. Right?
Hold on. The same model, measured a different way, gets 80% — 36 of its 45 trials right. Both numbers are correct. Working out how is the most useful thing on this page — and it has almost nothing to do with brains.
The free-throw problem. Someone shoots 9 free throws and makes 3. Are they a 33% shooter? You have no idea — one lucky bounce is 4/9, an eleven-point jump. Nine shots can't tell a bad shooter from a good one having a bad afternoon. Nine trials is nine free throws. That's the whole problem. Statisticians make it precise with a confidence interval; for 9 trials it's about 60 points wide — so wide it contains "coin flip" almost no matter what you scored.
The fix: give everyone a turn being the exam. Split the 45 trials into 5 groups of 9. Train a fresh model on four groups, test on the fifth. Rotate until every group has been the test. Now all 45 trials have an honest prediction from a model that never saw them — 45 instead of 9. This is cross-validation, and each round redoes its own scaling so nothing leaks.
S032 — same model, two ways of measuring
The mirror image. Another subject, S003, shows the same trap the other way: a single 9-trial test says 66.7% — looks good! — while the honest 45-trial number is 40.0%, below chance. If you'd only run the 9-trial test, you'd have reported a success that isn't there. A 9-trial score can flatter a bad model or bury a good one, and you can't tell which from the number alone.
S032's 45 trials are 21 left and 24 right, so a "model" that always answers "right" scores 24/45 = 53.3%. That's the floor. Anything at or under it is worse than a program with no brain at all. Now 80% means something. An accuracy number without a baseline is not a result.
One person is not the story
S032 gets 80%. Does that mean this works?
For S032, yes. For people in general, no. S032 is the best of 109. Every person got the full treatment — their own model, five rounds of cross-validation — which is 2,180 training runs in all, about four minutes on one desktop. Here's the whole cohort:
All 109 people, cross-validated
The old recipe wins
If 109 people each flip 9 coins, somebody gets a suspiciously good run — not because they're special, but because there were 109 of them. So when 21 people pass the usual test, you have to raise the bar for having taken 109 shots (a Bonferroni correction). After that, only 4 survive: S032, S050, S053, S054. That's also the honest defence of picking S032 — its result is strong enough to survive correcting for the 109 tries.
"25% at or below chance" can sound like those people's brains are faulty. They aren't. The signal may well be there — this pipeline, with these sensors and this much data, just can't find it. "The model couldn't read it" and "there was nothing to read" are different claims, and only the first has been shown.
Was building the network a waste, then? No — but be precise about why. Its job here was to be explainable, stage by stage, which is what this whole page is for. The classical method is more accurate; this one is inspectable. Different goals — and it's only honest if you say which you were optimizing.
It wasn't reading the motor areas
S032's model is right 80% of the time. Something real is being detected. What? The obvious guess is the motor areas — imagining a left hand changes activity over the right motor cortex. That's what the task is for. So let's check.
It isn't looking at the motor areas. Its attention leans toward the back of the head (vision) and the sides (jaw muscles). That is not a bug — it's the most interesting thing in the whole project.
First, a trap. The obvious move is to read the biggest spatial-filter numbers and call that "where it's looking." That fails. Think of noise-cancelling headphones: to cancel an engine's hum they turn the engine microphone way up — a big number — precisely so its contents can be subtracted away. A big weight can mean "important" or "subtract this," and the number looks identical. So you compute a second thing that works backwards to ask where a real source would show up — called a pattern (vs. a filter), made precise by Haufe and colleagues in 2014. Patterns can be read as locations; filters cannot.
Where the pattern points
back & sides, not motorShare of attention by region
Second, take the motor band away. If the model used motor rhythms, feeding it only the 8–30 Hz motor band should keep it working — or help. Instead it got worse:
Restrict to the motor band → accuracy drops
| Measurement | What it found |
|---|---|
| Where the patterns point | no motor preference |
| Removing all but the motor band | accuracy drops significantly |
| Racing a motor-band method against it | the motor-band method wins |
Three different methods, asking three different ways, agreeing. That is what a convincing scientific argument looks like — not one measurement shouting, but several unrelated ones quietly pointing the same way.
There's a famous image classifier that told huskies from wolves with high accuracy — and turned out to be detecting snow in the background, because the wolf photos had snow. It wasn't broken and it wasn't cheating. It found a real, reliable pattern that predicted the label — just not the one anyone intended. This EEG model leans on the back of the head and jaw the same way. That's not a rare failure; it's the normal behaviour of a system that optimizes for being right and has no concept of why.
"Snow instead of wolves" makes it sound like someone messed up. Nobody did — the data, training, and accuracy are all real. You can do everything right and still measure something other than what you named; the only defence is checking. And to be honest about our own claim: the evidence it wasn't motor is solid and threefold, but "it leans occipital" is a hint — the confirming test (retraining hundreds of times on scrambled labels) wasn't run, so that lean carries no p-value. The solid claim is the negative one.
Fpz, a frontal blink/eye-movement detector — not a motor site, and never to be described as one. The pattern math is A = ΣX WT ΣS−1, computed with a pseudo-inverse.86% sure, and wrong
The model shows a percentage. Should you trust it?
Meet trial_16.
| The person was imagining | right fist |
| The model said | left |
| How sure it was | 86.4% |
| Had it seen this trial before? | no — completely new |
Not "kind of leaning left." Not a 51-to-49 near-tie. 86% sure, and wrong. Softmax percentages come from comparing two scores: if left comfortably beats right, out comes a big number. Nothing in training ever made those percentages honest — the model was rewarded for being right, never for being appropriately unsure. So the number describes the size of an internal gap, and humans read it as a probability of being correct. Different things in the same costume.
You've met this. Autocorrect changes a word you spelled correctly, with total commitment. Face ID confidently refuses to recognize you. The system isn't malfunctioning — it's applying its rule, its rule is wrong here, and it has no mechanism for doubt.
"The model was fooled by this trial" is the tempting story, and it's an overread. A model with 1,338 numbers trained on 27 trials has no reason to produce trustworthy percentages in the first place. The lesson is "a confidence score is not a probability," not "something strange happened in this brain."
Say the whole thing back
The real test: explain to someone who's read none of this — what EEG measures and why every sensor hears a mixture; why 64 sensors get mixed to 8; what square-average-log accomplishes and what it destroys; why 40,960 numbers become 304 become 2; why 9 trials can't tell you anything; why 80% is meaningless without 53.3%; and how you find out whether a model looks where it claims. Get stuck on one? That's the section to reread. Getting stuck is information.
| Stage | Plain version | Numbers |
|---|---|---|
| 1 | 64 sensors, 4 seconds, 40,960 measurements | — |
| 2 | blend 64 sensors into 8 invented ones | 512 |
| 3 | give each an equalizer, 156 ms wide | 200 + 16 |
| 4 | measure loudness: square, average, log | 0 |
| 5 | 304 weighted votes → 2 scores → percentages | 610 |
| Total | 1,338 |
| Result | Value | Read it with |
|---|---|---|
| Best person (S032), cross-validated | 80.0% (36/45) | a 53.3% floor, range 65.4–90.4% |
| Same model, one 9-trial test | 33.3% | 9 trials means nothing |
| Typical person, all 109 | 53.3% | that's essentially the floor |
| At or below chance | 27 of 109 | matches published BCI research |
| Survive correction for 109 tries | 4 of 109 | S032, S050, S053, S054 |
| 1990s method (CSP+LDA) | 57.8% | it wins, p = 0.011 |
| Attention on motor areas | 0.92× | it isn't reading motor cortex |
| Motor band only (8–30 Hz) | −4.4 pts | removing "the signal" helps nothing |
1. Every stage of a model throws something away — understanding it means knowing what each stage discarded and what it bought. 2. An accuracy number alone is not a result; it needs a baseline, a sample size, and a range. 3. Small test sets lie in both directions — 33.3% and 80.0% came from the same model; so did 66.7% and 40.0%, the other way. 4. Try enough times and something looks impressive — 109 people, 21 apparent winners, 4 real ones. 5. A model finds what predicts the answer, not what you meant — snow instead of wolves, jaw muscles instead of motor cortex. Checking is not optional.
45 trials per person is few (even the careful measurement carries ±14 points). The classical method beats the network. "It leans occipital" has no p-value — the solid claim is the negative one. S032 was picked as the best of 109 (its result survives correcting for that). One design, one seed, no tuning search, one person at a time. And no eye-movement or muscle cleanup was done — so given Module 11, muscle contamination is a live explanation for part of what was decoded. None of these make the work wrong. They make it measured — which is the difference between a result and a claim.