# NeckQuest: PhysTech 2026 Hackathon Submission

**[🎮 Interactive demo](https://denpydev.github.io/NeckQuest/)** · **[🎬 Presentation video](https://denpydev.github.io/NeckQuest/video.html)** · **[GitHub](https://github.com/DenPyDev/NeckQuest)**

---

> **A Holter monitor for neck tension.**
> Wear one EMG patch on your left upper trapezius for a workday. Get a personal muscle-tension map. Then train the habit your data says you need, confirmed by the sensor, not a clock.

NeckQuest is an open-source **ESP32-S3 WiFi surface-EMG wearable** plus a **Python analysis server**. The device streams and records the electrical activity of the left upper trapezius (the muscle that quietly knots up during desk work) and classifies the muscle state as **relaxed** or **tense** using a personal machine-learning model calibrated to each user. It records a full-day Holter-style timeline of hidden neck/shoulder tension.

The project is local-first: no cloud, no account, no external backend. MIT-licensed.

---

## Contents

- [Try it without hardware](#try-it-without-hardware)
- [Judge quick start](#judge-quick-start)
- [What NeckQuest does](#what-neckquest-does)
- [The problem](#the-problem)
- [How the device works](#how-the-device-works)
- [Calibration and training](#calibration-and-training)
- [Live classification](#live-classification)
- [Holter / offline recording](#holter--offline-recording)
- [Data privacy](#data-privacy)
- [How to run the project](#how-to-run-the-project)
- [Video and demo](#video-and-demo)
- **Technical reference:** [Architecture](#architecture) · [Hardware](#hardware) · [Electrode placement](#electrode-placement) · [Firmware reference](#firmware-reference) · [Server reference](#server-reference) · [ADC noise notes](#adc-noise-notes)
- [Research basis](#research-basis)
- [Honest scope](#honest-scope)
- [What we built for the hackathon](#what-we-built-for-the-hackathon)
- [What's next](#whats-next)
- [Repository layout](#repository-layout)
- [License](#license)

---

## Try it without hardware

* 🎬 **[Watch the presentation video](video.html)** — walkthrough of the problem, device, and results.
* 🎮 **[Open the interactive demo](index.html)** — live recognition + Holter timeline running entirely in the browser (synthetic signal, no device or server needed).

Both pages are static and work on GitHub Pages.

---

## Judge quick start

```bash
cd NeckQuest/server
./run.sh
```

Then open:

* `http://127.0.0.1:8080` — server home
* `http://127.0.0.1:8080/app` — live oscilloscope
* `http://127.0.0.1:8080/classify` — calibration + live classifier
* `http://127.0.0.1:8080/holter` — Holter history viewer

No device is required for the static demo. Hardware mode uses the ESP32-S3 device on the local network.

---

## What NeckQuest does

A doctor who suspects an arrhythmia does not just check your pulse in the office; they fit a **Holter monitor** and let you live a normal day, because the problem is invisible and intermittent. NeckQuest applies the same idea to the neck and shoulders.

It does three things:

1. **Measure** the left upper-trapezius muscle with one EMG sensor.
2. **Recognize** whether the muscle is relaxed or tense in real time, using a model calibrated to each user.
3. **Record** a workday timeline to show when the muscle never got rest gaps.

![Live recognition — muscle relaxed](assets/images/live-relaxed.png)

*Live recognition: the left trapezius is at rest. The rolling RMS envelope sits below the personal threshold, so NeckQuest reports RELAXED.*

---

## The problem

Desk workers have a hidden problem: the **upper trapezius** never fully rests during a workday. It stays at a low, chronic activation level without the brief **rest gaps** that muscle tissue needs. The issue is not only tensing hard; it is the *absence* of short muscular rest gaps during the day.

Occupational EMG research links this no-rest pattern to neck/shoulder pain and productivity loss:

* A meta-analysis of **43,184 participants** found sedentary/screen behaviour associated with neck pain (OR ≈ 1.97 among workers).
  *(Meng et al. 2025, [PMID 39905389](https://pubmed.ncbi.nlm.nih.gov/39905389/))*
* Among **10,000 Japanese workers**, neck pain and stiff shoulders contributed to an estimated **$27 billion/year** in presenteeism losses.
  *(Yoshimoto et al. 2020, [PMC7537733](https://pmc.ncbi.nlm.nih.gov/articles/PMC7537733/))*
* Sustained low-level trapezius activity **> 4 min** is associated with future neck/shoulder pain; spending more than half the workday in such periods ≈ **3× risk**.
  *(Hanvold et al. 2013; Østensvik et al. 2009)*
* Recent work on bilateral upper-trapezius activation suggests that left/right asymmetry may independently contribute to neck-pain intensity.
  *(Koch et al. 2026)*

Timer apps fire on a fixed clock; they never see the muscle. NeckQuest is built on the opposite idea:

> **Measure first, then train based on what you actually find.**

Every claim above is backed by a citation, a verbatim excerpt, and a public verifiable link in the [Research basis](#research-basis) section.

---

## How the device works

**Hardware**

* ESP32-S3-N16R8 board (dual-core LX7 @ 240 MHz, 16 MB flash, 8 MB PSRAM)
* One analog surface-EMG sensor
* Active ADC channel: **GPIO10** (quietest ADC1 pin on this board)
* Target muscle: **left upper trapezius**, following the SENIAM electrode-placement standard
* Onboard WS2812 RGB LED for status at a glance
* Optional LiPo battery for untethered recording

**Firmware (dual-core)**

The firmware runs on FreeRTOS with two cores in parallel:

* **Core 0** samples the ADC at **500 Hz** and runs the EMG pipeline:

```text
raw ADC → centre at zero → full-wave rectify → rolling RMS (200 ms window) → normalise 0–1
```

* **Core 1** runs an HTTP server that streams `/live` frames, serves the device status page, and exposes the recording and config API.

**Status LED**

| LED colour | Pattern | State |
| ---------- | ------- | ----- |
| Yellow | 1 Hz blink | Connecting to WiFi |
| White | Dim solid | Idle, not recording |
| Green | 2 Hz blink | Recording |
| Blue | 2 Hz blink | Streaming: browser oscilloscope open |
| Red | Rapid blink | Error: WiFi lost after boot |

The device **always boots idle** and never auto-records. Recording only starts after a successful calibration, which guarantees you never collect a workday of uncalibrated, meaningless data.

**Server**

The Python server provides:

* device discovery on the local network
* live oscilloscope UI
* guided calibration
* EMG feature extraction
* personal SVM classifier
* Holter playback and timeline shading
* CSV/JSON access to recorded data

---

## Calibration and training

The classifier is **personal** because EMG amplitude depends on the user, electrode placement, skin contact, and posture.

Each session starts with two labelled recordings:

| Label | Position | Duration |
| ----- | -------- | -------- |
| **rest** | Head tilted gently to the left, left shoulder dropped, relaxed breathing | 20–30 s |
| **tense** | Left shoulder shrugged up + head rotated right, light resistance | 10–15 s |

The "rest" position (ipsilateral lateral flexion) mechanically shortens the muscle and resets motor-neuron drive, so even chronically tense workers can reach genuine relaxation.

The server extracts a **7-D EMG feature vector** per 200 ms window at 50% overlap, after a 50 Hz notch + Butterworth 20–225 Hz bandpass:

```text
RMS, MAV, WL, ZC, SSC, MDF, MNF
```

Then it trains a personal classifier:

```text
StandardScaler → SVC(kernel="rbf", C=10)
```

**Quality gate.** Calibration must pass before Holter recording starts. If the model cannot separate rest from tense well enough, the calibration is discarded and the user retries. This prevents collecting a full workday of meaningless uncalibrated data.

![Training / calibration flow](assets/images/training-flow.png)

*Guided calibration: record rest and tense examples, train the model, check accuracy, then continue only if the result is good enough.*

**Optional third label.** Standard seated desk posture produces ~2–3% MVE in the upper trapezius (well above the rest threshold of 0.5% MVE). Adding a "work" label lets the classifier distinguish three states and makes the Holter view richer. Record while typing or mousing normally for 30 seconds.

Recalibrate if you change electrode position or re-apply the patches.

---

## Live classification

After calibration, NeckQuest labels incoming EMG windows as **relaxed** or **tense** in real time.

The live page shows:

* raw/processed EMG trace
* rolling RMS envelope
* personal threshold band
* current state
* confidence readout

![Live recognition — muscle tense](assets/images/live-tense.png)

*Live recognition: the trapezius is contracted. The RMS envelope crosses the threshold and NeckQuest reports TENSE.*

You can try the same logic in the browser-only **[interactive demo](index.html)** under the *Live Classify* tab.

---

## Holter / offline recording

For a workday measurement, the device records to its own flash. It does not need a laptop connected all day.

The Holter log uses a custom format **NQH2** with **lossless delta compression**:

* Each record stores zig-zag + LEB128 varint deltas from the previous record, so slow-varying EMG RMS compresses to ≈2–6 bytes/record (~10× on quiet channels).
* A new 256 KB segment is opened as needed (up to 128 segments); the oldest is evicted when flash runs low, so recording can never "fill up and die".

After download, the Holter page shades every record:

* **green** = relaxed / rest gap
* **red** = tense / no rest gap

The clinically interesting signal is the *absence* of green rest gaps: long unbroken red stretches show sustained muscle activity with no recovery.

![Holter mode — recognized tension over time](assets/images/holter-recognized-data.png)

*Holter playback: red regions are sustained tension; green regions are muscular rest. The threshold can be adjusted and the timeline recomputed.*

Drag the threshold yourself in the **[interactive demo](index.html)** under the *Holter Timeline* tab.

---

## Data privacy

All data stays local.

* The ESP32 talks only to the Python server on the user's LAN.
* Calibration datasets are stored locally on the server.
* Holter logs stay on the device until downloaded.
* There is no cloud backend.
* There is no account system.
* There is no external analytics service.

---

## How to run the project

### 1. Flash the device

Set WiFi credentials in:

```text
NeckQuest/firmware/neckquest_s3/config.h
```

Then flash:

```bash
cd NeckQuest/firmware
./flash.sh
```

Default flashing port: `/dev/ttyACM0`. The first run downloads the toolchain (~200 MB) into `firmware/.tools/`; later runs are fast.

After boot, open `http://neckquest.local` for the device status page (or use the IP shown by the serial monitor).

### 2. Run the analysis server

```bash
cd NeckQuest/server
./run.sh
```

The server starts at `http://127.0.0.1:8080`. It auto-discovers the ESP32-S3 on the local network, or pass `--ip 192.168.1.8` to set a specific address.

### 3. Run tests

Server tests (offline, no device needed):

```bash
cd NeckQuest/server/tests
./run.sh
```

Firmware integration tests (require a live device):

```bash
cd NeckQuest/firmware/tests
./run.sh                           # device at 192.168.1.8
NECKQUEST_IP=192.168.1.5 ./run.sh  # custom IP
```

---

## Video and demo

* [`video.html`](video.html) — presentation video player
* [`index.html`](index.html) — interactive browser demo

Both are static pages; the demo synthesizes its signal in the browser with no backend required.

The demo includes three tabs:

* **Live Classify** — synthetic EMG signal + relaxed/tense recognition
* **Holter Timeline** — synthetic workday tension playback with adjustable threshold
* **How it works** — explanation of the EMG workflow

---

# Technical reference

> File paths below are relative to the `NeckQuest/` project directory (e.g. `firmware/neckquest_s3/config.h` means `NeckQuest/firmware/neckquest_s3/config.h`).

## Architecture

```
  ┌─────────────────────────────┐         WiFi / HTTP        ┌──────────────────────────────┐
  │  ESP32-S3 device            │ ◄────── (polled /live) ───► │  Python server (laptop)      │
  │                             │                             │                              │
  │  • dual-core FreeRTOS       │                             │  • aiohttp proxy + UI host   │
  │  • Core 0: 500 Hz ADC task  │                             │  • scikit-learn SVM          │
  │  • Core 1: HTTP server      │                             │    classifier (rest/tense)   │
  │  • rolling-RMS EMG pipeline │                             │  • holter history viewer     │
  │  • Holter recorder (flash)  │                             │  • reads the device channel  │
  │  • WS2812 status LED        │                             │                              │
  └─────────────────────────────┘                             └──────────────────────────────┘
```

**One fixed channel.** There is a single source of truth for the active ADC GPIO: the device's hardcoded channel, **GPIO10** (left upper trapezius). It is reported by the firmware and mirrored everywhere: live view, recording, history, and the classifier all read the same `channels` list. The channel is fixed in firmware and is not runtime-selectable.

* **Device = streamer + recorder.** It owns the ADC, the EMG pipeline, the Holter log, and the channel config. It serves one minimal status page; all rich UI lives on the server.
* **Server = UI + analysis.** It scans the LAN for the device, proxies its API, hosts the live/holter/classify pages, and runs the EMG classifier.

---

## Hardware

**Board: YD-ESP32-23** — DORHEA ESP32-S3-DevKitC-1 clone, 44-pin dual-row. ESP32-S3-N16R8: 16 MB NOR flash, 8 MB OPI PSRAM, dual-core LX7 @ 240 MHz. ([Amazon B0C9H7Y66W](https://www.amazon.com/DORHEA-Development-Bluetooth-ESP32-S3-DevKit-ESP32-S3-WROOM/dp/B0C9H7Y66W))

![NeckQuest hardware prototype](assets/images/device-prototype.png)

*The actual NeckQuest prototype: the ESP32-S3 board wired to the EMG front-end and bias divider, with electrode leads and snap electrode pads.*

| Component | Notes |
| --------- | ----- |
| ESP32-S3-N16R8 board | 16 MB flash / 8 MB PSRAM |
| 1× analog EMG sensor | e.g. Advancer Technologies Muscle Sensor V3 — analog SIG output |
| 10 kΩ resistors (×2) | Mid-supply bias divider — **required**, see [ADC noise notes](#adc-noise-notes) |
| LiPo + TP4056 (optional) | Battery + charger for untethered operation |
| Electrode patches | Double-sided medical tape or ECG gel pads |

### USB-C ports

| Port | Controller | Node | Use |
| ---- | ---------- | ---- | --- |
| Left | CH343P hardware UART | `/dev/ttyUSB0` | Serial monitor (survives flash) |
| Right | ESP32-S3 native USB-JTAG | `/dev/ttyACM0` | Flashing with `flash.sh` |

### GPIO pins hardwired internally (do not use)

| GPIO | Hardwired to |
| ---- | ------------ |
| 26–32 | NOR flash (Octal SPI) |
| 33–37 | OPI PSRAM (Octal SPI) |
| 19, 20 | Native USB D−/D+ |
| 43, 44 | UART0 TX/RX |
| 0 | BOOT button (strapping) |
| **48** | **Onboard WS2812 RGB LED** |

---

## Electrode placement

The target muscle is the **left upper trapezius (trapezius descendens)**: large, superficial, self-applicable, and the most studied muscle for desk-worker EMG biofeedback. The left (non-dominant) side is chosen because right-hand mouse use continuously activates the right trapezius with movement artifacts that contaminate the postural EMG baseline, making the left side cleaner.

Place electrodes following the [SENIAM](http://seniam.org/trapeziusdescendens.html) standard on the **left side**.

![Left upper trapezius — electrode positions](assets/images/electrode-placement.png)

*Red area: left upper trapezius muscle belly. Blue dots (A, B): signal electrodes on the muscle belly, 20 mm apart along fibres. Black dot (REF/GND): ground electrode on a bony, muscle-free area (C7 spinous process, acromion, or wrist).*

*3D model: [BodyParts3D](https://lifesciencedb.jp/bp3d/?shorten=q0XL9LfeS1PX0PrKTXSTL99P) © Database Center for Life Science (DBCLS), [CC BY-SA 2.1 JP](https://creativecommons.org/licenses/by-sa/2.1/jp/deed.en). Placement references: [emgguide.de — trapezius descendens](https://emgguide.de/?muscle=trapezius_descendens_l) · [SENIAM — trapezius descendens](http://seniam.org/trapeziusdescendens.html).*

| SENIAM spec | Value |
| ----------- | ----- |
| **Location** | 50% along the line from the acromion (tip of left shoulder) to the spine of C7 (base of neck) |
| **Orientation** | Along the acromion → C7 line |
| **Starting posture** | Erect sitting, arms hanging vertically |
| **Electrode size** | ≤ 10 mm in the fibre direction |
| **Inter-electrode distance** | 20 mm centre-to-centre (both electrodes on the muscle belly) |
| **Reference electrode** | Spinous process of C7, or right wrist |
| **Fixation** | Double-sided tape or adhesive rings |

**Wiring.** The Advancer Technologies Muscle Sensor V3 requires a dual ±9 V supply (two 9 V batteries, centre tap = sensor GND). Connect sensor GND to ESP32 GND; connect SIG to **GPIO10**.

**Anatomy.** Origin: external occipital protuberance, medial ⅓ of the superior nuchal line, ligamentum nuchae, and the C7 spinous process. Insertion: lateral ⅓ of the clavicle and the acromion. The descending fibres elevate and rotate the scapula; this is exactly the sustained low-level load that builds up during desk and phone use.

**Activation check.** Elevate the left shoulder while extending and rotating the head toward that shoulder (face turned the opposite way), then resist downward pressure on the shoulder; the band under the electrodes should tighten.

**Electrode note.** With ~4 cm adhesive patches and ~1 cm gel centres: the 20 mm inter-electrode distance is measured **centre-to-centre of the gel spots**, not patch edges. A slight shift of the pair toward the spine is fine; the trapezius belly is wide and a 1–2 cm medial offset stays on muscle.

> SENIAM also defines separate placements for trapezius *ascendens* and *transversalis*; this project uses *descendens* only.

---

## Firmware reference

### LED states

The onboard RGB LED (GPIO48, WS2812) reports state without a browser:

| LED | Pattern | State |
| --- | ------- | ----- |
| Yellow | 1 Hz blink | **BOOT** — connecting to WiFi |
| White | Dim solid | **IDLE** — connected, not recording |
| Green | 2 Hz blink | **RECORDING** |
| Blue | 2 Hz blink | **STREAMING** — browser oscilloscope open, not recording |
| Red | Rapid 5 Hz blink | **ERROR** — WiFi lost after boot |
| Off | - | Disabled via `POST /led {"enabled":false}` |

### ADC channel quality

GPIO10 was selected as the fixed EMG channel because in-project self-tests showed it and GPIO9 have the lowest baseline noise among all safe ADC1 pins on this board. The firmware internally ranks all safe ADC1 pins (visible via `/adc_pins`):

| Badge | Quality | Pins |
| ----- | ------- | ---- |
| ★ | best | GPIO9, GPIO10 |
| ✓ | good | GPIO5, GPIO7, GPIO8 |
| ~ | fair | GPIO4, GPIO6 |
| ⚠ | noisy | GPIO1, GPIO2 |

Excluded: **GPIO3** (strapping pin), **GPIO11–18** (ADC2, shared with WiFi), **GPIO19–20** (ADC2 + native USB).

### Signal pipeline

```
raw ADC (0–4095)
  − 2048              → centred at zero
  |x|                 → full-wave rectify
  rolling RMS         → 100-sample window (200 ms at 500 Hz)
  ÷ 2047.5            → normalised 0.0–1.0
```

`/live` `samples[].v[]` carries the centred value ÷ 2047.5 (range ±1) for the oscilloscope; `channels[].rms` carries the normalised 0–1 RMS.

### HTTP API

| Method | Path | Description |
| ------ | ---- | ----------- |
| GET | `/` | Minimal device status page |
| GET | `/live` | `{recording, sample_rate_hz, channels:[{slot,gpio,name,rms}], samples:[{v:[…]}]}` |
| GET | `/adc_pins` | Safe ADC GPIO list + quality ratings, excluded-pin reasons |
| POST | `/cmd` | `{"cmd":"start"|"stop"}` — start or stop recording |
| GET | `/status` | `{uptime_ms, unix_sec, wifi_ip, recording, free_bytes, total_bytes, record_count}` |
| GET | `/session/report` | `{record_count, session_s, free_bytes, total_bytes}` |
| GET | `/session/download` | CSV: `millis_ms,unix_sec,count,gpio0,rms0,…` |
| GET | `/session/records` | JSON rows paged via `offset`/`count` |
| DELETE | `/session` | Clear all stored Holter data |
| GET | `/adc_scan` | Raw scan of safe GPIOs (mean/stddev/min/max) |
| GET/DELETE | `/files` | LittleFS listing / delete-all |
| GET/POST | `/led` | Read LED state / toggle `{"enabled":bool}` |
| GET | `/device` | `{"device":"neckquest","ip":…}` — subnet discovery |

### Storage: delta-compressed rolling Holter log

**Format NQH2.** Each segment starts with an 18-byte header (magic, channel count, GPIO map, log rate, start unix + millis), then per-record bodies.

**Delta encoding.** The first record stores absolute RMS values; every later record stores per-channel **zig-zag + LEB128 varint deltas** from the previous record. Slow-varying EMG RMS compresses to **≈2–6 bytes/record** vs. 20 bytes raw (~10× on quiet channels).

**Rolling segments.** New segment every 256 KB (max 128). When free space drops below 320 KB the oldest segment is evicted: bounded flash use, no "overwrite death".

At 10 Hz logging, delta compression extends recording time well beyond the ~14 h that uncompressed records would allow on the 9.9 MB partition.

| Partition | Offset | Size |
| --------- | ------ | ---- |
| nvs | 0x9000 | 20 KB |
| otadata | 0xE000 | 8 KB |
| app0 | 0x10000 | 3 MB |
| app1 | 0x310000 | 3 MB |
| spiffs (LittleFS) | 0x610000 | 9.9 MB |
| coredump | 0xFF0000 | 64 KB |

### Flashing notes

`flash.sh` uses `partitions.csv` with the correct `spiffs` subtype. **Do not** use the Arduino IDE's "16M Flash" menu option: it creates a FAT partition incompatible with LittleFS.

Manual compile command:

```bash
arduino-cli compile \
  --fqbn "esp32:esp32:esp32s3:FlashSize=16M,FlashMode=qio,PartitionScheme=custom,PSRAM=opi,CDCOnBoot=cdc,UploadSpeed=921600" \
  --build-path /tmp/nq_build  firmware/neckquest_s3/
```

Key flags: `PartitionScheme=custom` · `CDCOnBoot=cdc` (Serial → USB-CDC) · `PSRAM=opi`.

---

## Server reference

`server/app.py` is an **aiohttp** application that:

* **Discovers** the device by probing `192.168.1.1–20` for `GET /device` returning `{"device":"neckquest"}`.
* **Hosts** the rich pages locally (`welcome.html`, `index.html`/`/app`, `holter.html`, `classify.html`) and **proxies** every other path to the device.
* **Works around an ESP32 chunked-encoding quirk** on `/session/download` with a raw-socket reader.
* **Caches `/live`** responses so the classifier loop reads samples without extra HTTP calls.

### Classifier (ml.py + emg.py)

A per-channel **support vector machine** classifier (scikit-learn `Pipeline`: `StandardScaler` → `SVC(kernel='rbf', C=10)`) over a **7-D EMG feature vector** (RMS, MAV, WL, ZC, SSC, MDF, MNF) computed on **200 ms windows at 50% overlap**, with a sample-space debounce/hysteresis. Filtering is a 50 Hz notch + causal 4th-order **Butterworth bandpass 20–225 Hz** (scipy), zero-phase for training data.

Server-local classify API: `GET /classify/status`, `POST /classify/cal` (`start`/`label`/`stop`), `POST /classify/train`, `DELETE /classify/model`, `GET /classify/models`, `POST /classify/model/save`, `POST /classify/model/load`, `GET /classify/datasets`.

Other server-local routes (not proxied to the device): `GET /version.json`, `GET /device/status` (reachability indicator), `POST /reconnect` (re-scan or set `{ip}` without restart), `GET /ws` (WebSocket push of `/live` frames at 20 Hz).

---

## ADC noise notes

A disconnected ADC pin is **not** a valid "zero signal": it is a high-impedance antenna charged by your hand, nearby wires, USB, flash/PSRAM activity, and WiFi current spikes.

**ESP32-S3 facts:** ADC1 = GPIO1–10, ADC2 = GPIO11–20. ADC2 is shared with WiFi (unsuitable for EMG while networking). GPIO19/20 are also native USB. **Use ADC1 only.**

**Required wiring: bias every channel to mid-supply.**

```text
3.3V ── 10kΩ ──┬── ADC GPIO
               │
             EMG SIG          (+ optional 10–100 nF from pin to GND, close to the chip)
               │
GND  ── 10kΩ ──┘
```

| Symptom | Likely cause |
| ------- | ------------ |
| Noisy only while floating | Normal — fix wiring/bias |
| Changes when WiFi starts | ADC2 pin, or long high-impedance wires |
| Changes when recording starts | Flash-write noise, rail droop |
| Both channels move together | Power/GND/reference coupling |
| Only one channel moves | That GPIO/wire/sensor or asymmetric routing |

---

## Research basis

NeckQuest is built on occupational-EMG research, not assumption. Every scientific claim is listed below with the **claim as used**, a **verbatim excerpt** from the paper, the **full citation**, and a **public link** so any reviewer can verify it.

### 1 · The problem is large

**Yoshimoto et al. 2020 — ~$27 billion/year presenteeism in Japan.**
*Claim:* Among 10,000 Japanese workers, neck pain and stiff shoulders were a leading cause of presenteeism, with national lost-productivity cost above **$27 billion/year**.
*Excerpt:* *"We conducted an Internet survey among 10,000 Japanese workers… The common health conditions most interfering with work were neck pain or stiff shoulders, low back pain, and mental illnesses… The estimated national costs for each were all above $27 billion."*
Yoshimoto T, Oka H, Fujii T, Nagata T, Matsudaira K. *The Economic Burden of Lost Productivity due to Presenteeism Caused by Health Conditions Among Workers in Japan.* J Occup Environ Med. 2020. — Free full text [PMC7537733](https://pmc.ncbi.nlm.nih.gov/articles/PMC7537733/) · DOI [10.1097/JOM.0000000000002001](https://doi.org/10.1097/JOM.0000000000002001)

**van den Heuvel et al. 2007 — productivity loss in symptomatic computer workers.**
*Claim:* In 654 computer workers with neck/shoulder symptoms, **26%** had measurable productivity loss, mostly reduced performance while present.
*Excerpt:* *"In 26% of all the cases reporting symptoms, productivity loss was involved, the most often in cases reporting both symptoms (36%)."*
van den Heuvel SG, IJmker S, Blatter BM, de Korte EM. *Loss of Productivity Due to Neck/Shoulder Symptoms and Hand/Arm Symptoms: Results from the PROMO-Study.* J Occup Rehabil. 2007. — Free full text [PMC2039785](https://pmc.ncbi.nlm.nih.gov/articles/PMC2039785/) · DOI [10.1007/s10926-007-9095-y](https://doi.org/10.1007/s10926-007-9095-y)

**Meng et al. 2025 — sedentary/screen behaviour and neck pain.**
*Claim:* A meta-analysis of **43,184 participants** found sedentary/screen behaviour associated with neck pain (OR ≈ 1.97 among workers).
PubMed [PMID 39905389](https://pubmed.ncbi.nlm.nih.gov/39905389/)

### 2 · The hidden pattern: absence of muscular rest gaps

**Koch et al. 2024 (PLOS ONE) — breaks, muscular rest, and sustained activity.**
*Claim:* In a pooled Scandinavian dataset of **731 workers**, break frequency, amount of muscular rest, and sustained activity are all related to neck pain. "Muscular rest" is defined as activity below **0.5% of maximal voluntary contraction (MVC)**.
*Excerpts:* *"Electromyographic (EMG) data for the upper trapezius muscle, collected during working hours, were coupled with questionnaire responses… for a total of 731 subjects."* · *"In most studies, 'muscular rest' is defined as very low muscular activity, typically falling below 0.5% of the highest level achieved during maximal voluntary contraction of the trapezius…"* · *"it is crucial to distribute work breaks throughout the workday."*
Koch M, Forsman M, Enquist H, et al. *Frequency of breaks, amount of muscular rest, and sustained muscle activity related to neck pain in a pooled dataset.* PLoS ONE 19(6):e0297859. 2024. — Open access [PLOS ONE full text](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0297859) · [PMC11198897](https://pmc.ncbi.nlm.nih.gov/articles/PMC11198897/) · DOI [10.1371/journal.pone.0297859](https://doi.org/10.1371/journal.pone.0297859)

### 3 · Duration thresholds that predict pain

**Hanvold et al. 2013 (SJWEH) — sustained activity > 4 min.**
*Claim:* Sustained upper-trapezius activity **> 4 min** is associated with future neck/shoulder pain; spending more than half of work time in such periods ≈ **3× risk**.
SJWEH [abstract 3357](https://www.sjweh.fi/show_abstract.php?abstract_id=3357&fullText=1)

**Østensvik et al. 2009 — sustained low-level activity > 8 min.**
*Claim:* Sustained low-level muscle activity (SULMA) periods **> 8 min** are associated with neck pain one year later, OR ≈ **3.0**.
PubMed [PMID 19941187](https://pubmed.ncbi.nlm.nih.gov/19941187/)

### 4 · Why the left (non-dominant) upper trapezius

The upper trapezius is the single best-supported muscle for desk-worker EMG biofeedback, with a defined SENIAM placement standard. The left side is chosen for the single-sensor version because right-hand mouse use continuously activates the right trapezius with movement components that contaminate the postural EMG baseline.
References: Koch 2024 (§2) · Dellve 2011 (§5) · [SENIAM — trapezius descendens](http://seniam.org/trapeziusdescendens.html) · [emgguide.de](https://emgguide.de/?muscle=trapezius_descendens_l)

### 5 · The pattern can be changed

**Dellve et al. 2011 — myofeedback vs. strength training, 60 women on sick leave.**
*Claim:* In 60 women on long-term sick leave with chronic neck pain, both myofeedback and strength training reduced pain and improved work ability.
*Excerpts:* *"The theoretical framework is that muscle tension in the neck is related to insufficient muscular rest and is a risk factor for chronic pain and reduced work ability."* · *"For both intervention groups, pain was lowered over time compared with the control group."*
Dellve L, Ahlström L, Jonsson A, et al. *Myofeedback training and intensive muscular strength training to decrease pain and improve work ability…* Int Arch Occup Environ Health. 2011. — Free full text [PMC3037486](https://pmc.ncbi.nlm.nih.gov/articles/PMC3037486/) · DOI [10.1007/s00420-010-0568-5](https://doi.org/10.1007/s00420-010-0568-5)

**Lidegaard et al. 2013 — 2 min/day resistance training.**
*Claim:* 30 office workers, **2 min/day** elastic-resistance training for 10 weeks → pain reduced ~**40%**, EMG rest-gap duration and frequency increased.
Biomed Res Int. 2013;2013:262386. — Free full text [PMC3892746](https://pmc.ncbi.nlm.nih.gov/articles/PMC3892746/) · DOI [10.1155/2013/262386](https://doi.org/10.1155/2013/262386)

**Johnston et al. 2021 — cluster-RCT, 740 office workers.**
*Claim:* Exercise + ergonomics reduced neck-pain intensity at 12 weeks across 14 organisations.
BMC Musculoskelet Disord [article 03945-y](https://link.springer.com/article/10.1186/s12891-021-03945-y)

### 6 · Biofeedback as a class of solution: honest caveats

**Voerman et al. 2007 — ambulant myofeedback RCT, 79 computer workers.**
*Claim:* Myofeedback + ergonomics vs. ergonomics alone both improved pain and disability, with **no clear superiority** for myofeedback.
*Excerpt:* *"Pain intensity and disability significantly reduced after both interventions and this effect remained at follow-up. No differences were observed between the two intervention groups."*
Voerman GE et al. *Effects of ambulant myofeedback training and ergonomic counselling in female computer workers…* J Occup Rehabil. 2007. — DOI [10.1007/s10926-007-9066-3](https://doi.org/10.1007/s10926-007-9066-3) · PubMed [PMID 17680350](https://pubmed.ncbi.nlm.nih.gov/17680350/)

**Aegerter et al. 2023 (NEXpro) — multi-component intervention, productivity.**
*Claim:* A multi-component neck intervention cut neck-pain-related work productivity loss by **2.8 percentage points**, saving **CHF 27.40 per participant per week**.
*Excerpt:* *"We found an effect of our multi-component intervention on neck pain-related work productivity loss, with a marginal predicted mean reduction of 2.8 percentage points (b = −0.27; 95% CI: −0.54 to −0.001, p = 0.049). Weekly saved costs were Swiss Francs 27.40 per participant."*
Aegerter AM et al. (NEXpro). *Effectiveness of a multi-component intervention on neck pain-related work productivity loss…* J Occup Rehabil. 2023;33(2):288–300. — Free full text [PMC9514678](https://pmc.ncbi.nlm.nih.gov/articles/PMC9514678/) · DOI [10.1007/s10926-022-10069-0](https://doi.org/10.1007/s10926-022-10069-0)

**Campo et al. 2021 & Kamonseki et al. 2021 — biofeedback evidence is mixed.**
*Claim:* Across reviews (Campo: 15 RCTs / 990 participants; Kamonseki: 5 RCTs / 272 participants), biofeedback evidence is **mixed**: short-term disability gains, no proven superiority for pain or work ability. This is why NeckQuest must *prove* added value in a pilot rather than assume it.
Campo [PMID 33461043](https://pubmed.ncbi.nlm.nih.gov/33461043/) · Kamonseki [10.1177/0269215521990950](https://journals.sagepub.com/doi/10.1177/0269215521990950)

### 7 · Future direction: bilateral asymmetry

**Koch et al. 2026 (PLOS ONE) — asymmetric upper-trapezius activation.**
*Claim:* Asymmetric low-level upper-trapezius activation is associated with neck-pain intensity even at similar absolute bilateral levels, motivating a future two-sensor version.
*Excerpts:* *"Seven research institutes provided data on bilateral upper trapezius muscle activity on one working day, along with corresponding questionnaire data on cross-sectional (n = 530) and longitudinal (n = 256) neck pain intensity."* · *"In cross-sectional analyses, asymmetry in the levels 0–0.05 and 0.05–2%MVIC was significantly positively associated with neck pain intensity in both unadjusted and adjusted analyses."*
Koch M, Fan X, Andersen LL, et al. *Is asymmetric upper trapezius muscle activation during work associated with neck pain?* PLoS One 21(6):e0349265. 2026. — Open access DOI [10.1371/journal.pone.0349265](https://doi.org/10.1371/journal.pone.0349265) · PMID [42284355](https://pubmed.ncbi.nlm.nih.gov/42284355/)

**Merkus et al. 2021 — arm elevation + trapezius activity cohort.**
*Claim:* Prospective cohort (n=118) relating trapezius load to the 2-year course of neck/shoulder pain intensity; time spent at low-level activity (0.5–7.0%MVE) showed the most certain association.
*Excerpt:* *"Time spent in 0.5–7.0%MVE showed the largest and most certain association with changes in NSPi during follow-up (β = −0.13; p = 0.037…)."*
Merkus SL et al. *Can a metric combining arm elevation and trapezius muscle activity predict neck/shoulder pain?* Int Arch Occup Environ Health. 2021. — Free full text [PMC8068682](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8068682/) · DOI [10.1007/s00420-020-01610-w](https://doi.org/10.1007/s00420-020-01610-w)

These studies do not mean NeckQuest is a medical treatment. They motivate the measurable pattern: sustained low-level trapezius activity, insufficient rest gaps, and potentially left/right asymmetry.

---

## Honest scope

NeckQuest targets a **measurable occupational risk pattern**: prolonged low-level upper-trapezius activity without muscular rest gaps. It is a **prevention, training, and wellbeing prototype, not a medical device.**

| We claim | We do not claim |
| -------- | --------------- |
| NeckQuest measures a real, studied EMG pattern | It diagnoses or treats disease |
| Rest gaps are measurable with a low-cost single sensor | It equals clinical biofeedback |
| Sustained no-rest activity is a studied risk pattern | Pain will drop by a fixed percent |
| A personal classifier can separate relaxed vs. tense states | It replaces clinical EMG |
| The prototype works locally with open-source code | It is ready as a certified wearable |

The correct pilot endpoint is not "pain cured"; it is whether users can increase muscular rest gaps and reduce sustained tension episodes during real workdays.

---

## What we built for the hackathon

For PhysTech 2026 we built:

* ESP32-S3 firmware with dual-core FreeRTOS
* one-channel EMG signal pipeline (500 Hz ADC, rolling RMS, normalisation)
* local HTTP device API (57 integration tests)
* on-device Holter recording with delta compression (NQH2 format)
* Python analysis server (aiohttp)
* guided calibration workflow
* personal SVM classifier (scikit-learn)
* live relaxed/tense recognition with confidence readout
* Holter timeline playback with adjustable threshold
* 16-slide static presentation
* interactive browser demo (no backend needed)
* screenshots and project documentation
* research-backed README with verbatim citations

---

## What's next

The next hardware step is adding a **second EMG channel** for the right upper trapezius.

That would let NeckQuest compare both shoulders during a real workday and show:

* left/right activation imbalance
* mouse-arm overload patterns
* asymmetric posture habits
* bilateral rest-gap patterns

Research (Koch et al. 2026) suggests that asymmetry in upper-trapezius activation may independently relate to neck-pain intensity, so bilateral tracking is a natural next step.

Other future directions:

* improve wearable packaging and sensor attachment
* collect real workday recordings across more users
* improve coaching based on detected tension patterns
* add session summaries after Holter downloads

---

## License

[MIT](NeckQuest/LICENSE) © 2026 NeckQuest contributors.
