Capacitor 7 plugin for Lab Streaming Layer (LSL) — stream biosignal data from mobile devices to LabRecorder or any LSL-compatible receiver.
npm install @mindfield/capacitor-lsl
npx cap syncWarning: Do NOT install
cordova-plugin-lsland@mindfield/capacitor-lslin the same project. They share the same native libraries and will cause duplicate class conflicts.
| Platform | Status | Notes |
|---|---|---|
| Android | Supported | ARM64, ARMv7, x86_64 |
| iOS | Supported | ARM64 (device + simulator) |
| Web | Not supported | LSL requires native UDP multicast |
import { LSL } from '@mindfield/capacitor-lsl';
// Create an outlet
const { outletId } = await LSL.createOutlet({
name: 'eSense_EDA',
type: 'EDA',
channelCount: 1,
sampleRate: 5.0,
channelFormat: 'float32',
sourceId: 'esense-eda-001',
metadata: {
manufacturer: 'Mindfield Biosystems',
device: 'eSense EDA',
channels: [{ label: 'EDA', unit: 'microsiemens', type: 'EDA' }],
},
});
// Show IP for KnownPeers config
const { ip } = await LSL.getDeviceIP();
console.log(`Add to lsl_api.cfg: KnownPeers = {${ip}}`);
// Push samples
await LSL.pushSample({
outletId,
data: [3.14],
});
// Push chunks (more efficient for high sample rates)
await LSL.pushChunk({
outletId,
data: [[3.14], [3.15], [3.16], [3.17], [3.18]],
});
// Check for consumers (LabRecorder etc.)
const { hasConsumers } = await LSL.hasConsumers({ outletId });
// Wait for a consumer to connect (blocking, with timeout)
const result = await LSL.waitForConsumers({ outletId, timeout: 30.0 });
// Cleanup
await LSL.destroyOutlet({ outletId });
// or: await LSL.destroyAllOutlets();| Method | Description |
|---|---|
createOutlet(options) |
Create a new LSL outlet. Returns { outletId }. |
pushSample(options) |
Push a single sample to an outlet. |
pushChunk(options) |
Push a chunk of samples (single JNI/C call, more efficient). |
hasConsumers(options) |
Check if any consumer is connected. Returns { hasConsumers }. |
waitForConsumers(options) |
Wait for a consumer (blocking). Returns { hasConsumers }. |
destroyOutlet(options) |
Destroy a specific outlet. |
destroyAllOutlets() |
Destroy all outlets and release resources. |
| Method | Description |
|---|---|
getLocalClock() |
Get LSL monotonic clock time. Returns { timestamp }. |
getLibraryVersion() |
Get liblsl version. Returns { version }. |
getProtocolVersion() |
Get LSL protocol version. Returns { version }. |
getDeviceIP() |
Get Wi-Fi IP address. Returns { ip }. |
type ChannelFormat = 'float32' | 'double64' | 'int32' | 'int16' | 'int8' | 'string';
interface CreateOutletOptions {
name: string;
type: string;
channelCount: number;
sampleRate: number;
channelFormat: ChannelFormat;
sourceId?: string;
metadata?: StreamMetadata;
}
interface StreamMetadata {
manufacturer?: string;
device?: string;
channels?: ChannelInfo[];
}
interface ChannelInfo {
label: string;
unit: string;
type: string;
}- Install LabRecorder
- Get the device IP:
const { ip } = await LSL.getDeviceIP() - Add to
lsl_api.cfg:[lab] KnownPeers = {192.168.1.100} - Start LabRecorder — the stream should appear in the stream list
| Cordova | Capacitor |
|---|---|
LSL.createOutlet(options) → string |
LSL.createOutlet(options) → { outletId: string } |
LSL.pushSample(outletId, data, ts) |
LSL.pushSample({ outletId, data, timestamp }) |
LSL.pushChunk(outletId, data) |
LSL.pushChunk({ outletId, data }) |
LSL.hasConsumers(outletId) → boolean |
LSL.hasConsumers({ outletId }) → { hasConsumers } |
LSL.waitForConsumers(outletId, timeout) |
LSL.waitForConsumers({ outletId, timeout }) |
LSL.destroyOutlet(outletId) |
LSL.destroyOutlet({ outletId }) |
LSL.getLocalClock() → number |
LSL.getLocalClock() → { timestamp } |
LSL.getLibraryVersion() → string |
LSL.getLibraryVersion() → { version } |
LSL.getDeviceIP() → string |
LSL.getDeviceIP() → { ip } |
Key differences:
- All parameters are passed as options objects (Capacitor convention)
- All return values are wrapped in objects
- Import from
@mindfield/capacitor-lslinstead of globalLSLvariable
TypeScript API (definitions.ts)
│
├── Android: Kotlin Plugin (LslPlugin.kt)
│ └── JNI Shim (de.mindfield.cordova.lsl.LSLPlugin.java)
│ └── liblsl_jni.so → liblsl.so (pre-built C library)
│
└── iOS: Objective-C Plugin (LslPlugin.m)
└── liblsl.xcframework (pre-built C library, direct C API calls)
MIT — Copyright (c) 2026 Mindfield Biosystems Ltd.