A two-phase reproduction of the SJTU SEED emotion experiment (Zheng & Lu, 2015). Phase 1 — collection: stream EEG from an OpenBCI Cyton and cardiac data from an EmotiBit through a local bridge — optionally, on a local desktop or edge device NVIDIA Jetson Orin Nano — add webcam eye and hand tracking (WebGazer + MediaPipe), and let an LLM lab assistant run the SEED stimulus protocol and collect the data. Phase 2 — analysis & prediction: pre-train on SEED data and decode emotion from the recording with the models , right here — inference runs in this browser (onnxruntime-web) or through AWS Sagemaker endpoints. Source code is on GitHub.
A short video tour of this page — the study and models behind it, followed by a live run: sensors on, protocol paced by Pythia, data collected.
WebGazer gaze + MediaPipe hand keypoints, fully local, logged alongside the EEG. Calibrate trains on nine dots; Validate measures accuracy in degrees of visual angle using the screen and viewing distances below.
off
hands: off
EmotiBit over Wi-Fi via BrainFlow — auxiliary = cardiac (PPG), ancillary = EDA + temperature. Every sensor is graded live for signal quality.
bridge required · HR —
8-channel Cyton (or Daisy for 16), mapped by 10-20 name into the 62-channel SEED montage. Each channel is graded live on a rolling 10-second window — hover any tile for its numbers.
bridge required
Collection phase, following SEED's session structure: each trial is a 5 s hint, an emotion-eliciting film clip, then self-assessment + rest — 15 trials in the full protocol. SEED's own clips are licensed, so the built-in library is cut from openly licensed and public-domain films into 15 four-minute clips, five per valence, with no in-session repeats — or switch stimuli to self-guided. All enabled streams record throughout, to local disk via the bridge or to S3.
Recording is gated on signal quality: a railed, flat, drifting or noisy
channel holds the session until it is fixed or you accept it —
accepting clears the hold for good, and the take is marked accepted
rather than clean. While a session runs, a quality snapshot is written
into the recording every 30 seconds, so an electrode that lifts during
trial 7 costs you trial 7 rather than the whole session. Start the
bridge with --lsl to mirror every stream and marker onto
Lab Streaming Layer, so a LabRecorder can capture it all in one XDF.
Pythia checks the sensors, paces every trial, collects self-reports, and archives the synchronized recording. Start Session opens her automatically and asks you to share Entire Screen, recorded alongside the sensor streams.
With lab ticked (on by default) she watches signal quality and speaks up about anything outstanding, naming the electrode and the fix. Turn reminders off from a reminder or under ⚙ and she stays quiet for good; accepting the flagged quality below also retires its reminders.
Session controls — Pythia uses these same settings.
clip library: loading…
desktop capture: select Entire Screen when the session starts
bridge-saved sessions are training data:
python -m eeg_lab.finetune continues the SEED checkpoint
on them; --promote --export-web redeploys the
personalized weights.
needs: EEG connected + local stimuli + desktop capture
accepted — quality no longer blocks the session or triggers reminders; every issue is still measured and recorded.
Analysis phase: the decoders were chosen in a nine-arm model ablation on
SJTU SEED —
statistical representations (differential entropy + LDA, MiniRocket), two
foundation models (NormWear2, LaBraM) across three transfer modes, and a
from-scratch CNN+LSTM — all on the identical trial-grouped split
(eeg_lab/seed_iv_inverted.ipynb; code deep-dive on the
model page).
NormWear2 arms use a 17-channel montage with per-window z-normalization (its channel-fusion attention was pretrained on 2–6 channel inputs); all other arms see the full 62 channels. Caveat printed next to the numbers in the notebook, too.
The 15.7M-parameter CNN+LSTM trained from random init hit 90.3% — 9 points above the best foundation-model arm and 32 above the best that the 187M-parameter NormWear2 could manage. None of NormWear2's three transfer modes cleared the humble LDA baseline.
LaBraM is 32× smaller than NormWear2 but was pretrained on ~2,500 h of scalp EEG at 200 Hz with 1 s patches — SEED's exact regime. Same harness, same loaders, same LoRA config: 81.3% vs 58.2%. What the encoder saw in pretraining matters more than how big it is.
In both families LoRA outran progressive unfreezing (81.3 vs 66.0; 58.2 vs 50.7), and frozen linear probes came last — NormWear2's frozen probe landed below the statistical baseline. Thawing more blocks visibly plateaued: LaBraM went 63.6 → 66.0 → 65.9 as blocks unfroze.
The winning arms are exported by eeg_lab.models and run
here: emotion inference over 1-second windows, exactly like the notebook —
resample to 200 Hz, map into the SEED montage, classify. Runs live while
streaming and over each recorded stimulus segment after a session. Pick where
it runs:
connect EEG (or the synthetic board) for a live read-out
hint
How did that clip actually make you feel?
Look at each dot and click it until it turns gold (3 clicks each). Keep your head reasonably still.