A vanilla JavaScript single-page application (SPA) for controlling vagus nerve stimulation devices via Web Bluetooth API.
Live: https://vgctrl.rd.to or https://vg.rd.to
Source: https://github.com/AppliedEllipsis/VGCtrl
THIS IS NOT MEDICAL ADVICE. USE AT YOUR OWN RISK.
- This application is for experimental and educational purposes only
- Not evaluated by any medical regulatory authority (FDA, CE, etc.)
- Not intended to diagnose, treat, cure, or prevent any disease
- Suggestions, modes, and configurations are based on trial and error, observation, and community experimentation — not necessarily peer-reviewed science
- Vagus nerve stimulation affects individuals differently; effects vary significantly
- Consult a qualified healthcare professional before use, especially if you have:
- Heart conditions, epilepsy, or seizure disorders
- Implanted medical devices (pacemakers, etc.)
- Are pregnant, or have other medical conditions
- Stop immediately if you experience pain, dizziness, irregular heartbeat, or any adverse effects
- You assume full responsibility for any consequences of using this software
- Device Connection: Scan and connect to vagus nerve stimulator devices via Web Bluetooth
- Session Management: Start, pause, resume, and stop stimulation sessions
- 8 Stimulation Modes: Stress Relief, Sleep, Focus, Pain Relief, Calm, Headache, Nausea, Meditation
- Intensity Control: Adjust stimulation intensity (levels 1-9)
- Timer: Configurable session duration (1-60 minutes)
- Breathing Guide: Visual breathing guidance for respiratory-gated modes (Calm, Meditation)
- Page Visibility API: Detects when app goes to background/foreground
- Wake Lock API: Prevents screen from sleeping during active sessions
- Wall-Clock Reconciliation: Accurate timing across background transitions
- Service Worker: Offline capability and background sync support
- PWA Manifest: Installable as a progressive web app
- Keepalive Pings: Sends periodic commands to prevent connection timeout
Browsers aggressively throttle, suspend, or terminate background tabs to save battery and resources. This is a fundamental limitation that cannot be fully bypassed. This app implements aggressive countermeasures, but they only extend survival time—they cannot guarantee indefinite background operation.
Known behaviors:
- Chrome terminates Web Bluetooth connections when tabs go to background
- Safari suspends JavaScript execution in background tabs
- Firefox throttles timers in inactive tabs (minimum 1s intervals)
- All browsers eventually suspend tabs regardless of countermeasures
- Battery saver modes on mobile devices ignore keepalive attempts
Implemented countermeasures (extend survival, do not prevent eventual suspension):
- Wake Lock API - Keeps screen on during sessions
- Silent Audio Context - Keeps audio processing thread alive (prevents some suspension)
- Web Worker - Runs timer in separate thread
- BroadcastChannel - Cross-tab communication pings
- No-op CSS Animation - Constant layout recalc prevents some throttling
- MessageChannel Microtasks - Aggressive scheduling
- Canvas Render Loop - requestAnimationFrame keeps rendering thread active
- Periodic Background Sync - Scheduled wake-ups (Chrome only)
- Persistent Notification - requireInteraction keeps service worker alive
- Aggressive Pinging - 250ms interval when hidden
Best Practices for Users:
- Keep Chrome in the foreground - Do not switch to other apps
- Use a dedicated Chrome window - Not a background tab
- Disable screen timeout in OS settings
- On Android:
- Enable "Desktop site" mode
- Use split-screen to keep Chrome partially visible
- Disable Chrome's "Battery Saver" for this site
- On Desktop: Use a separate Chrome window, not a tab
If Disconnection Occurs:
- Return to the tab immediately
- The app attempts auto-reconnection (requires user interaction)
- Session timing continues accurately via wall-clock reconciliation
- ASCII Protocol: Uses string-based commands with newline terminator
- Keepalive: Sends intensity command every 10 seconds during active sessions
- Status Polling: Queries battery and charging status every 30 seconds
- Auto-reconnect: Exponential backoff reconnection on disconnection
- Response Parsing: Handles battery voltage, charging status, and acknowledgments
vgctrl/
├── index.html # Main HTML structure
├── style.css # Dark theme styling
├── manifest.json # PWA manifest
├── sw.js # Service worker for offline/PWA
├── server.js # HTTPS server with clickable terminal links
├── icon.svg # App icon
├── js/
│ ├── protocol.js # BLE protocol definitions (ASCII)
│ ├── bluetooth.js # Web Bluetooth API wrapper
│ ├── session-clock.js # Session timing with wall-clock tracking
│ ├── mode-engines.js # Stimulation pattern generators
│ ├── background-keepalive.js # Anti-suspension keepalive techniques
│ └── app.js # Main application logic
- Chrome/Edge/Opera with Web Bluetooth support (Chrome 56+, Edge 79+)
- HTTPS connection (required for Web Bluetooth)
- Vagus nerve stimulator device with Nordic UART Service BLE support
A Node.js server (server.js) with auto-generated certificates and clickable terminal links:
# Start server (default port 8443)
node server.js
# Custom port (Unix/Mac)
PORT=9000 node server.js
# Custom port (Windows PowerShell)
$env:PORT=9000; node server.js
# Custom port (Windows CMD)
set PORT=9000 && node server.js
# Bind to specific IP
HOST=192.168.1.5 node server.jsThe server will display clickable links like:
📍 Localhost (this computer):
https://127.0.0.1:8443/
https://localhost:8443/
🌐 Network (other devices on same WiFi):
https://192.168.1.100:8443/ (WiFi)
https://10.0.0.15:8443/ (Ethernet)
Ctrl+Click (or Cmd+Click on Mac) any URL to open directly.
Terminal Support for Clickable Links: The server uses OSC 8 escape sequences to create clickable hyperlinks in supported terminals:
| Terminal | Support |
|---|---|
| iTerm2 (Mac) | ✅ Cmd+Click |
| Windows Terminal | ✅ Ctrl+Click |
| VS Code Terminal | ✅ Ctrl/Cmd+Click |
| GNOME Terminal | ✅ 3.26+ Ctrl+Click |
| Konsole | ✅ Ctrl+Click |
| Hyper | ✅ Ctrl/Cmd+Click |
If your terminal doesn't support OSC 8, URLs are still visible and can be copied.
# Using Python 3 (HTTP only - Web Bluetooth won't work)
python -m http.server 8443
# For HTTPS with Python, use a reverse proxy or:
python -m http.server 8443 &
# Then use ngrok or similar for HTTPS tunnel# Using Node.js http-server package
npx http-server -p 8443 -S -C cert.pem -K key.pem
# Using ngrok for public HTTPS URL
npx ngrok http 8080-
Open the URL in Chrome/Edge (HTTPS required for Web Bluetooth)
-
Click "Scan for Device" and select your device
| Command | ASCII | Purpose |
|---|---|---|
| Start bilateral | D\n |
Both channels active |
| Start left | A\n |
Left channel only |
| Start right | C\n |
Right channel only |
| Stop | -\n |
Deactivate (or 0\n legacy) |
| Intensity | 1\n to 9\n |
Set stimulation level |
| Battery query | Q\n |
Request battery voltage |
| Charging query | u\n |
Request charging status |
| Response | Format | Example |
|---|---|---|
| Battery | Batt:X.XX or X.XX |
Batt:3.72 |
| Charging | 0, 1, or text |
1 = charging |
| Strength ack | Single digit 1-9 |
5 |
| Start ack | Single char D |
D |
| Stop ack | Single char - |
- |
| Mode | Duration | Breathing | Pattern |
|---|---|---|---|
| Stress Relief | 10 min | No | Bilateral continuous |
| Sleep | 20 min | No | 5-phase rotation (D→A→D→C→D) |
| Focus | 15 min | No | 30s on/off duty cycle, left only |
| Pain Relief | 15 min | No | Sine wave ±1 oscillation |
| Calm | 10 min | Yes | 5s inhale, 5s hold, 7s exhale |
| Headache | 12 min | No | 2 min on / 30s off burst |
| Nausea | 10 min | No | Bilateral continuous |
| Meditation | 15 min | Yes | 5s inhale, 4s hold, 5s exhale |
| Feature | Chrome | Edge | Firefox | Safari |
|---|---|---|---|---|
| Web Bluetooth | ✅ 56+ | ✅ 79+ | ❌ | ❌ |
| Wake Lock API | ✅ 84+ | ✅ 84+ | ❌ | ❌ |
| Page Visibility | ✅ | ✅ | ✅ | ✅ |
| Service Worker | ✅ | ✅ | ✅ | ✅ |
- ASCII Protocol: All BLE commands are ASCII strings with newline terminator
- Session Clock: Wall-clock reconciliation handles background/foreground timing
- Mode Engines: State machines generate tick-based stimulation patterns
- Event-Driven: Components emit events for loose coupling
Web Bluetooth Limitation: Chrome aggressively suspends background tabs, including Bluetooth connections. This app implements 11 different keepalive techniques to resist suspension:
Keepalive Systems:
- Wake Lock API (screen on)
- Silent Audio Context (audio thread alive)
- Web Worker (isolated timer thread)
- BroadcastChannel (cross-tab pings)
- CSS Animation (layout thrashing)
- MessageChannel (microtask scheduling)
- Canvas Loop (rendering thread alive)
- Periodic Background Sync (scheduled wake)
- Persistent Notification (service worker alive)
- 250ms Aggressive Pinging (when hidden)
- beforeunload Warning (prevents accidental nav)
When tab goes to background:
- Warning banner appears immediately
- All 11 keepalive systems activate
- Device stops for safety (stop command sent)
- Wall-clock tracking continues (accurate timing)
- 250ms ping interval attempts to prevent full suspension
When returning to foreground:
- Warning banner removed
- Time recalculated from wall-clock
- Connection checked and re-established if needed
- Device reactivated with current session state
- All keepalive systems continue
Reality Check: Despite all countermeasures, Chrome will eventually suspend background tabs. The techniques increase survival time but cannot guarantee indefinite background operation.
- Web Bluetooth requires HTTPS (except localhost during development)
- User must explicitly grant permission for each device connection
- Device pairing is handled by the OS, not the web app
- No persistent storage of device identifiers
GLWTS (Good Luck With That Shit) Public License - See LICENSE file for details.
You can do whatever the fuck you want with this software at your OWN RISK. The author has no fucking clue what the code does, and you can never track them down to blame them.
- Web Bluetooth Specification
- Pulsetto Protocol Matrix
- Pulse Libre BLE Implementation
- Open Pulse iOS Reference
If you find this extension useful, then please support its continued development:
If you'd prefer to donate directly via cryptocurrency, you can send Bitcoin to:
bc1q8nrdytlvms0a0zurp04xwfppflcxwgpyrzw5hn
Thank you for supporting free and open source software! 🙏
Built with human creativity enhanced by artificial intelligence.