| Manufacturer | Device | Channels | Sample Rate | Brainflow ID | Protocol |
|---|---|---|---|---|---|
| OpenBCI | Cyton | 8 | 250 Hz | 0 | Serial/WiFi |
| OpenBCI | Cyton + Daisy | 16 | 125 Hz | 2 | Serial/WiFi |
| OpenBCI | Ganglion | 4 | 200 Hz | 1 | BLE |
| NeuroSky | MindWave | 1 | 512 Hz | 18 | Bluetooth |
| Muse | Muse 2 | 4 | 256 Hz | 22 | BLE |
| Muse | Muse S | 4 | 256 Hz | 21 | BLE |
| Emotiv | Insight | 5 | 128 Hz | 25 | BLE |
| Emotiv | EPOC X | 14 | 128/256 Hz | 26 | BLE |
| PiEEG | PiEEG | 8 | 250-16000 Hz | 46 | SPI (Raspberry Pi) |
| PiEEG | PiEEG-16 | 16 | 250-8000 Hz | 47 | SPI (Raspberry Pi) |
| PiEEG | IronBCI | 8 | 250 Hz | N/A* | BLE/WiFi |
| PiEEG | IronBCI-32 | 32 | 250 Hz | N/A* | WiFi |
| PiEEG | JNEEG | 8 | 250-2000 Hz | N/A* | SPI (Jetson Nano) |
| PiEEG | ardEEG | 8 | 250 Hz | N/A* | Serial (Arduino) |
| PiEEG | MicroBCI | 8 | 250 Hz | N/A* | BLE (STM32) |
| Cerelog | ESP-EEG | 8 | 250 Hz | N/A* | WiFi (TCP) |
| LSL | Generic (8-64ch) | 8-64 | Variable | -2** | Lab Streaming Layer |
| LSL | Brain Products | 32+ | Up to 25kHz | N/A* | LSL |
| LSL | BioSemi ActiveTwo | 32+ | Up to 16kHz | N/A* | LSL |
| LSL | g.tec | 16+ | Up to 38kHz | N/A* | LSL |
| LSL | Cognionics | 20-30 | 500 Hz | N/A* | LSL |
| LSL | ANT Neuro | 32+ | 2048 Hz | 29 | LSL |
| LSL | NIRx fNIRS | 16+ | 10 Hz | N/A* | LSL |
| Brainflow | Synthetic | 8 | 250 Hz | -1 | Virtual |
*Requires WebSocket bridge (included). **LSL streams can use Brainflow Streaming Board ID -2 for forwarding.
Browsers cannot open raw TCP, Serial, or Bluetooth connections directly.
For hardware devices, you need a WebSocket bridge that runs locally and proxies the device data to the browser. This project includes:
- lsl_ws_bridge.py - For any LSL source (130+ devices → WebSocket)
- pieeg_ws_bridge.py - For PiEEG devices (SPI/BrainFlow → WebSocket)
- cerelog_ws_bridge.py - For Cerelog ESP-EEG (TCP → WebSocket)
EEG Device (TCP/Serial/BLE)
↓
WebSocket Bridge (Python/Node.js)
↓ (ws://localhost:876x)
PhantomLoop (Browser)
- Comprehensive device registry with specs for 10+ EEG devices
- Brainflow board IDs for all supported devices
- Auto-configuration based on device selection
- Default montages with standard 10-20 electrode positions
- Single adapter that works with all device profiles
- Automatic protocol detection and parsing
- Signal quality estimation from amplitude characteristics
- Reconnection handling with exponential backoff
- Interactive electrode configuration UI with support for 1-16+ channel setups
- Standard 10-20/10-10 montage with predefined positions
- Device-specific defaults (Muse positions, Emotiv layout, etc.)
- Real-time signal quality monitoring with color-coded indicators
- Auto-detect board ID from device type
- Device-aware Python code generation
- Complete channel mapping with positions
- Ready-to-run scripts for any Brainflow-compatible device
import { createAdapterForDevice } from './streams';
// Create adapter for your device
const adapter = createAdapterForDevice('openbci-cyton');
// or: 'muse-2', 'emotiv-insight', 'neurosky-mindwave', etc.
// Connect to WebSocket bridge
await adapter.connect('ws://localhost:8766');# Install brainflow
pip install brainflow
# Use brainflow-websocket-bridge (community)
# https://github.com/brainflow-dev/brainflow-websocket-bridge
python bridge.py --board-id 0 --serial-port COM3# Install muselsl
pip install muselsl
# Stream via LSL, then use LSL-to-WebSocket bridge
muselsl stream
# In another terminal, run the LSL bridge
python scripts/lsl_ws_bridge.py --stream "Muse"# The LSL bridge supports any LSL-compatible device:
# Brain Products, BioSemi, g.tec, ANT Neuro, Cognionics, NIRx, etc.
# Install dependencies
pip install websockets pylsl numpy
# Auto-discover and connect to first EEG stream
python scripts/lsl_ws_bridge.py
# Connect to specific stream by name
python scripts/lsl_ws_bridge.py --stream "OpenBCI_EEG"
# List available LSL streams on your network
python scripts/lsl_ws_bridge.py --list
# Run with simulated data (for testing)
python scripts/lsl_ws_bridge.py --simulate
# Connect in PhantomLoop to ws://localhost:8767WebSocket Commands:
{"command": "discover"}
{"command": "connect", "name": "OpenBCI_EEG", "stream_type": "EEG"}
{"command": "disconnect"}
{"command": "ping"}# Use Emotiv's Cortex API with WebSocket bridge
# https://emotiv.gitbook.io/cortex-api/# 1. Connect PiEEG shield to Raspberry Pi GPIO
# 2. Enable SPI: sudo raspi-config → Interface Options → SPI
# Install dependencies
pip install websockets spidev RPi.GPIO numpy
# Run the bridge
cd scripts
python pieeg_ws_bridge.py --rate 250 --gain 24
# Options:
# --rate Sample rate: 250, 500, 1000, 2000, 4000, 8000, 16000
# --gain PGA gain: 1, 2, 4, 6, 8, 12, 24
# --channels 8 or 16 (for PiEEG-16)
# --port WebSocket port (default: 8766)
# --brainflow Use BrainFlow instead of direct SPI
# Connect in PhantomLoop to ws://<raspberry-pi-ip>:8766Development Mode (no hardware):
# On non-Raspberry Pi systems, the bridge auto-enables simulation mode
# Generates synthetic alpha waves for testing
python pieeg_ws_bridge.py# Connect to ESP-EEG WiFi (SSID: CERELOG_EEG, Password: cerelog123)
# Run the included bridge
cd scripts
pip install websockets
python cerelog_ws_bridge.py
# Connect in PhantomLoop to ws://localhost:8765- WelcomeScreen → Click "Configure Electrodes"
- Select your device from the dropdown
- Configure electrodes and monitor signal quality
- Proceed to Dashboard when ready
src/
├── devices/
│ ├── index.ts # Device exports
│ └── deviceProfiles.ts # Universal device registry
├── streams/
│ └── UniversalEEGAdapter.ts # Universal EEG stream adapter
├── types/
│ └── electrodes.ts # Updated for multi-device support
└── utils/
└── brainflowExport.ts # Enhanced with device profiles
interface DeviceProfile {
id: string; // 'openbci-cyton', 'muse-2', etc.
name: string; // Human-readable name
manufacturer: string; // 'OpenBCI', 'Muse', etc.
channelCount: number; // Number of EEG channels
samplingRates: number[]; // Supported sample rates
resolution: number; // ADC resolution in bits
brainflowBoardId?: number; // Brainflow board ID
protocols: string[]; // Supported protocols
capabilities: {
hasImpedanceMeasurement: boolean;
hasAccelerometer: boolean;
supportsBrainflow: boolean;
// ...
};
defaultMontage?: {
labels: string[]; // ['Fp1', 'Fp2', ...]
positions: Position3D[]; // 3D positions
};
}import { BRAINFLOW_BOARD_IDS } from './devices';
// OpenBCI
BRAINFLOW_BOARD_IDS.CYTON // 0
BRAINFLOW_BOARD_IDS.GANGLION // 1
BRAINFLOW_BOARD_IDS.CYTON_DAISY // 2
// NeuroSky
BRAINFLOW_BOARD_IDS.MINDWAVE // 18
// Muse
BRAINFLOW_BOARD_IDS.MUSE_2 // 22
BRAINFLOW_BOARD_IDS.MUSE_S // 21
// Emotiv
BRAINFLOW_BOARD_IDS.INSIGHT // 25
BRAINFLOW_BOARD_IDS.EPOC // 26
// PiEEG
BRAINFLOW_BOARD_IDS.PIEEG // 46
BRAINFLOW_BOARD_IDS.PIEEG_16 // 47
// Testing
BRAINFLOW_BOARD_IDS.SYNTHETIC // -1import { listDeviceProfiles, getSupportedDevices } from './streams';
// Get all device profiles
const devices = getSupportedDevices();
console.log(devices);
// [{ id: 'openbci-cyton', name: 'OpenBCI Cyton', channelCount: 8, ... }, ...]import { createUniversalEEGAdapter } from './streams';
const adapter = createUniversalEEGAdapter({
deviceId: 'muse-2',
bridgeUrl: 'ws://localhost:8767',
channelLabels: ['TP9', 'AF7', 'AF8', 'TP10'], // Optional override
});
adapter.onSample((sample) => {
console.log('EEG data:', sample.channels);
});
await adapter.connect();import {
downloadBrainflowPythonCode,
getBoardIdFromDeviceType
} from './utils/brainflowExport';
// Auto-detects board ID from device type
downloadBrainflowPythonCode(electrodeConfig);
// Or specify explicitly
const boardId = getBoardIdFromDeviceType('openbci-cyton'); // Returns 0const adapter = createAdapterForDevice('openbci-cyton');
await adapter.connect();
// Get channel statistics
const stats = adapter.getChannelStats();
stats.forEach((ch, i) => {
console.log(`Ch${i}: quality=${ch.quality}, std=${ch.std.toFixed(1)}µV`);
});- Impedance check: Supported on Cyton/Ganglion
- Accelerometer: Built-in 3-axis accelerometer
- Markers: Supports event markers via aux channels
- Positions: Non-standard (TP9, AF7, AF8, TP10)
- Aux sensors: PPG, gyroscope, accelerometer
- No impedance: Quality estimated from signal
- Subscription: Requires Emotiv account for raw data
- Impedance: Supported on all models
- Motion: Gyroscope + accelerometer
- ADS1299: 24-bit resolution, programmable gain (1-24x)
- Sample rates: 250 Hz to 16 kHz configurable
- Impedance: Supported via ADS1299 lead-off detection
- Signals: EEG, EMG, ECG all supported
- Raspberry Pi: Compatible with Pi 3, 4, and 5
- Safety: Battery power only (5V) - never connect to mains!
- BrainFlow: Supported (board ID 46 for PiEEG, 47 for PiEEG-16)
- Variants:
- PiEEG-16: 16-channel daisy-chain
- IronBCI: Wearable with BLE/WiFi and mobile SDK
- IronBCI-32: 32-channel high-density
- JNEEG: Jetson Nano for GPU-accelerated DL
- ardEEG: Arduino shield for beginners
- MicroBCI: STM32 NUCLEO-WB55 compact BLE
- ADS1299: No impedance measurement (signal-based quality only)
- WiFi AP: Device creates its own network
- Binary protocol: Requires WebSocket bridge
For devices without impedance measurement (Muse, Cerelog), quality is estimated:
| Quality | Std Dev (µV) | Pseudo-Impedance | Description |
|---|---|---|---|
| Good | 5-100 | <5 kΩ | Normal EEG range |
| Fair | 100-200 | 5-15 kΩ | Slightly elevated noise |
| Poor | 200-500 | 15-50 kΩ | High noise/artifacts |
| Disconnected | <5 or >500 | >50 kΩ | No signal or saturated |
When electrode configuration is available, decoders receive spatial features:
// In your decoder
return (input) => {
const { spikes, spatialFeatures, channelMask } = input;
// ROI averages by brain region
if (spatialFeatures?.roiAverages) {
const motorActivity = spatialFeatures.roiAverages.central;
const visualActivity = spatialFeatures.roiAverages.occipital;
}
// Only use active channels
const activeData = channelMask
? spikes.filter((_, i) => channelMask[i])
: spikes;
return { x: 0, y: 0 };
};Use Brainflow's synthetic board:
from brainflow import BoardShim, BrainFlowInputParams
board = BoardShim(-1, BrainFlowInputParams()) # -1 = Synthetic
board.prepare_session()
board.start_stream()Q: Can I add support for a new device?
A: Yes! Add a new entry to DEVICE_PROFILES in src/devices/deviceProfiles.ts.
Q: Do I need a bridge for all devices?
A: Yes, browsers can't access hardware directly. Each device type needs a bridge.
Q: Can I use multiple devices simultaneously?
A: Yes, create multiple adapter instances with different bridge URLs.
Q: What if my device isn't listed?
A: Use 'brainflow-generic' device type and configure manually, or add a new profile.
Same as PhantomLoop main project.