Can AI Improve a 20-Year-Old Brain Decoder? A Little, and Not Everywhere
Last Updated on November 2, 2026 by Editorial Team
Author(s): Tianyao Wu
Originally published on Towards AI.
Can AI Improve a 20-Year-Old Brain Decoder? A Little, and Not Everywhere
AI-assisted: this article was drafted with AI assistance and checked against BCI Report’s published results.
Look at a light flickering ten times a second, and your visual cortex starts to oscillate at ten hertz, and at twenty and thirty. This steady-state visual evoked potential (SSVEP) is strong enough to read from a few scalp electrodes, which is why SSVEP spellers work: each key flickers at its own rate, and a decoder names the rate you are looking at.
In 2006, Lin and colleagues showed how to do that with no training at all. Canonical correlation analysis (CCA) writes down sine waves at each candidate frequency and its harmonics, and asks which set the EEG window lines up with best.
Twenty years later, BCI Report trained small neural networks on 32 of a benchmark’s 40 flicker frequencies, then asked them about the 8 they had never seen. They reached at most 41.46% among those eight. CCA, untrained, reached 80.90% on the same windows.
No large model was trained along the way. The learned parts are heads of a few hundred thousand parameters, ridge readouts and one 64-unit MLP; the only foundation-model features, from a frozen CBraMod encoder, appear in experiment 1.
A reader’s comments on the Hugging Face post about that result raised the questions that became fourteen experiments. Was it the network, the input, or the way each frequency was asked? And can learning improve on CCA? The experiments were published on one result page in October 2026. Here is what they found.
TL;DR
- The original kind of head never caught up. Asked a different way, given phase, or swapped for another head (experiments 1–4), it stayed at or below 41.46% on new frequencies, against CCA’s 80.90%. Chance is 12.5%.
- Learning on top of CCA helped a little, and not everywhere. A small learned correction to CCA’s score (about 0.2 million parameters) added +5.24 percentage points (pp) on the main dataset (+3.91 to +6.62), with an unresolved change on a second one (+0.07 pp dry, −0.61 pp wet). A learned head that looked like a +2.07 pp gain over CCA came out nearly level with an untrained sum of the same statistics.
- A training-free variant reached 90.21%. Filter-bank CCA, from 2015, was +9.31 pp above CCA (+6.84 to +11.71; two-second windows, eight electrodes, 70 people). Fixed weighting tweaks moved accuracy a few points either way, and one flipped sign on the second dataset.
The setup in one minute
The data. BETA (Liu et al., 2020) is a public SSVEP benchmark: 70 people and 40 targets flickering from 8.0 to 15.8 Hz in 0.2 Hz steps, recorded outside a shielded room. The experiments use 11,200 two-second windows from eight electrodes over the back of the head.
The split. People are divided into seven folds, so every test person is new to the decoder. Frequencies are divided into five rotations, each with 32 used for training and 8 held out. The main score is accuracy at choosing among the 8 held-out frequencies, where chance is 12.5%. A second score, balanced accuracy among the 32 trained frequencies (chance 3.13%), is a different task. “Unseen” means unseen by the trained part of the decoder.
The second dataset. Five experiments run on Wearable-102 (Zhu et al., 2021), one of them (experiment 7) only there. It has 102 people and 12 targets from 9.25 to 14.75 Hz, recorded with a dry and a wet headband. Models trained on BETA run there unchanged.
Two pieces of jargon.
- Joint CCA is standard CCA: all three harmonics go in one reference set, so they share one combination of electrodes.
- The harmonic sum runs CCA on each harmonic separately and adds the three squared correlations, so each harmonic gets its own combination.
Neither needs training.
How to read the numbers. Each experiment fixed its comparisons before it ran. Intervals are 95% intervals from resampling people and, unless noted, are not adjusted for multiple comparisons. When an interval includes zero, the direction is unresolved, which is not the same as “no effect”.

Each dot is a mean over 70 people with the 95% interval from its own experiment. The numeric-code row is the original study’s control without text; the dashed line marks chance.
Round 1: teach a network what a frequency looks like
Experiments 1 to 4 on the result page.
Asked through a text embedding, the original head reached 28.69% on new frequencies. Asked with a plain numeric code, a control without text, the same FiLM head reached 41.46%. Experiment 1 gave it something more physical: the spectrum of CCA’s own reference signals, sine and cosine at the frequency and its second and third harmonics. It reached 39.00% (36.07% to 41.78%), a paired difference of −2.46 pp from the numeric code (−3.47 to −1.52).
Swapping the head did not clear the bar either. A matching head in place of the FiLM head changed balanced accuracy on trained frequencies by −3.60 pp (−4.37 to −2.73) or +2.54 pp (+1.74 to +3.37), depending on the question, and neither met the protocol’s bar for a meaningful gain. The harmonics did matter: dropping the second and third from the question changed accuracy on new frequencies by −13.27 pp (−15.04 to −11.45).
Phase cut both ways. A complex spectrum keeps phase, and also changes the normalization and the head’s size. Asked with question numbers, it raised balanced accuracy on the 32 trained frequencies by +18.03 pp (+14.13 to +21.77), to 50.08%, still below CCA’s 65.46%. Asked with the reference spectrum, the same head scored 65.56% on eight frequencies in the rotations where they were trained. When the same eight were held out, it scored 8.67%, below chance. BETA gives each target its own stimulus phase, so phase carries label information a head can learn for its training targets. Telling the head each held-out target’s phase lifted it only to 24.97%.
Two last tries scrambled phase during training:
- A random delay of up to 125 ms brought the phase-carrying head down from 24.97% to 12.51% (11.93% to 13.13%), at chance.
- A random phase ramp, with the magnitude question, reached 30.56% (27.81% to 33.22%). That is up from 8.67% without the ramp (+21.88 pp, +18.78 to +25.00), but −8.45 pp (−10.14 to −6.69) against the same head on the plain spectrum, and far from CCA’s 80.90%.
The bet in the comment thread had been that it would clear 39%; it did not.
What round 1 showed: the heads learned something about the frequencies they trained on. Less of it carried to held-out frequencies: at most 41.46%, above the 12.5% chance level but far below CCA’s 80.90%.
Round 2: take the old algorithm apart
Experiment 5.
On the same splits, CCA on the raw two-second window reached 80.90% (76.80% to 85.13%). Tapering the window with a Hann function first, a standard step before taking a spectrum, changed it by −7.95 pp (−9.07 to −6.78).
A training-free match on the spectral coordinates that the complex-spectrum heads read, taken before the heads’ fitted scaling, reached 73.20%. That is +0.24 pp from Hann-tapered CCA (−0.12 to +0.64), unresolved. Simpler scorers that read only each channel’s energy reached 55.21% untrained and 57.45% with a learned ridge.
What round 2 showed: with no training, the coordinates the complex-spectrum heads read supported 73.20%. The learned heads on that kind of input reached at most 30.56%. The page quotes these side by side, with no paired contrast.
Round 3: don’t replace CCA, correct it
Experiments 6 and 7.
The next idea was humbler: keep CCA, and let a learned part only nudge its scores. A bounded learned correction on top of CCA reached 86.15% on new frequencies (82.84% to 89.35%), against 80.90% for CCA alone: +5.24 pp (+3.91 to +6.62). On the trained-frequency task its balanced accuracy was 73.19%, against 65.46% for CCA.
The correction is bounded by design. It can reorder only candidates whose CCA correlations lie within 0.2 of each other, and decisions with a wider margin never changed. It turned 7.39% of held-out decisions from wrong to right, 2.14% from right to wrong and 2.59% from one wrong answer to another: 12.12% changed in all.
Then the same 105 trained models (7 folds × 5 rotations × 3 seeds) ran unchanged on Wearable-102. Paired with CCA on the same windows, using the same three harmonics, they changed accuracy by +0.07 pp on dry electrodes (−0.71 to +0.91) and −0.61 pp on wet (−1.48 to +0.23). Both intervals include zero, and no equivalence margin was set. Wearable-102’s stored amplitude unit is undocumented and the correction reads amplitude spectra, so these are as-stored figures.
Hardware, reference, people, frequency grid and window onset all change between the datasets, so no single cause can be named. The dataset’s documentation also cannot rule out that some people appear in both.
Round 4: a gain an untrained sum also reached
Experiments 8 to 10.
Could a learned scorer do better with richer statistics than channel energy? Experiment 8 gave one shared set of learned weights the cross-spectral matrices between channels, at the fundamental and two harmonics:
- A ridge readout reached 68.03%. That is +10.58 pp over the energy-based ridge (+9.05 to +12.28) and −12.88 pp from CCA (−15.55 to −10.22).
- A cross-spectral MLP reached 72.00%, −8.90 pp from CCA (−11.20 to −6.69). It did not pass its prespecified non-inferiority test.
- Whitening the matrices first brought the ridge to 79.99% (76.20% to 83.79%). With no paired interval against CCA, that shows neither equivalence nor non-inferiority.
These scorers read Hann-tapered, cropped spectral coordinates while CCA reads the raw window, so each contrast against CCA changes the representation as well.
Experiment 9 is the one worth remembering. It summarized each harmonic’s matrix by its eigenvalues, which reproduce CCA’s squared correlation for that harmonic, and trained a ridge head on them. The head reached 82.97% (79.09% to 86.82%), +2.07 pp above CCA (+1.09 to +3.12). That looks like learning on top of CCA working, but the head is handed statistics derived from CCA, so that contrast does not isolate what learning adds.
The same experiment also ran the untrained control: simply summing the same per-harmonic statistics. The sum reached 82.90% (78.90% to 86.93%). The head minus the sum was +0.07 pp (−0.45 to +0.62), unresolved. No equivalence margin was set, so it is not shown to be zero either.
Experiment 10 tried other ways of pooling those statistics:
- A ridge learned on each harmonic’s leading correlation, minus the sum: +0.30 pp (−0.12 to +0.78), unresolved.
- Taking the largest harmonic instead of the sum: −7.72 pp (−9.12 to −6.46).
- On Wearable-102, the eigenvalue head minus the sum was −0.36 pp on dry electrodes (−1.06 to +0.31) and +0.36 pp on wet (−0.24 to +0.94), both unresolved.

Each row is a paired difference in percentage points with its 95% interval. The Wearable-102 rows run the BETA-trained models unchanged.
Round 5: changes that need no training
Experiments 11 to 14.
A filter bank. Filter-bank CCA (Chen et al., 2015) runs CCA on several band-pass-filtered copies of the window, called sub-bands, and adds the results with fixed, decaying weights. BCI Report’s SciPy realization of the published recipe, with seven sub-bands and five harmonics, reached 90.21% (87.65% to 92.62%) with two-second windows and eight electrodes. That is +7.31 pp over the harmonic sum (+5.15 to +9.47), the experiment’s primary contrast, and +9.31 pp over CCA (+6.84 to +11.71). It was above CCA in every window and electrode setting tested.
Shorter windows and fewer electrodes.
- Shorter windows cut accuracy sharply. From two seconds to half a second, filter-bank CCA changed by −57.72 pp with eight electrodes (−59.64 to −55.78), to 32.49%.
- Four electrodes instead of eight, at one second: every method changed by between −1.73 and −5.48 pp, every interval below zero.
- Four electrodes instead of eight, at half a second: three methods had higher means with four electrodes, with no paired interval.
- The learned eigenvalue head across all six settings: it stayed close to the untrained sum. One difference was above zero (+0.87 pp, +0.08 to +1.70), and five intervals include zero.

Shaded bands are 95% intervals.
Where the harmonic sum’s gain arrives. The untrained harmonic sum was +2.00 pp above joint CCA (+1.32 to +2.74), though both read the same three harmonic references. Experiment 12 inserted an intermediate score: the harmonics are scored separately and added, but must share one combination of electrodes.
- Joint CCA to that score: +0.04 pp (−0.10 to +0.19), unresolved.
- That score to the harmonic sum, where each harmonic chooses its own combination: +1.96 pp (+1.06 to +2.96).
Both are 99.44% intervals from a Bonferroni family of nine comparisons. So nearly all of the gain arrives at the step that lets each harmonic have its own spatial filter. The page treats this accounting as descriptive, not as a cause. On Wearable-102 the same step added +1.17 pp on wet electrodes (99.44% interval +0.35 to +2.01) and +0.66 pp on dry, unresolved (−0.25 to +1.58).
Weighting the harmonics. Filter-bank CCA weights its sub-bands with a fixed decaying rule, n^−1.25 + 0.25 for the n-th sub-band. Experiment 13 applied the same rule in two places:
- On filter-bank CCA’s seven sub-bands it raised accuracy from 85.01% with flat weights to 90.21%: +5.21 pp (+4.08 to +6.38).
- On the three harmonics of the harmonic sum it lowered accuracy from 82.90% to 79.91%: −2.99 pp (−3.77 to −2.29). Its prespecified 90% interval (−3.64 to −2.38) lay wholly outside the ±0.5 pp equivalence margin, so the equivalence criterion was not met.
Experiment 14 tried the opposite: rising weights of 1, 1.5 and 1.9 on the three harmonics. It is a coverage heuristic: filter-bank CCA’s sub-bands share one upper edge, so a higher harmonic falls inside more of them.
- On BETA, the rising weights changed accuracy from 82.90% to 83.76% (79.91% to 87.32%): +0.86 pp (+0.29 to +1.45), which meets the experiment’s single prespecified criterion.
- On Wearable-102, the change was −1.14 pp on dry electrodes (−1.86 to −0.46) and −0.43 pp on wet (−1.03 to +0.16), unresolved.
No weight was learned or searched.

Each row is a paired difference against flat weights in the same construction, with its 95% interval. The decay rule on the harmonics changed Wearable-102 accuracy by −1.28 pp dry (−2.05 to −0.54) and −1.36 pp wet (−1.91 to −0.79).
Three things the experiments taught
1. New frequencies were the hard part for the original kind of head. The heads of experiments 1 to 4 learned something about the trained frequencies and paid a large cost on held-out ones. CCA builds in a model of the response at any frequency. The heads had to learn it from 32 trained frequencies, and none reached CCA’s level.
2. On BETA, a bounded learned correction added about five points over CCA; on Wearable-102 every primary learned-versus-untrained contrast was unresolved. The correction changed about one decision in eight, in both directions. The eigenvalue head came out nearly level with an untrained sum of the same statistics.
3. Untrained choices moved accuracy too. Filter-bank CCA is a complete pipeline in which sub-bands, five harmonics and weights change together, and it was +9.31 pp above CCA. Letting each harmonic have its own spatial filter came with about two points on BETA. One fixed weighting rule changed accuracy by +5.21 pp in one place and −2.99 pp in another.
None of this says learning cannot help SSVEP decoding. Supervised spatial templates learned from a person’s own labeled trials were not compared. Each learned part was one fixed recipe. The only foundation-model features tested were frozen CBraMod features, in experiment 1.
What it does say: for frequencies and people the trained part never saw, the learned heads tested here did not replace CCA’s analytic structure. Against CCA alone, the bounded learned correction added +5.24 pp on BETA (+3.91 to +6.62).
If you build an SSVEP decoder
- Keep training-free references in every table. CCA and filter-bank CCA need no training and set a high bar on this task.
- Score new frequencies separately from trained ones. Also score the same frequencies in a split where they were trained, so the cost of generalizing is visible.
- Put every learned part beside its untrained control, and count the decisions it changes in both directions. A net gain of five points can hide two points of right-to-wrong changes.
- Test fixed tweaks on a second dataset before adopting them. Rising harmonic weights helped on BETA and cost accuracy on Wearable-102’s dry headband.
The fine print
- Repeated analyses, not replication. Thirteen experiments reuse the same BETA cohort as the original study, and each was designed with the earlier results known.
- Wearable-102 is a different setting. Its windows start 0.14 seconds after stimulus onset and BETA’s at onset, and its stored amplitude unit is undocumented. Hardware, reference, people and frequency grid all change at once.
- Candidates are given. Every method receives the candidate frequencies at test time. Nothing here supports a 40-way or online spelling claim, and nothing finds an unknown frequency.
- Offline conditions. A window length is not the time from stimulus to decision, and four recorded electrodes are not a four-electrode headset.
- Intervals. They are conditional on the fitted models and fixed seeds. Only experiment 12’s primary family is adjusted for multiplicity.
- One recipe per arm. Filter-bank CCA is a SciPy realization, not a bitwise replication of the authors’ MATLAB code. Each learned arm is one fixed design without a hyperparameter search.
On the result page the experiments appear as cards 1 to 14. The page also lists, card by card, what was not run, its checks and any engineering disclosures.
How this relates to other work
Standard CCA (Lin et al., 2006) and its many variants, most of them calibrated, have been compared systematically (Nakanishi et al., 2015). A survey of the field framed the choice as “to train or not to train” (Zerafa et al., 2018), and Chen et al. (2015) introduced filter-bank CCA with its comparison to standard CCA.
Some methods learn each person’s responses from labeled trials of every target, such as task-related component analysis (Nakanishi et al., 2018). Others transfer what a person’s calibrated targets teach to stimuli they have not calibrated (Wong et al., 2021; Wang et al., 2023; Mai et al., 2025). Here both the people and the frequencies are new to the trained part, and none of those methods was run.
The first article in this thread, Can an EEG Decoder Answer a Question it Was Never Trained on?, covers the head that started it.
Reproduce the numbers
Every number above comes from two public JSON files, pinned here to the commit of the release; no EEG recordings or weights are needed:
import json
from urllib.request import urlopen
commit = "39ebb1e30585cd6f4711e66f2c69913c140bfb28"
base = f"https://raw.githubusercontent.com/twu3202/bci-report/{commit}/site/public/data/"
def load(name):
with urlopen(base + name, timeout=30) as response:
return json.load(response)["studies"]
early, late = load("new-ssvep-frequencies-update.json"), load("ssvep-followups-update.json")
def pct(x): return f"{100 * x:.2f}"
def level(study, key):
row = study["levels"][key]
lo, hi = row["interval"] or (None, None)
return pct(row["estimate"]) + (f"% ({pct(lo)}% to {pct(hi)}%)" if lo is not None else "%")
def diff(study, key):
row = study["contrasts"][key]
lo, hi = row["interval"]
return f'{100 * row["estimate"]:+.2f} pp ({100 * lo:+.2f} to {100 * hi:+.2f})'
print("CCA, held-out:", level(early["cca_diagnostics"], "CCA-RAW/u8"))
print("CCA + learned correction:", diff(early["cca_adapter"], "RAW-RESIDUAL minus CCA-RAW/u8"))
for sensor in ("dry", "wet"):
print(" Wearable-102", sensor, diff(early["wearable_transfer"], f"RAW-RESIDUAL_minus_CCA-RAW/{sensor}"))
print("Eigenvalue head - harmonic sum:", diff(late["coherence_eigen"], "EIG-RAW minus RAW-HARM-SUM/u8"))
print("Filter-bank CCA:", level(late["practical_settings"], "w500-c8/FBCCA-M3-N7-H5/u8"))
print(" minus CCA:", diff(late["practical_settings"], "w500-c8/FBCCA-M3-N7-H5 minus w500-c8/CCA-RAW-H3/u8"))
print("Separate spatial filters:", diff(late["harmonic_gap"], "S minus B/u8"))
print("Rising weights, BETA:", diff(late["rising_weights"], "literal_rise minus flat/u8"))
print("Rising weights, dry:", diff(late["rising_weights"], "literal_rise minus flat/dry"))
The card 12 contrast prints its 99.44% family interval. The result page, the data API and the Hugging Face dataset carry every level, contrast and check count.
Sources and licenses
BETA was created by Bingchuan Liu and colleagues (Figshare, CC BY 4.0); its participant consent described use for non-commercial scientific research. The stimulus phases in cards 2 and 3 come from the BETA authors’ corrigendum. Wearable-102 is by Zhu F, Jiang L, Dong G, Gao X and Wang Y (Figshare version 4, CC BY 4.0); its paper describes consent for noncommercial scientific research. CBraMod’s code is MIT and its weights Apache-2.0. Filter-bank CCA follows Chen et al. (2015); no code is distributed. BCI Report’s aggregate measurements are CC BY 4.0. The dataset and model authors do not endorse these evaluations. All figures were rendered from BCI Report’s published aggregates; no EEG recordings, individual scores or trained weights accompany this article.
To cite these results: BCI Report (2026), doi:10.5281/zenodo.23123296, together with the upstream datasets. The release used here is open-register-update-20261028 (doi:10.5281/zenodo.23735101).
References
- Lin Z, Zhang C, Wu W, Gao X. Frequency Recognition Based on Canonical Correlation Analysis for SSVEP-Based BCIs. IEEE Transactions on Biomedical Engineering 53(12), 2610–2614 (2006). doi:10.1109/TBME.2006.886577
- Chen X, Wang Y, Gao S, Jung T-P, Gao X. Filter bank canonical correlation analysis for implementing a high-speed SSVEP-based brain–computer interface. Journal of Neural Engineering 12(4), 046008 (2015). doi:10.1088/1741–2560/12/4/046008
- Liu B, Huang X, Wang Y, Chen X, Gao X. BETA: A Large Benchmark Database Toward SSVEP-BCI Application. Frontiers in Neuroscience 14, 627 (2020). doi:10.3389/fnins.2020.00627
- Zhu F, Jiang L, Dong G, Gao X, Wang Y. An Open Dataset for Wearable SSVEP-Based Brain-Computer Interfaces. Sensors 21(4), 1256 (2021). doi:10.3390/s21041256
- Nakanishi M, Wang Y, Wang Y-T, Jung T-P. A Comparison Study of Canonical Correlation Analysis Based Methods for Detecting Steady-State Visual Evoked Potentials. PLOS ONE 10(10), e0140703 (2015). doi:10.1371/journal.pone.0140703
- Zerafa R, Camilleri T, Falzon O, Camilleri KP. To train or not to train? A survey on training of feature extraction methods for SSVEP-based BCIs. Journal of Neural Engineering 15(5), 051001 (2018). doi:10.1088/1741–2552/aaca6e
- Nakanishi M, Wang Y, Chen X, Wang Y-T, Gao X, Jung T-P. Enhancing Detection of SSVEPs for a High-Speed Brain Speller Using Task-Related Component Analysis. IEEE Transactions on Biomedical Engineering 65(1), 104–112 (2018). doi:10.1109/TBME.2017.2694818
- Wong CM, Wang Z, Rosa AC, et al. Transferring Subject-Specific Knowledge Across Stimulus Frequencies in SSVEP-Based BCIs. IEEE Transactions on Automation Science and Engineering 18(2), 552–563 (2021). doi:10.1109/TASE.2021.3054741
- Wang Z, Wong CM, Rosa AC, et al. Stimulus-Stimulus Transfer Based on Time-Frequency-Joint Representation in SSVEP-Based BCIs. IEEE Transactions on Biomedical Engineering 70(2), 603–615 (2023). doi:10.1109/TBME.2022.3198639
- Mai X, Meng J, Ding Y, et al. SRRNet: Unseen SSVEP Response Regression From Stimulus for Cross-Stimulus Transfer in SSVEP-BCIs. IEEE Transactions on Neural Systems and Rehabilitation Engineering 33, 1460–1472 (2025). doi:10.1109/TNSRE.2025.3560434
- Wang J, Zhao S, Luo Z, et al. CBraMod: A Criss-Cross Brain Foundation Model for EEG Decoding. ICLR (2025). arXiv:2412.07236
- BCI Report. What lets a decoder answer an SSVEP frequency it never trained on? (October 20 and 28, 2026). bci.report/topics/new-ssvep-frequencies; cite as BCI Report (2026), doi:10.5281/zenodo.23123296
- BCI Report. Can an EEG model answer questions asked in words? (October 8, 2026). bci.report/topics/questions-in-language
BCI Report, which produced these evaluations, is a personal, noncommercial project; this article uses only its published aggregate results.
Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.
Published via Towards AI
Towards AI Academy
We Build Enterprise-Grade AI. We'll Teach You to Master It Too.
15 engineers. 100,000+ students. Towards AI Academy teaches what actually survives production.
Start free — no commitment:
→ AI Engineering For Production — our new book, coming before the end of 2026. Join the waitlist.
→ Agents Architecture Cheatsheet — 3 years of architecture decisions in 6 pages
Our courses:
→ AI Engineering Certification — 90+ lessons from project selection to deployed product. The most comprehensive practical LLM course out there.
→ Agent Engineering Course — Hands on with production agent architectures, memory, routing, and eval frameworks — built from real enterprise engagements.
→ AI for Work — Understand, evaluate, and apply AI for complex work tasks.
Note: Article content contains the views of the contributing authors and not Towards AI.