Capstone project. I'm ngl it's vibe-coded and it's only here so I can mess around with it
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove obsolete README

- Delete outdated project documentation
- Keep repository root focused on current app files

-188
-188
README.md
··· 1 - # UXET — UX Eye-Tracking Testing Framework 2 - 3 - UXET is a browser-based UX testing framework with integrated **WebGazer.js** eye tracking. Load any web app into a sandboxed iframe, define a task and a win condition, and UXET records mouse, keyboard, scroll, and gaze data — generating a heatmap when the test completes. 4 - 5 - --- 6 - 7 - ## Quick Start 8 - 9 - ```bash 10 - # Serve the project root (any static server works) 11 - npx http-server . -p 8080 12 - 13 - # Open http://localhost:8080 14 - ``` 15 - 16 - 1. Select an app from the dropdown 17 - 2. Click **Load** 18 - 3. Complete the 9-point eye-tracking calibration (or skip it with the invisible debug button at the top-right corner) 19 - 4. Read the task briefing, then click **Begin Testing** 20 - 5. Interact with the app — the test auto-stops when the **win condition** is met 21 - 6. View the debrief screen with stats and a gaze heatmap 22 - 23 - --- 24 - 25 - ## Adding a Test App 26 - 27 - ### 1. Create the app 28 - 29 - Place your app inside `testable-apps/`: 30 - 31 - ``` 32 - testable-apps/ 33 - my-app/ 34 - index.html 35 - ``` 36 - 37 - Your app is a normal HTML page. **No UXET-specific code is required.** 38 - 39 - ### 2. Register it in the dropdown 40 - 41 - Open `index.html` and add a `<div class="dropdown-item">` inside `#dropdown-menu`: 42 - 43 - ```html 44 - <div class="dropdown-item" 45 - data-value="testable-apps/my-app/index.html" 46 - data-task="Complete the signup flow" 47 - data-win="selector:.signup-success"> 48 - <span class="item-name">My App</span> 49 - <span class="item-task">Sign up</span> 50 - </div> 51 - ``` 52 - 53 - | Attribute | Description | 54 - |---|---| 55 - | `data-value` | Path to the app's entry HTML file | 56 - | `data-task` | Task description shown to the test participant | 57 - | `data-win` | Win condition that ends the test (see below) | 58 - 59 - --- 60 - 61 - ## Win Conditions 62 - 63 - Win conditions define **when the test automatically stops**. They are evaluated externally by UXET — the test app does not need any UXET-specific code. 64 - 65 - ### Syntax 66 - 67 - ``` 68 - data-win="strategy:value" 69 - ``` 70 - 71 - ### Available Strategies 72 - 73 - | Strategy | Syntax | What it detects | 74 - |---|---|---| 75 - | `selector` | `selector:<CSS selector>` | Element exists **and is visible** in the iframe | 76 - | `text` | `text:<substring>` | Substring appears in the iframe's visible text | 77 - | `url` | `url:<glob pattern>` | Iframe URL matches a glob pattern (`*` = wildcard) | 78 - | `postMessage` | `postMessage` | Iframe sends `{ type: 'UXET_TASK_COMPLETE' }` via `postMessage` | 79 - 80 - ### Examples 81 - 82 - ```html 83 - <!-- Fires when .checkout-success becomes visible --> 84 - data-win="selector:.checkout-success.active" 85 - 86 - <!-- Fires when "Order confirmed" appears on the page --> 87 - data-win="text:Order confirmed" 88 - 89 - <!-- Fires when the iframe navigates to a /success URL --> 90 - data-win="url:*/success*" 91 - 92 - <!-- Legacy: app explicitly signals completion --> 93 - data-win="postMessage" 94 - ``` 95 - 96 - ### Strategy Details 97 - 98 - **`selector:`** — Polls every 300ms for the CSS selector inside the iframe DOM. The element must be visible (not `display: none`, `visibility: hidden`, or `opacity: 0`). Best for single-page apps where a success state is reflected by a DOM change. 99 - 100 - **`text:`** — Polls every 500ms for a substring match in `document.body.innerText`. Best for detecting success messages, confirmation text, or any visible string. 101 - 102 - **`url:`** — Polls every 500ms. The glob pattern uses `*` as a wildcard. Best for multi-page flows where success means navigating to a specific URL. 103 - 104 - **`postMessage`** — Listens for `window.postMessage({ type: 'UXET_TASK_COMPLETE' })` from the iframe. The only strategy that works with **cross-origin** iframes. 105 - 106 - > **Cross-origin note:** `selector:` and `text:` require same-origin iframe access. If the iframe is cross-origin, use `url:` or `postMessage` instead. UXET will log a warning to the console if a DOM-based strategy fails due to CORS restrictions. 107 - 108 - --- 109 - 110 - ## Test Flow 111 - 112 - ``` 113 - Load App → Calibration → Task Briefing → Testing → Debrief + Heatmap 114 - ``` 115 - 116 - 1. **Load**: App is loaded into the iframe; WebGazer initializes the webcam (hidden) 117 - 2. **Calibration**: 9-point gaze calibration grid. Click each point 5 times. An advisory card explains the process first. 118 - 3. **Task Briefing**: Shows the task description (`data-task`). Participant clicks "Begin Testing" when ready. 119 - 4. **Testing**: Participant interacts with the app. Mouse, keyboard, scroll, and gaze events are recorded. The win condition watcher runs in the background. 120 - 5. **Debrief**: Shows session stats (time, clicks, keystrokes, scroll events, gaze points, fixations) and a rendered gaze heatmap. 121 - 122 - --- 123 - 124 - ## File Structure 125 - 126 - ``` 127 - UXET/ 128 - ├── index.html # Main UXET shell (dropdown, calibration, briefing, debrief) 129 - ├── index.css # All styles 130 - ├── js/ 131 - │ ├── main.js # App controller — flow, win conditions, heatmap 132 - │ ├── tracker.js # Mouse, keyboard, scroll event tracking 133 - │ ├── session.js # Session timer and state management 134 - │ └── gazeTracker.js # WebGazer.js wrapper — gaze data collection 135 - ├── testable-apps/ 136 - │ ├── shop-app/ 137 - │ │ └── index.html # ShopEasy store demo (win: checkout success) 138 - │ └── example-app/ 139 - │ └── index.html # Form demo (win: form submitted) 140 - └── README.md 141 - ``` 142 - 143 - ### Key Modules 144 - 145 - | Module | Responsibility | 146 - |---|---| 147 - | `main.js` (`UXETApp`) | Orchestrates the entire flow: app loading, calibration, testing, win conditions, heatmap rendering, data export | 148 - | `tracker.js` (`Tracker`) | Attaches to the iframe and records mouse position/clicks/distance, keyboard events, and scroll events | 149 - | `session.js` (`Session`) | Manages session lifecycle (start/stop/reset) and elapsed time | 150 - | `gazeTracker.js` (`GazeTracker`) | Initializes WebGazer.js, runs calibration, collects gaze coordinates mapped to iframe-relative positions, detects fixations | 151 - 152 - --- 153 - 154 - ## Heatmap 155 - 156 - When a test completes, UXET renders a gaze density heatmap on a `<canvas>` in the debrief screen: 157 - 158 - - Each gaze point is drawn as a **Gaussian splat** (radial gradient) 159 - - Intensity is normalized and colorized: **blue → cyan → green → yellow → red** 160 - - Only gaze points that fell within the iframe are included 161 - - A reference grid is drawn for spatial context 162 - 163 - --- 164 - 165 - ## Debug Skip 166 - 167 - For automated testing, an invisible 20×20px button at the top-right corner of the calibration overlay bypasses calibration entirely. It has `id="debug-skip-calibration"` with `opacity: 0`. 168 - 169 - --- 170 - 171 - ## Data Export 172 - 173 - Click **Export Data** on the debrief screen to download a JSON file containing: 174 - 175 - - Session metadata (app, task, duration) 176 - - All tracked events (mouse, keyboard, scroll) 177 - - Raw gaze data points with iframe-relative coordinates 178 - - Per-screen heatmap data 179 - - Aggregate statistics 180 - 181 - --- 182 - 183 - ## Dependencies 184 - 185 - - [WebGazer.js](https://webgazer.cs.brown.edu/) — loaded from jsDelivr CDN 186 - - [MediaPipe Face Mesh](https://google.github.io/mediapipe/) — loaded internally by WebGazer from jsDelivr CDN 187 - 188 - No build step, no npm install. Just serve and open.