Skip to main content

Real-time Processing for BCI

Real-time performance is critical for brain-computer interfaces. Users expect immediate responses to their neural commands - any delay breaks the sense of direct neural control and degrades the user experience. NimbusSDK achieves 10-25ms inference latency, enabling truly responsive BCI applications.

Why Latency Matters in BCI

The 20ms Threshold

Research shows that for natural interaction, BCI systems need to respond within 20 milliseconds of neural signal acquisition:
  • Motor BCIs: Cursor control feels natural only with sub-20ms latency
  • Communication BCIs: Real-time typing requires immediate character selection
  • Gaming BCIs: Competitive gaming demands instant response to mental commands
  • Assistive BCIs: Wheelchair control and robotic arms need immediate execution
Standard BCI processing pipelines take 200ms or more, creating a noticeable delay that breaks the illusion of direct neural control.

Current BCI Latency Bottlenecks

Traditional BCI systems have multiple latency sources:
Signal Acquisition → Feature Extraction → Classification → Post-processing → Output
     ~10ms              ~50ms            ~100ms         ~40ms        ~10ms
                                    
Total: ~210ms (10x slower than needed)
Major bottlenecks:
  • Batch processing: Waiting for signal windows (50-100ms)
  • Complex feature extraction: FFTs, spatial filters, etc.
  • Inefficient classifiers: SVMs, neural networks with high overhead
  • Post-processing: Smoothing, voting, calibration steps

Nimbus Real-time Architecture

Streaming Inference Pipeline

NimbusSDK processes neural signals as continuous streams, not batches:
Neural Stream → Reactive Processing → Probabilistic Inference → Real-time Output
    ~1ms            ~5ms                    ~10-15ms            ~2ms

Total: 18-23ms (sub-25ms target achieved)

Key Performance Features

Reactive Message Passing

Updates only when new data arrives using RxInfer.jl

Incremental Inference

Builds on previous computations instead of starting fresh

Optimized Factor Graphs

Efficient graph structures minimize computation

Julia Performance

Native Julia compilation for maximum speed

Technical Implementation

Streaming Data Processing

NimbusSDK handles continuous data streams efficiently:
using NimbusSDK

# Initialize streaming session
metadata = BCIMetadata(
    sampling_rate = 250.0,
    paradigm = :motor_imagery,
    feature_type = :csp,
    n_features = 16,
    n_classes = 4,
    chunk_size = 250  # 1 second chunks
)

session = init_streaming(model, metadata)

# Process chunks as they arrive - no waiting for full trials
for chunk in eeg_stream
    # Incremental processing - updates beliefs in ~15ms
    chunk_result = process_chunk(session, chunk)
    
    # Immediate feedback to user
    if chunk_result.confidence > 0.7
        provide_immediate_feedback(chunk_result.prediction)
    end
end

# Finalize with weighted voting across chunks
final_result = finalize_trial(session; method=:weighted_vote)

Batch Processing for Offline Analysis

For offline analysis where latency isn’t critical:
using NimbusSDK

# Process multiple trials efficiently
results = predict_batch(model, bci_data; iterations=10)

# Typical performance: 200 trials in ~5 seconds
# Average latency per trial: ~25ms

Reactive Message Passing

RxInfer.jl reactive programming principles:
  • Event-driven: Computation triggered by new data arrival
  • Incremental: Only update affected parts of the factor graph
  • Non-blocking: Asynchronous message updates
  • Memory efficient: Bounded memory usage for streaming

Memory Efficiency

Real-time systems must manage memory carefully:
  • Bounded memory: Fixed memory usage regardless of runtime
  • Efficient data structures: Optimized for sequential access
  • Minimal allocation: Reuse memory buffers when possible
  • Low GC pressure: Julia’s memory management optimized for real-time

Performance Optimizations

Algorithmic Optimizations

Most BCI models have sparse connectivity. NimbusSDK exploits this:
  • Local updates: Only affected nodes recompute
  • Message caching: Reuse previous computations
  • Efficient scheduling: Optimal update order
RxInfer uses variational message passing for fast approximate inference:
  • Closed-form updates: No sampling required
  • Parallel computation: Independent message updates
  • Convergence guarantees: Provably correct inference
Adjust computational precision based on requirements:
# RxLDA: Fast with shared covariance
# RxGMM: More flexible with class-specific covariances

# Choose based on accuracy/speed tradeoff
model = accuracy_priority ? RxGMMModel : RxLDAModel

Julia Performance

JIT Compilation

First inference compiles, subsequent calls are fast

Type Specialization

Compiler optimizes for actual data types

SIMD Operations

Vectorized operations for array computations

Memory Efficiency

Minimal allocation and efficient GC

Real-World Performance

Benchmark Results

NimbusSDK consistently achieves sub-25ms latency for BCI applications: || Application Type | Signal Type | Features | NimbusSDK Latency | Typical Pipeline | ||-----------------|-------------|----------|-------------------|------------------| || Motor Imagery | CSP | 16 | 15-20ms | 180ms | || P300 Detection | ERP | 12 | 12-18ms | 150ms | || Multi-class MI | CSP | 32 | 20-25ms | 200ms | || Binary MI | CSP | 8 | 10-15ms | 120ms |

Scaling Characteristics

  • Feature count: Linear scaling with number of features
  • Classes: Minimal impact from 2-4 classes
  • Streaming: Constant per-chunk latency
  • Batch: Efficient parallel processing
Performance measured on modern CPU (Intel i7 or equivalent). RxLDA is typically faster than RxGMM due to shared covariance structure.

Deployment Considerations

Local Processing

For optimal latency, deploy NimbusSDK locally:
  • No network latency: All processing on device
  • Predictable performance: No cloud variability
  • Privacy: Neural data stays local
  • Offline capability: Works without internet

System Requirements

Minimum:
  • Julia 1.9+
  • 4GB RAM
  • Modern CPU (Intel i5 or equivalent)
Recommended:
  • Julia 1.10+
  • 8GB RAM
  • Fast CPU (Intel i7 or equivalent)
  • SSD for model loading

Monitoring and Debugging

Performance Metrics

Monitor system performance during inference:
using NimbusSDK

# Track performance with built-in metrics
tracker = OnlinePerformanceTracker(window_size=50)

for (pred, label, conf) in zip(predictions, true_labels, confidences)
    metrics = update_and_report!(tracker, pred, label, conf)
end

# Get comprehensive metrics
full_metrics = get_metrics(tracker, n_classes=4, trial_duration=4.0)

println("Accuracy: $(round(full_metrics.accuracy * 100, digits=1))%")
println("ITR: $(round(full_metrics.information_transfer_rate, digits=1)) bits/min")
println("Mean confidence: $(round(full_metrics.mean_confidence, digits=3))")

Quality Assessment

Identify trials with poor signal quality:
using NimbusSDK

# Assess each trial's quality
quality = assess_trial_quality(results)

if !quality.confidence_acceptable
    @warn "Trial quality below threshold" quality.overall_score
    println("Recommendation: $(quality.recommendation)")
end

# Check if rejection is warranted
if should_reject_trial(confidence, 0.7)
    println("⚠️  Low confidence trial - consider rejecting")
end
Use quality assessment to identify when recalibration is needed or when environmental conditions are affecting signal quality.

Getting Started with Real-time BCI

Ready to build ultra-fast BCI applications?
Next: Learn how Nimbus handles uncertainty in neural signals to build robust BCI applications.