# muse-lsl: streaming EEG from a Muse headset over Lab Streaming Layer

> muse-lsl is a Python package and command line tool that connects to Muse 2, Muse S and the 2016 Muse headsets over Bluetooth and publishes their signals as Lab Streaming Layer streams. It fits researchers and developers who want EEG data in a format other tools can subscribe to, and it depends on a lab streaming layer installation on the host machine.

**alexandrebarachant/muse-lsl** — Python script to stream EEG data from the muse 2016 headset

- Repository: https://github.com/alexandrebarachant/muse-lsl
- Stars: 783 · Forks: 207
- Language: Python
- License: BSD-3-Clause
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/alexandrebarachant-muse-lsl

## A bridge between Bluetooth headsets and a network time synchronised stream

muse-lsl does one thing: it takes the EEG signal coming off a Muse headset and republishes it as a Lab Streaming Layer stream that any other program on the network can subscribe to. The README describes the package as a tool for streaming, visualizing and recording EEG data from the Muse devices developed by InteraXon, and the repository description calls it a Python script to stream EEG data from the Muse 2016 headset.

The audience is a research or prototyping team with the hardware already on the desk. LSL itself is described in the README as a system designed to unify the collection of time series data for research experiments, which has become standard in the field of EEG-based brain-computer interfaces because it makes separate streams of data available on a network with time synchronization and near real-time access. Those two properties are the reason to adopt this approach rather than reading a headset over a serial cable: a stimulus presentation script and a recording program on two machines can share a clock.

The package is licensed BSD-3-Clause, which is permissive, allows commercial use and modification, and requires only that the copyright notice and licence text travel with copies of the code. A DOI badge points at a Zenodo record, so the releases are citable in a paper.

## Bleak is the default backend, with BlueMuse and bgapi as platform fallbacks

Getting a headset onto the machine is where most of the friction lives, and the README is explicit that the code relies on a number of different Bluetooth backends. The recommendation is the bleak backend, which is enabled by default. The setup.py file lists bleak with a version floor of 0.18.0, and the release list shows v2.5.1 was cut to fix a bleak double-subscribe error, which is a good indication that the default path is the one the project spends its attention on.

Two alternatives are named for specific situations. BlueMuse is a GUI that discovers and connects to Muse devices on Windows, and the README recommends it for setting up an LSL stream on Windows 10. bgapi is the option for a Mac with a BLED112 dongle, and the Common Issues section adds that the --interface argument may be needed to supply the right COM port value for the dongle.

That spread of backends is the real constraint of the project. A researcher moving between a Windows laptop, a Mac and a Linux workstation is not running the same acquisition path, and the troubleshooting notes are correspondingly per platform. The three headsets named as compatible are Muse 2, Muse S, and the classic Muse from 2016.

## pipx for the command line, a virtual environment for the library

The README splits installation into two paths because muselsl is both a command line tool and an importable library. For the command line, the recommendation is pipx, which installs the tool in its own isolated environment and puts it on PATH, so there is no virtual environment to manage. On macOS:

```bash
brew install pipx
pipx ensurepath        # one-time: adds pipx's app folder to your PATH
# close and reopen your terminal once after running ensurepath
pipx install muselsl
```

On Linux the same two steps follow a distribution package install, and on Windows the README uses the python.org Python through PowerShell:

```bash
py -m pip install --user pipx
py -m pipx ensurepath
# close and reopen your terminal once after running ensurepath
pipx install muselsl
```

For use inside an existing Python project, the README asks for a virtual environment and a plain pip install:

```bash
python3 -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install muselsl
```

Python 3.9 or later is required, with 3.10 to 3.12 recommended. The console entry point is registered in setup.py as muselsl=muselsl.__main__:main, so the command name matches the package name, which keeps the two install paths interchangeable from a shell.

## The stream command finds the device, and the name is on the left earpiece

Getting a stream running is three commands. The first lists the Muse devices that are reachable:

```bash
muselsl list
```

The second starts a stream from the first available Muse, and the third selects a specific one. The README notes that device names are printed on the inside of the left earpiece, with Muse-41D2 given as an example:

```bash
    $ muselsl stream --name YOUR_DEVICE_NAME
```

A MAC address can be passed instead, and the README gives the reason: it bypasses the device discovery step and can make connecting to devices quicker and more reliable. That is the option to reach for in a scripted experiment where the headset is always in the same place.

```bash
    $ muselsl stream --address YOUR_DEVICE_ADDRESS
```

One operational constraint runs through all of this and is easy to miss. The note under Working with Streaming Data says the process running the stream command must be kept alive in order to maintain the LSL stream, and the other commands have to be run in a second terminal or a second process. So a recording session is at minimum two processes, and the recording process does not survive the streamer exiting.

## Recording directly, and recording through the stream, are not the same thing

The recording commands come in two forms, and the difference is a documented limitation rather than an accident. Recording through the LSL stream writes EEG to a CSV and, according to the README, also saves data from any LSL stream containing Markers data, such as the stimulus presentation scripts in the EEG Notebooks repository. Recording directly bypasses LSL:

```bash
    $ muselsl record --duration 60  
```

```bash
    $ muselsl record_direct --duration 60
```

The README states plainly that direct recording does not allow Markers data to be recorded. That matters for anyone running an event-related paradigm, because the marker channel is what tells the analysis code when the stimulus happened. Choosing the direct path for convenience silently removes the timing information that makes the recording analysable, and the README does not offer a flag that would let you have both.

The recording process also handles one data type at a time. The note under Alternate Data Types says the record process will only record from one data type at a time, and that multiple terminals or processes can be used to record from several data types at once. So a full sensor recording means one process per stream, coordinated by the operator.

## PPG, accelerometer and gyroscope each become their own LSL stream

The Muse hardware carries more than EEG. The README lists an accelerometer, a gyroscope, and, on the Muse 2, a photoplethysmography sensor, and explains that these data types can be enabled through command line arguments or through parameters to the stream function. When enabled, each arrives as a separate LSL stream named PPG, ACC, and GYRO respectively, not as extra channels inside the EEG stream.

From the command line all three are turned on together:

```bash
    muselsl stream --ppg --acc --gyro
```

As a library the same thing is expressed as keyword arguments:

```python
from muselsl import stream, list_muses

muses = list_muses()
stream(muses[0]['address'], ppg_enabled=True, acc_enabled=True, gyro_enabled=True)
```

Splitting the sensors into separate streams is the right shape for LSL, because consumers subscribe to what they need and each stream carries its own sampling information. It also means a consumer of the EEG data alone never has to filter out motion channels that would otherwise contaminate an analysis. The trade off is that recording several of them means several record processes, as the note in the previous section describes.

## Streaming is synchronous, so library code after the call runs late

The example in the README shows the library entry points, list_muses and stream, and the ordering of the code is the thing to notice:

```python
from muselsl import stream, list_muses

muses = list_muses()
stream(muses[0]['address'])

# Note: Streaming is synchronous, so code here will not execute until after the stream has been closed
print('Stream has ended')
```

The stream function blocks. The comment says the code after the call does not execute until after the stream has been closed, which means the call returns when the stream ends, not when it starts. Any application logic that expects to set up a subscription before the first sample arrives has to be arranged around that, and the examples folder in the repository contains neurofeedback.py, recordStream.py and startMuseStream.py, which are the places to look for how the authors handle it.

The project also has a documented history in the experiments direction. The README says Muse LSL was designed so the Muse could be used to run classic EEG experiments including the P300 event-related potential and the SSVEP and SSAEP evoked potentials, and that the code for those experiments is still available but is now maintained in the EEG Notebooks repository by the NeuroTechX community. So the analysis code left this repository, and what remains here is the acquisition layer.

## Recent releases, and a viewer that needs extra dependencies

The release list shows three recent tags: v2.5.3 and v2.5.2 both dated 2026-09-08, and v2.5.1 dated 2026-07-16 with the message that it fixes a bleak double-subscribe error. The last push was on 2026-09-08, and the version in setup.py matches the latest tag, 2.5.3.

The dependency list in setup.py is worth a look, because it is larger than a streaming utility would suggest. It pulls in bitstring, pygatt, pandas, scikit-learn, numpy, seaborn, pexpect, and pylsl with a floor of 1.16. A comment in the file explains that 1.16 or later is needed for a universal2 and arm64 liblsl on Apple Silicon, referencing issue 203, and that an older Linux pin is being removed with CI watching for a resurface of an original could not create stream outlet issue under issue 235. The scikit-learn and seaborn entries are there for the built-in viewer.

The viewer has a second version, and it costs extra dependencies:

```bash
    $ muselsl view --version 2
```

The README suggests it when the visualization freezes or is laggy, and states that it requires the additional vispy and mne dependencies, which setup.py records as an extras_require entry named Viewer V2. The default viewer, muselsl view, needs none of that.

## Conclusion

muse-lsl is the right choice for a lab that already owns Muse hardware and wants its signals in a network accessible, time synchronised stream that other tools can subscribe to, and the wrong choice for a team that needs a headset-agnostic acquisition stack or support for a device model this package does not list. Before installing, check the Bluetooth backend on your platform, because the README recommends BlueMuse on Windows 10 and bgapi on a Mac with a BLED112 dongle while bleak is the default elsewhere, and confirm that a lab streaming layer runtime is present on the host, since the package declares pylsl as a dependency but the streams themselves are consumed by separate applications.

## FAQ

### How do I install muselsl on macOS?

The README recommends pipx for the command line tool, so install pipx with brew install pipx, run pipx ensurepath once to add its app folder to your PATH, and then run pipx install muselsl. For use inside your own project, create a virtual environment and run pip install muselsl.

### Which Muse headsets does muse-lsl support?

The README states that it is compatible with Muse 2, Muse S, and the classic Muse from 2016. It requires Python 3.9 or later, with Python 3.10 to 3.12 recommended.

### Why does my muse-lsl recording have no marker information?

The README states that direct recording does not allow Markers data to be recorded. Use muselsl record instead of muselsl record_direct if you need the markers from a stimulus presentation stream.

### How do I connect a Muse headset that is not discovered?

You can pass the device name, which is printed on the inside of the left earpiece, or the MAC address with muselsl stream --address, which the README says bypasses the discovery step and can make connecting quicker. On Windows with a BLED112 dongle the README also points at the BlueMuse GUI and the --interface argument for the COM port.

### What is the difference between muselsl record and muselsl record_direct?

Recording through the LSL stream also saves Markers data from any stream that carries it, such as the stimulus scripts in the EEG Notebooks repository. Direct recording skips LSL and cannot record markers.

## Sources

- [alexandrebarachant/muse-lsl on GitHub](https://github.com/alexandrebarachant/muse-lsl)
- [Issues](https://github.com/alexandrebarachant/muse-lsl/issues)
- [License: BSD-3-Clause](https://github.com/alexandrebarachant/muse-lsl/blob/master/LICENSE)
- [README](https://github.com/alexandrebarachant/muse-lsl/blob/master/README.md)
- [Releases](https://github.com/alexandrebarachant/muse-lsl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/alexandrebarachant-muse-lsl
