A gaze-driven on-screen keyboard for accessible, hands-free communication. Look at a letter, hold your gaze for a moment, and the key types itself. Built as an accessibility aid for users with limited motor control (ALS, late-stage Parkinson's, locked-in syndrome, etc.).
EyeLS deliberately uses a mix of technologies, with each piece doing what it does best.
| Layer | Tech | Where |
|---|---|---|
| Eye tracking | eyels.js (WebGazer-derived) + TensorFlow.js face mesh |
runs in the browser |
| Main UI (keyboard, calibration, gaze cursor) | Vanilla JavaScript + CSS | index.html, js/app.js, css/style.css |
| Web server + REST API | Node.js + Express | server.js |
| Typing-statistics engine | C++17 (compiled CLI) | tools/stats.cpp (built to tools/stats) |
| Stats dashboard | Angular (AngularJS 1.x via CDN) | stats.html, js/stats.js |
| Settings page | Vue 3 (via CDN) | settings.html, js/settings.js |
Data flow when you press Save on the keyboard:
Browser (vanilla JS)
│ POST /api/sessions
▼
Node.js (Express) ──────▶ data/sessions.json
▲
│ POST /api/stats ───▶ spawn ./tools/stats (C++) ──▶ JSON
│ │
Angular dashboard ◀────────────── fetch /api/sessions │
▼
stats rendered in
Angular templates
The Vue 3 settings page writes preferences (dwell time, cursor color, smoothing, etc.) to localStorage. The main app reads them on load and applies them to the gaze tracker.
EyeLS/
├── index.html ← landing / calibration / typing (vanilla JS)
├── stats.html ← Angular dashboard
├── settings.html ← Vue 3 settings
├── server.js ← Node.js Express server + REST API
├── eyels.js ← gaze-tracking library bundle
├── css/
│ ├── style.css ← shared design system + main app
│ └── pages.css ← stats + settings page styling
├── js/
│ ├── app.js ← eye-tracking, calibration, keyboard
│ ├── stats.js ← Angular controller
│ └── settings.js ← Vue 3 app
├── tools/
│ ├── stats.cpp ← native typing-statistics tool
│ └── Makefile
└── data/
└── sessions.json ← persisted typed sessions (created on first save)
- Node.js 18+ (tested on 22)
- A C++17 compiler (
clang++org++) — pre-installed on macOS with Xcode Command Line Tools, and on most Linux distros - A modern browser with webcam access (Chrome / Edge / Safari)
- A webcam and decent, even lighting
npm installThis installs Express and automatically compiles the C++ stats binary via the postinstall script. If your machine doesn't have a C++ compiler, the install will continue and warn you — you can build the binary later with:
npm run build:cppnpm startThe Node.js server boots on http://localhost:3000 and prints which subsystems are reachable.
- Open http://localhost:3000 in a modern browser.
- Grant camera permission when prompted.
- Click Get Started → Start.
- Look directly at the bright blue dot and click it 5 times. The calibration is sequential — one dot is highlighted at a time. Repeat for all 9 dots.
- The keyboard appears automatically when calibration completes.
- Look at a letter. After ~0.9s of dwell, the key fires.
- Hold your gaze to type the same key again.
- Speak reads your message aloud (uses the browser's speech synthesis).
- Clear wipes the textarea.
- Save posts the current text to the Node API for later analysis.
- Recalibrate returns you to the calibration screen if accuracy drifts.
Visit http://localhost:3000/stats (or click Stats from the keyboard topbar) to see every saved session. Click Analyze on any session and the Angular front-end calls POST /api/stats, which spawns the C++ binary to crunch word counts, top words, sentence count, average word length, and estimated reading time.
Visit http://localhost:3000/settings (or click Settings) to adjust:
- Dwell time — how long to hold your gaze before a key fires
- Cursor smoothing — how many samples to average (higher = smoother but laggier)
- Cursor responsiveness — how fast the cursor catches up to your eyes
- Cursor size / color — with a live preview
- Show webcam preview while typing
- Auto-save on Speak
All settings persist in localStorage and take effect on your next visit to the keyboard.
Better calibration = smoother typing.
- Keep your head still during calibration. Move only your eyes.
- Sit at a consistent distance from the camera (~50–70 cm works well).
- Click each dot deliberately, looking directly at it. Each click triggers a 600 ms multi-sample harvest, so quality matters more than speed.
- If accuracy drifts during use (e.g., you shifted in your chair), press Recalibrate.
| Method | Endpoint | Body | Purpose |
|---|---|---|---|
GET |
/api/health |
— | Server / stack status |
POST |
/api/stats |
{ "text": "…" } |
Crunch stats via C++ binary |
GET |
/api/sessions |
— | List saved sessions |
POST |
/api/sessions |
{ "text": "…" } |
Save a session |
GET |
/api/sessions/:id |
— | Fetch one |
DELETE |
/api/sessions/:id |
— | Delete one |
npm start # boot the Node.js server
npm run build:cpp # rebuild the C++ stats binary
npm run clean:cpp # remove the C++ binary