HyperStudy Bridge
A unified, high-performance desktop application for bridging HyperStudy web experiments with research hardware devices.
Overview
HyperStudy Bridge provides a reliable, low-latency communication layer between the HyperStudy web application and various research devices. Built with Tauri and Rust for maximum performance and minimal resource usage, it serves as the central hub for all device integrations.
The Bridge runs locally on your research computer and creates a WebSocket server that the HyperStudy web application connects to, enabling real-time communication with connected hardware.
Features
- High Performance: Sub-millisecond latency for time-critical operations
- Multi-Device Support: Simultaneous connection to multiple device types
- Auto-Reconnection: Resilient connection management with automatic recovery
- Real-Time Monitoring: Live status dashboard for all connected devices
- Secure: Local-only connections with sandboxed architecture
- Cross-Platform: macOS, Windows, and Linux
Supported Devices
| Device | Type | Connection | Status |
|---|---|---|---|
| HyperStudy TTL | TTL Pulse Generator | USB Serial | Supported |
| Kernel Flow2 | fNIRS | TCP Socket | Supported |
| Pupil Labs Neon | Eye Tracker | REST API + LSL (mDNS discovery) | Supported |
| EyeLink 1000 Plus | Eye Tracker | Ethernet (TCP) | Supported* |
| Lab Streaming Layer | Various | LSL Protocol | Supported |
*Requires the SR Research EyeLink Developers Kit installed on the Bridge machine.
Installation
Download the latest release for your platform:
Updating
Bridges from v0.8.22 check for updates on launch and install them in-app. Older installs must be updated by hand: download the current release and install it over the old one. Version 0.8.27 or later is required for the Neon phone pinning and recording safeguards described below.
Post-Install Steps
macOS:
- Open the downloaded DMG file and drag HyperStudy Bridge to your Applications folder
- On first launch, if macOS shows a security warning, right-click the app and select "Open"
- The app is signed and notarized by Apple for security
- Grant permissions when prompted:
- Serial Port Access: Required for TTL device communication
- Network Access: Required for device connections
Windows:
- Run the downloaded MSI installer
- Windows may show a SmartScreen warning on first run — click "More info" then "Run anyway"
Linux:
- Make the AppImage executable:
chmod +x HyperStudy-Bridge-*.AppImage - Run the AppImage directly:
./HyperStudy-Bridge-*.AppImage
Configuration
Bridge Settings
Settings are stored in:
- macOS:
~/Library/Application Support/hyperstudy-bridge/ - Windows:
%APPDATA%\hyperstudy-bridge\ - Linux:
~/.config/hyperstudy-bridge/
Example configuration:
{
"bridge": {
"port": 9000,
"autoConnect": true
},
"devices": {
"ttl": {
"port": "/dev/tty.usbmodem1234",
"baudRate": 115200
},
"kernel": {
"ip": "192.168.1.100",
"port": 6767
}
}
}
Device Configuration
Each device type has its own configuration panel in the Bridge GUI:
TTL Device:
- Serial port selection (auto-detected by USB VID/PID)
- Baud rate (default: 115200)
- Pulse duration (default: 10ms, range 1–10000ms)
- Connection timeout settings
Kernel Flow2:
- IP address of Kernel acquisition computer
- Port (default: 6767)
- Event categories to forward
Pupil Labs Neon:
- Find phones: lists every Neon Companion phone on the network with its name, hardware id, IP, battery and recording state; Use this phone pins the Bridge to that phone's hardware id
- Companion URL (optional): a fixed
ip:portfallback for networks without multicast; a pinned phone is always resolved by id first - Gaze data streaming options
See Pupil Labs Neon for why pinning matters when more than one phone is on the network.
EyeLink 1000 Plus:
- IP address of EyeLink Host PC (default:
100.1.1.1) - Sample rate (250, 500, 1000, or 2000 Hz)
- Display width/height for gaze coordinate mapping
- Requires EyeLink Developers Kit installed separately
LSL Streams:
- Stream name patterns to discover
- Outlet configuration
Usage
Basic Workflow
- Launch the Bridge before starting your experiment
- Configure Devices using the GUI interface
- Connect Devices by clicking "Connect All" or individual device buttons
- Verify Status - all required devices should show green status
- Start Experiment - the Bridge handles all communication automatically
Device Identity and Safeguards
Since v0.8.27 the Bridge reports which hardware is behind each connection and refuses operations that would affect someone else's device:
- Identity in every status. Connect, status and send responses — and the status broadcasts every connected client receives — carry the Neon phone's hardware id, name, IP, battery level and recording state. HyperStudy uses this in its pre-flight check.
- Pinned phones. A Pupil connection can be pinned to a phone's hardware id; a different phone answering is refused (
wrong_device) instead of silently used. - Recording ownership. The Bridge refuses to stop or cancel a Neon recording it did not start (
recording_not_owned) and reportsphone_busywhen asked to start while the phone is already recording. Recording start waits until the phone confirms the recording is active. - Independent device queues. Each device's commands run in arrival order on their own queue, so a slow Kernel connect or a Neon status probe never delays a TTL pulse or another device's markers.
- Kernel auto-reconnect. A marker sent to a Kernel Flow2 that lost its connection reconnects it first using the last known configuration, unless an explicit connect is already in progress.
- Reconnect-safe. A HyperStudy page that reloads and reconnects cannot be undone by the old connection's late commands; the Bridge app's own Disconnect and Reset buttons follow the same rule and are broadcast to connected clients.
Error codes returned in the code field of error responses:
| Code | Meaning |
|---|---|
wrong_device | A phone other than the pinned one answered |
device_not_found | The pinned phone is not on the network |
phone_busy | The phone is already recording |
recording_not_owned | The active recording was not started by this Bridge |
communication_error | The device could not be reached or its status could not be read |
Finding Your Bridge IP Address
Participants need the Bridge's IP address to connect during experiments:
- The Bridge displays its IP address and port in the status bar
- Alternatively, find your IP:
- macOS: Open Terminal and run
ifconfig | grep "inet " - Windows: Open Command Prompt and run
ipconfig - Linux: Run
ip addrorhostname -I
- macOS: Open Terminal and run
WebSocket Protocol
The Bridge exposes a WebSocket server on port 9000 (configurable). Connect using:
const ws = new WebSocket('ws://192.168.1.100:9000');
Message Format:
// Send command to device
{
"type": "command",
"device": "ttl",
"action": "send",
"payload": { "command": "PULSE" }
}
// Receive data from device
{
"type": "data",
"device": "kernel",
"payload": { /* device-specific data */ },
"timestamp": 1634567890123
}
Architecture
┌─────────────────┐ WebSocket ┌──────────────┐
│ HyperStudy │◄──────:9000───────►│ Bridge │
│ Web App │ │ Server │
└─────────────────┘ └──────┬───────┘
│
┌──────────────┬────────────┼────────────┬──────────────┐
│ │ │ │ │
┌─────▼─────┐ ┌─────▼────┐ ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ TTL │ │ EyeLink │ │ Kernel │ │ Pupil │ │ LSL │
│ Serial │ │ FFI │ │ TCP │ │ WS │ │ Streams │
└─────┬─────┘ └─────┬────┘ └────┬────┘ └─────┬─────┘ └─────┬─────┘
│ │ │ │ │
┌─────▼─────┐ ┌────▼─────┐ ┌───▼─────┐ ┌────▼─────┐ ┌─────▼─────┐
│ TTL Device│ │ EyeLink │ │ Flow2 │ │ Neon │ │ LSL-based │
└───────────┘ │ 1000 Plus│ └─────────┘ └──────────┘ │ Devices │
└──────────┘ └───────────┘
Performance
| Metric | Target | Typical |
|---|---|---|
| TTL Latency | <1ms | 0.5ms |
| Message Throughput | >1000/sec | 1500/sec |
| Memory Usage | <100MB | 45MB |
| CPU Usage (idle) | <5% | 2% |
| Startup Time | <2sec | 1.2sec |
Troubleshooting
Connection Issues
Cannot connect from HyperStudy:
- Verify the Bridge is running and shows "Server running on port 9000"
- Check that both devices are on the same network
- Ensure firewall allows connections on port 9000
- Try using the Bridge computer's IP address directly
Port already in use:
# Find process using port 9000
lsof -i :9000
# Kill process if needed (use with caution)
kill -9 <PID>
Device Issues
Serial port access denied (macOS/Linux):
# Add user to dialout group (Linux)
sudo usermod -a -G dialout $USER
# Check port permissions (macOS)
ls -la /dev/tty.*
Device not detected:
- Verify device is powered on and connected
- Check device appears in system (System Information on macOS)
- Try unplugging and reconnecting the device
- Restart the Bridge application
Neon: wrong phone, phone busy, or markers failing: see the Pupil Labs Neon troubleshooting section.
Logging
View Bridge logs for debugging:
- macOS:
~/Library/Logs/hyperstudy-bridge/ - Check the Bridge GUI's log panel for real-time output
Development
For developers who want to build from source:
Prerequisites
Building
# Clone the repository
git clone https://github.com/hyperstudyio/hyperstudy-bridge.git
cd hyperstudy-bridge
# Install dependencies
npm install
# Run in development mode
npm run tauri dev
# Build for production
npm run tauri build
Resources
- GitHub Repository: hyperstudyio/hyperstudy-bridge
- API Documentation: See repository
/docs/api/folder - Related: Kernel Integration Guide
Release Notes
v0.8.27
Released: 2026-09-04
What's Changed
This release fixes the dropped-marker and cross-talk failures seen in multi-person (hyperscanning) sessions where two Pupil Labs Neon phones share a network.
New Features
- Find Neon phones by hardware id. The device config modal can browse the network for Neon Companion phones and pin a station to one phone's hardware id. A pinned station resolves its phone over mDNS on every connect and refuses to connect to a different phone (
wrong_device), so two stations can no longer drive the same phone throughneon.local. - Device identity in status. Connect, status and send responses (and the broadcasts every client receives) carry the phone's hardware id, name, IP, battery and recording state, so HyperStudy can verify which hardware is behind each participant.
- Kernel auto-reconnect on send. A marker sent to a Kernel Flow2 that dropped its connection reconnects it first using the last known configuration.
Bug Fixes
- Recording start now waits for the phone to report the recording active before returning, so markers sent immediately after start are no longer lost.
- The bridge refuses to stop or cancel a recording it did not start (
recording_not_owned) and reportsphone_busywhen the phone is already recording; both checks fail closed when the phone's status cannot be read (passforceto override). - Markers sent to a recording other than the one the bridge started are flagged (
recording_mismatch). - A Neon reporting null hardware fields (glasses unplugged) no longer fails to parse.
- Overlapping WebSocket connections (a HyperStudy page reloading and reconnecting) can no longer overwrite or unregister each other's devices; the bridge app's own Disconnect and Reset follow the same rule and are broadcast to clients.
- LSL: same-named Neon streams from different phones are grouped by host instead of collapsing into one entry; the same stream cannot be opened twice under two labels.
Technical
- Per-device command queues: each device's commands run in arrival order on their own queue, so a slow device never delays another's markers; queues are bounded and drained safely on disconnect; a panicking handler no longer wedges a device.
- Connect tickets order overlapping connects by start time; stale disconnects are refused.
- CI: unsigned builds on pull requests, cargo-audit and npm audit gates, dependency license review; refreshed npm and cargo lockfiles.
- Stable error codes on device errors (
DeviceError::code()).
v0.8.26
Released: 2026-03-21
What's Changed
Bug Fixes
- Fix update check showing both error and success toasts simultaneously when the update check fails
v0.8.25
Released: 2026-03-21
What's Changed
New Features
- In-app auto-updates: The app now checks for updates on launch and can download and install them without leaving the app
- Native menu bar: macOS app menu with About, Check for Updates, Edit (Cmd+C/V/X), and Window submenus; Help menu on Windows/Linux
- Toast notifications: New toast notification system for update status, download progress, and success/error feedback
Technical
- Integrated
tauri-plugin-updaterandtauri-plugin-processfor self-updating and relaunch - Added updater artifact generation (
.tar.gz,.msi.zip,.sig) to all platform build workflows - Added
latest.jsonmanifest generation to release pipeline usingjq - Fixed
tauri.conf.jsonversion discrepancy (was 0.8.4, now synced) - Now tracking
Cargo.lockfor reproducible builds - Updated release process to include
tauri.conf.jsonas third version file
Note: This is the first release with updater support. Future updates will be installable directly from the app. This version must be installed manually.
v0.8.24
Released: 2026-02-25
What's Changed
Bug Fixes
- Fix EyeLink crash when connecting to unreachable Host PC — adds TCP reachability pre-check and FFI timeout to prevent segfault
v0.8.23
Released: 2026-02-25
What's Changed
New Features
- EyeLink 1000 Plus frontend integration — device configuration UI with IP address, sample rate, and display resolution settings
Technical
- Runtime dynamic loading for EyeLink SDK — replaced build-time linking with
libloadingfor runtime SDK detection; one universal binary for all users, EyeLink activates only if the SR Research SDK is present - Removed
eyelinkCargo feature flag — no more separate builds needed; all 15cfgguards removed - Added EyeLink to README supported devices table and CLAUDE.md device specs
- Added frontend tests for EyeLink display fields and IP validation
v0.8.22
Released: 2026-02-25
What's Changed
New Features
- Configurable TTL pulse duration — New "Pulse Duration (ms)" setting in the TTL config modal (1–1000ms, default 10ms). The duration is passed through to both Tauri commands and the WebSocket bridge, and can also be overridden per-pulse via
PULSE <ms>commands.
Bug Fixes
- Fix Neon connect timeout and add connection progress indicator
New Device Support
- Add EyeLink 1000 Plus eye tracker integration
v0.8.21
Released: 2026-02-17
What's Changed
Improvements
- TTL module now sends
PULSE <duration_ms>format to support configurable pulse duration on firmware v1.4.0+ - Configurable
pulse_duration_mssetting (default 10ms, range 1–10000ms) - Serial buffer drain on TTL connect for cleaner handshake
Documentation
- Updated TTL protocol docs, API documentation, and CLAUDE.md for firmware v1.4.0 compatibility
Compatibility
- Requires TTL firmware v1.4.0+ (bridge now sends duration parameter with every PULSE command)
v0.8.20
Released: 2026-02-15
What's Changed
New Features
- Unified FRENZ Connect button — Single "Connect" button now orchestrates the full FRENZ flow: starts the Python bridge, waits for streaming state with a progress bar, then connects LSL streams. "Disconnect" tears down both LSL and the bridge process. No more separate "Start Bridge" / "Stop Bridge" buttons.
Technical
- Backend:
"frenz"recognized in WebSocket device validation; early-return handlers for Connect, Disconnect, and Status actions route throughFrenzLslManager - Frontend: orchestrated multi-step connect with indeterminate progress bar and phase text during bridge startup (30s–3min)
v0.8.19
Released: 2026-02-15
What's Changed
Improvements
- Forward Neon event response data (recording_id, timestamp) to HyperStudy instead of discarding it
- Normalize device IDs to lowercase to prevent silent lookup failures from case mismatches
Bug Fixes
- Fix flaky memory leak integration tests by measuring process RSS instead of system-wide memory
Technical
- Change
Device::send_eventreturn type toResult<serde_json::Value, DeviceError>for richer response data - Upgrade Neon event logging from debug to info level with recording_id
v0.8.18
Released: 2026-02-15
Bug Fixes
- Pupil Labs Neon: Fix "Failed to parse event response" error when sending events without an active recording. The
recording_idfield in the Neon API response is now handled as optional, allowing test events and annotations to succeed regardless of recording state.
v0.8.17
Released: 2026-02-15
What's Changed
Bug Fixes
- Fixed Pupil Labs Neon connection failure — two issues resolved:
- Made
PhoneInfo.portoptional (#[serde(default)]) since the Neon REST API doesn't include it in the Phone status response - Added mDNS hostname pre-resolution for
.localaddresses — the reqwest/hyper HTTP client can't resolve mDNS names on macOS, so we now resolve viatokio::net::lookup_host(which uses the system's mDNSResponder) before making HTTP requests
- Made
v0.8.16
Released: 2026-02-15
What's Changed
Bug Fixes
- Fixed Pupil Labs Neon connection failure caused by missing
portfield in the device's Phone status response. The field is now optional via#[serde(default)], matching the real Neon REST API behavior.
v0.8.15
Released: 2026-02-14
Changes since v0.8.14
🐛 Bug Fixes
- Rewrite Pupil Labs Neon integration to use real REST API (2132975)
🔨 Maintenance
- Bump version to 0.8.15 (5b242ab)
📊 Statistics
- Commits: 2
- Contributors: 1
- Files changed: 17 files changed, 1116 insertions(+), 1204 deletions(-)
Full Changelog: https://github.com/hyperstudyio/hyperstudy-bridge/compare/v0.8.14...v0.8.15
📦 Installation
macOS
- Apple Silicon (M1/M2/M3): Download
HyperStudy-Bridge-v0.8.15-aarch64-apple-darwin.dmg - Intel Macs: Download
HyperStudy-Bridge-v0.8.15-x86_64-apple-darwin.dmg
Open the DMG and drag HyperStudy Bridge to your Applications folder.
All macOS builds are signed and notarized by Apple.
Windows
- Download
HyperStudy-Bridge-v0.8.15-x86_64-windows.msi
Note: Windows may show a SmartScreen warning on first run. Click "More info" → "Run anyway" to proceed.
Linux
- Download
HyperStudy-Bridge-v0.8.15-x86_64-linux.AppImage
Make executable and run: ```bash chmod +x HyperStudy-Bridge-.AppImage ./HyperStudy-Bridge-.AppImage ```
v0.8.14
Released: 2026-02-14
Changes since v0.8.13
✨ Features
- Add PyApp-based FRENZ Python bridge with process lifecycle management (3b1d2b7)
🔨 Maintenance
- Bump version to 0.8.14 (e1d3d41)
- Fix formatting across Rust and frontend code to pass CI checks (4191b2a)
📝 Other Changes
- Fix Kernel event delivery reliability with TCP keepalive and send retry (8446bba)
📊 Statistics
- Commits: 4
- Contributors: 1
- Files changed: 64 files changed, 7959 insertions(+), 2953 deletions(-)
Full Changelog: https://github.com/hyperstudyio/hyperstudy-bridge/compare/v0.8.13...v0.8.14
📦 Installation
macOS
- Apple Silicon (M1/M2/M3): Download
HyperStudy-Bridge-v0.8.14-aarch64-apple-darwin.dmg - Intel Macs: Download
HyperStudy-Bridge-v0.8.14-x86_64-apple-darwin.dmg
Open the DMG and drag HyperStudy Bridge to your Applications folder.
All macOS builds are signed and notarized by Apple.
Windows
- Download
HyperStudy-Bridge-v0.8.14-x86_64-windows.msi
Note: Windows may show a SmartScreen warning on first run. Click "More info" → "Run anyway" to proceed.
Linux
- Download
HyperStudy-Bridge-v0.8.14-x86_64-linux.AppImage
Make executable and run: ```bash chmod +x HyperStudy-Bridge-.AppImage ./HyperStudy-Bridge-.AppImage ```
Release notes are automatically synced from GitHub releases.