Neurodecode provides a real-time brain signal decoding framework with a modular software design. Its decoding performance was recognised at the Microsoft Brain Signal Decoding competition with the First Prize Award (2016) for high decoding accuracy — 2nd out of 1863 algorithms.
It has been applied to online decoding projects across a wide range of electrode types — EEG, ECoG, DBS, and microelectrode arrays — and acquisition systems, including AntNeuro eego, g.tec gUSBamp, BioSemi ActiveTwo, BrainProducts actiCHamp, and Wearable Sensing.
Decoding runs at roughly 15 classifications per second (cps) on a 4th-gen i7 laptop with a 64-channel setup at 512 Hz. High-speed decoding of up to 200 cps has been achieved using a process-interleaving technique on 8 cores. Neurodecode is tested on both Linux and Windows with Python 3.8+.
The underlying data communication is built on the Lab Streaming Layer (LSL), which provides sub-millisecond time-synchronization accuracy. Any signal-acquisition system supported natively by LSL or by OpenVibe is supported by Neurodecode. Because the transport is TCP-based, signals can also be transmitted wirelessly.
| Module | Description |
|---|---|
| StreamReceiver | Base module for acquiring signals, used by Decoder, StreamViewer, and StreamRecorder. |
| StreamViewer | Visualize signals in real time with spectral filtering, common-average filtering, and real-time FFT. |
| StreamRecorder | Record signals into fif format, the standard used by the MNE EEG analysis library. |
| StreamPlayer | Replay recorded signals in real time, as if streamed from a live acquisition server. |
| Decoder | Decoder and trainer modules. Supports LDA, regularized LDA, Random Forests, and Gradient Boosting out of the box; custom (e.g. neural-network) decoders can be added. |
| Protocols | Basic protocols for training and testing. Google Glass visual feedback is supported over USB. |
| Triggers | Mark event (stimulus) timings during recording. Includes common trigger event-definition files. |
| Utils | Various utilities. |
- Python 3.8+ (Anaconda is recommended for an easy environment setup)
- numpy, scipy, scikit-learn
- MNE 1.10+
- matplotlib, pyqtgraph, PyQt5
- pylsl
- opencv-python, pyserial, psutil
- xgboost, lightgbm
All of these are installed automatically with the package (see below).
Optional but strongly recommended: OpenVibe. OpenVibe supports a wide range of acquisition servers, all of which become available to Neurodecode through LSL. When you run the acquisition server, tick "LSL_EnableLSLOutput" in Preferences so the data is streamed over the LSL network for Neurodecode to receive.
Create a fresh virtual environment (Python 3.8 or higher):
conda create --name nd python=3.10
conda activate ndInstall the latest release from PyPI:
pip install neurodecodeOr install the development version from source:
git clone https://github.com/dbdq/neurodecode.git
cd neurodecode
pip install --editable .Add the scripts directory to your PATH for convenient access to the commonly used command-line tools.
Troubleshooting: PyQt version conflicts
The Qt API is sensitive to versioning and must be compatible with all dependencies. If pyqtgraph complains about an incompatible PyQt version (e.g. PyQt < 5.12), try:
conda remove pyqt
pip install -U PyQt5This is usually caused by Anaconda not shipping the latest PyQt version.
Windows: increase timer resolution
The default timer resolution on some Windows versions is 16 ms, which limits timing precision. Run the following tool and set the resolution to 1 ms or lower: Windows System Timer Tool
Hardware triggering without a legacy parallel port
We developed an Arduino-based triggering system to send triggers to a parallel port over standard USB, achieving sub-millisecond extra latency compared to a physical parallel port (150 ± 25 µs). Oscilloscope results are in the doc folder.
git clone https://github.com/dbdq/arduino-trigger.gitThe customized firmware should be flashed to an Arduino Micro, and the circuit design in the document folder printed to a circuit board.
g.USBamp users
Use the following customized acquisition server instead of the default LSL app, so the trigger channel is streamed as part of the signal channels:
git clone https://github.com/dbdq/gUSBamp_pycnbi.gitThe default gUSBamp LSL server streams the event channel as a separate server rather than as part of the signal stream; the customized version supports simultaneous signal + event streaming.
AntNeuro eego users
Use the OpenVibe acquisition server and enable "LSL output" in preferences. If "eego" does not appear in the device list, it is likely that the additional drivers were not installed with OpenVibe.
Copy the /sample folder to a local folder and cd into it.
Replay a pre-recorded EEG sample in real time, as if acquiring live signals, with a chunk size of 8. The sample was recorded from a 24-channel EEG system while a participant performed left- and right-hand motor imagery. Hardware events recorded during the experiment are also streamed over LSL.
nd_stream_player mi_left_right.fif 8Setting up an LSL server and streaming the recorded data:
Simulate real-time decoding from the brain. Data is streamed with nd_stream_player (above) while the receiver stays source-agnostic, enabling a full simulation of replay and validation of the processing pipeline. You can skip this step by creating a ./fif/ folder and copying the sample fif file into it.
nd_stream_recorder $PWD # Linux
nd_stream_recorder %CD% # WindowsChoose StreamPlayer from the list. Cursor keys change the amplitude and time scaling.
nd_stream_viewerRuns an offline training protocol. This step is illustrative and can be skipped.
nd_train_mi ./config_offline.pyTrain a decoder from the fif file using defined events — here, left (event 11) vs. right (event 9) hand motor imagery. Events are defined in mi_left_right_events.ini.
nd_trainer ./config_trainer.pyThe sample is set to a 60-second time-out with no early termination, so you can watch the decoder output shift toward left or right as event 11 (left) or event 9 (right) is emitted from the stream player terminal. Other events, such as rest, are undefined and produce random behaviour.
nd_test_mi config_online.pyBar position during the protocol:
Decoder output with probabilities and the corresponding bar position (accumulated probabilities):
Events emitted from the stream player:
There is still plenty of room to optimize speed across the codebase, and contributions are very welcome. For comments or feedback, contact lee.kyuh@gmail.com.
Released under the GNU General Public License.
If any of this code was useful for your project, please consider citing below paper that conceived this project:
- Kyuhwa Lee et al., "A Brain-Controlled Exoskeleton with Cascaded Event-Related Desynchronization Classifiers," Robotics and Autonomous Systems, Elsevier, 2016, p. 15–23.






