{"id":19489426,"url":"https://github.com/1j01/tracky-mouse","last_synced_at":"2026-04-01T20:53:24.477Z","repository":{"id":52605222,"uuid":"328041186","full_name":"1j01/tracky-mouse","owner":"1j01","description":"Mouse control via head tracking, as a cross platform desktop app and JS library. eViacam alternative.","archived":false,"fork":false,"pushed_at":"2026-02-05T01:01:30.000Z","size":16324,"stargazers_count":56,"open_issues_count":78,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-02-05T05:44:22.871Z","etag":null,"topics":["accessibility","clmtrackr","dwell-clicking","electron-app","facemesh","facial-mouse","hands-free","hci","head-mouse","head-movement","head-tracker","head-tracking","optical-flow"],"latest_commit_sha":null,"homepage":"https://trackymouse.js.org/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1j01.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":"https://www.paypal.me/isaiahodhner"}},"created_at":"2021-01-09T00:17:58.000Z","updated_at":"2026-02-04T21:09:36.000Z","dependencies_parsed_at":"2024-10-20T16:17:35.459Z","dependency_job_id":null,"html_url":"https://github.com/1j01/tracky-mouse","commit_stats":{"total_commits":211,"total_committers":1,"mean_commits":211.0,"dds":0.0,"last_synced_commit":"8c42fe895f74f1d9be82cc706c7d95df24cddfdf"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/1j01/tracky-mouse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Ftracky-mouse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Ftracky-mouse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Ftracky-mouse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Ftracky-mouse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1j01","download_url":"https://codeload.github.com/1j01/tracky-mouse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Ftracky-mouse/sbom","scorecard":{"id":2146,"data":{"date":"2025-08-11","repo":{"name":"github.com/1j01/tracky-mouse","commit":"2aad9b851bfa5c86414a84fe273e1997ea4a0952"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.2.0 not signed: https://api.github.com/repos/1j01/tracky-mouse/releases/191269209","Warn: release artifact v1.1.0 not signed: https://api.github.com/repos/1j01/tracky-mouse/releases/180892147","Warn: release artifact v1.2.0 does not have provenance: https://api.github.com/repos/1j01/tracky-mouse/releases/191269209","Warn: release artifact v1.1.0 does not have provenance: https://api.github.com/repos/1j01/tracky-mouse/releases/180892147"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":0,"reason":"25 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-p7v2-p9m8-qqg7","Warn: Project is vulnerable to: GHSA-7x97-j373-85x5","Warn: Project is vulnerable to: GHSA-7m48-wc93-9g85","Warn: Project is vulnerable to: GHSA-qqvq-6xgj-jw8g","Warn: Project is vulnerable to: GHSA-6r2x-8pq8-9489","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-px4h-xg32-q955","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T12:48:19.006Z","repository_id":52605222,"created_at":"2025-08-14T12:48:19.006Z","updated_at":"2025-08-14T12:48:19.006Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29436177,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T03:34:37.767Z","status":"ssl_error","status_checked_at":"2026-02-14T03:34:09.092Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessibility","clmtrackr","dwell-clicking","electron-app","facemesh","facial-mouse","hands-free","hci","head-mouse","head-movement","head-tracker","head-tracking","optical-flow"],"created_at":"2024-11-10T21:08:32.148Z","updated_at":"2026-04-01T20:53:24.460Z","avatar_url":"https://github.com/1j01.png","language":"JavaScript","funding_links":["https://www.paypal.me/isaiahodhner"],"categories":[],"sub_categories":[],"readme":"# ![](./images/tracky-mouse-logo-32.png) Tracky Mouse\n\n\u003e Control your computer by moving your head.\n\nTracky Mouse is a desktop application providing **hands-free universal computer access**.\n\nIt's also embeddable in web applications as a JavaScript library. See the [API docs](./core/README.md).\n\nFeatures include:\n- [x] Move your head to move the mouse pointer.\n- [x] Dwell to click.\n- [x] Blink to click mode (desktop app only).\n- [x] Open mouth to click mode (desktop app only). This provides **three-button mouse** functionality using closed eyes as modifiers.\n- [x] The screen overlay provides visual feedback for dwell clicking and facial gestures at your cursor.\n- [x] Settings for sensitivity, acceleration, running at login, and more.\n- [x] Moving the mouse manually (with a physical mouse or touchpad) automatically pauses control.\n\nBy building it as a desktop app *and* an embeddable web UI, users can try it out right away in their browser, and then install the desktop app for full computer control.\n\u003c!-- Building Tracky Mouse as a desktop app *and* an embeddable web UI means you can try it out right away in their browser, and then install the desktop app for full computer control. --\u003e\n\n\u003c!--\nUsers will even be able to share settings between the embedded web UI and desktop app.\nI also have plans for a browser extension https://github.com/1j01/tracky-mouse/issues/27\nmaking this a three-in-one project: desktop app, JavaScript library, and browser extension.\nSettings could be shared between all three products (with import/export, which is already implemented in the desktop app, and possibly cloud syncing, but also just through familiarity with the settings UI).\n--\u003e\n\n[✨👉 **Try out the Demo!** 👈✨](https://trackymouse.js.org/)\n\n## Install Desktop App\n\n- [⬇️ Download for Windows (.exe installer)](https://github.com/1j01/tracky-mouse/releases/download/v2.6.0/Tracky.Mouse-2.6.0.Setup.exe) and run the installer.\n  - If you get a SmartScreen warning, click \"More info\" and then \"Run anyway\".\n- [⬇️ Download for Windows (.msix)](https://github.com/1j01/tracky-mouse/releases/download/v2.6.0/Tracky.Mouse.msix)\n  - After downloading the `.msix` file, right click on it, select Properties, go to the Digital Signatures tab, select the embedded signature from the list, click Details, click View Certificate, and install the certificate. Then double click the `.msix` file. Then click \"Restart as administrator\" to restart the installer (you do not need to restart your computer, don't worry). Say Yes to allow App Installer to make changes to the device. Then click Install.\n- [⬇️ Download for Linux (.AppImage)](https://github.com/1j01/tracky-mouse/releases/download/v2.6.0/Tracky.Mouse-2.6.0-x64.AppImage)\n  - You may need to make it executable first by right-clicking the file, selecting \"Properties\", going to the \"Permissions\" tab, and checking \"Allow this file to run as a program\" or \"Is executable\" or similar (depending on your file manager). Then you can double-click it to run it. See [How to run an AppImage](https://discourse.appimage.org/t/how-to-run-an-appimage/80) for more details.\n- [⬇️ Download for Linux (.deb)](https://github.com/1j01/tracky-mouse/releases/download/v2.6.0/tracky-mouse_2.6.0_amd64.deb)\n- [⬇️ Download for Linux (.rpm)](https://github.com/1j01/tracky-mouse/releases/download/v2.6.0/tracky-mouse-2.6.0-1.x86_64.rpm)\n\nPre-built binaries are not yet available for macOS, due to a couple issues: [camera permissions](https://github.com/1j01/tracky-mouse/issues/119), and [the more powerful clicking modes not clicking properly](https://github.com/1j01/tracky-mouse/issues/102).\nYou *can* still run the app on macOS, if you follow the [Development Setup](#development-setup) instructions.\n\n## Usage Guide\n\nThese instructions apply to using the desktop app or the web UI.\n\n### Set up your camera and environment:\n- Make sure to have **good lighting** on your face. Placing a lamp beside your monitor can help a lot!\n- Back-lighting can be problematic, especially if your head moves in and out from occluding the light during use, but also due to glare.\n- Your webcam should be centered in front of your head, with your head fully visible when sitting comfortably.\n\n### Start using Tracky Mouse:\n- Press the \"Start\" button to start moving the mouse and clicking. You can also use the keyboard shortcut \u003ckbd\u003eF9\u003c/kbd\u003e. When using the desktop app, this shortcut works even when the app is not in focus.\n- Dwell in one spot to click. To avoid clicking, you have to keep moving your head, or pause the app with \u003ckbd\u003eF9\u003c/kbd\u003e.\n- (Desktop app only) Try changing **Clicking mode** to **Wink to click** or **Open mouth to click** in the settings. \n  - With **Wink to click**:\n    - Close your right eye to left click\n    - Close your left eye to right click.\n    - If this feels backwards, you can enable \"Swap mouse buttons\"\n  - With **Open mouth to click**:\n    - Close your right eye and open your mouth to middle click\n    - Close your left eye and open your mouth to right click\n    - Open your mouth with both eyes open to left click.\n\n### General usage tips:\n- Adjust the settings until you can comfortably move the mouse to the edges of the screen with some accuracy.\n- Advice for point tracking mode:\n  - If the mouse cursor feels off-center, you can recalibrate by simply moving your head past where the cursor meets the edge of the screen.\n  - Note that not only rotating your head, but translating your head (moving it left/right, up/down, or forward/backward) moves the mouse.\n    - One nuance to this is, if the camera is positioned above your head, leaning forward generally moves the pointer down, whereas if the camera is below your head, leaning forward generally moves the pointer up.\n- Using the **Tilt influence** slider: This setting lets you blend between using 2D point tracking and 3D head tilt.\n  - At 0% it will use only point tracking. This moves the cursor according to visible movement of 2D points on your face, so it responds to both head rotation and translation. It's very accurate and responsive, but can get out of sync with your head orientation over time, requiring you to recenter by pushing the cursor to the edge of the screen.\n    - Recommended: high acceleration\n  - At 100% it will use only head tilt. This uses an estimate of your face's rotation in 3D space, and ignores head translation. Note that this signal is smoothed (as it's very jittery otherwise), so it's not as responsive as point tracking. In this mode you never need to recenter by pushing the cursor to the edge of the screen, but you do need to calibrate it in the **Head tilt calibration settings** section first.\n    - Acceleration does not apply, as movement is absolute based on head tilt.\n  - In between it will behave like an automatic calibration, subtly adjusting the point tracking to match the head tilt. This works by slowing down mouse movement that is moving away from the position targeted based on the head tilt, and (only past 80% on the slider) actively moving towards it. \n    - Recommended: medium point tracking acceleration; point tracking sensitivity should roughly match head tilt sensitivity.\n- **Head tilt calibration settings:** You can adjust the horizontal and vertical tilt range and offset. This allows the head tilt feature to be used with different camera placements (above or below the screen) and postures, and lets you balance comfort+speed and precision.\n\n### Troubleshooting:\n- If you have multiple cameras, make sure to select the correct one under **Video \u003e Camera source**.\n- If the camera feed appears black:\n  - Make sure there is no privacy/dust cover on the camera.\n  - Ensure there's enough light.\n  - Check the camera in another application to make sure it's working.\n  - Resuming from sleep/hibernate can also cause this (see [issue #77](https://github.com/1j01/tracky-mouse/issues/77)). Try restarting the app.\n- If the camera can't be accessed at all, make sure it's not being used by another application, then click \"Allow Camera Access\" in the app. Also try unplugging the camera and plugging it in again (if it's an external camera), or restarting your computer.\n  - On Linux: Installing (and maybe running?) `guvcview` can magically fix a webcam not showing up. ([source](https://forums.linuxmint.com/viewtopic.php?t=131011))\n  - On Windows 11, it's possible to allow multiple apps to access the camera at once.\n    - Go to **Settings \u003e Bluetooth \u0026 devices \u003e Camera**, select your camera, and in **Advanced camera options**, enable **Allow multiple apps to use camera at the same time**. (You'll need to stop any apps currently using the camera first.)\n- Auto-focus and auto-brightness can cause head tracking disruptions. Consider disabling auto-focus on your camera, and adjusting focus manually. If you disable auto-brightness, you will have to adjust the brightness regularly as the lighting changes, at least assuming you have any natural light in the room.\n  - Advanced camera settings can be accessed with **Video \u003e Open Camera Settings** in the desktop app on Windows.\n\n### Integrating with external software\nTracky Mouse comes with a command-line interface (CLI) which can be used to control the desktop app with a voice command system or other external programs. See [CLI documentation](./CLI.md) for usage.\n\n\n## Add to your project\n\nTracky Mouse is available on npm:\n```sh\nnpm install tracky-mouse\n```\n\nRead the [API documentation](./core/README.md) for more information.\n\n## License\n\nMIT-licensed, see [LICENSE.txt](./LICENSE.txt)\n\n## Changelog\n\nSee [CHANGELOG.md](./CHANGELOG.md) for project history and API changes.\n\n## Why did I make this?\n\nSomeone emailed me asking about how they might adjust the UI of [JS Paint](https://jspaint.app/) to work with eye tracking (enlarging the color palette, hiding other UI elements, etc.)\nand I decided to do them one better and build it as an official feature, with dwell clicking and everything.\n\nTo test these accessibility features properly, I needed a facial mouse, but eye trackers are expensive, so I tried looking for head tracking software, and found eViacam, but... either it didn't work, or at some point it stopped working on my computer.\n\n- eViacam wasn't working on my computer.\n- I didn't find there to be very many facial mouse software options out there, especially cross-platform, and I want people to have options.\n- I wanted people to be able to try JS Paint's dwell clicking out easily, and an embeddable facial mouse GUI would be great for that.\n- I've had some joint pain issues in the past (although also neck pain, which is a bit ironic)\n- I think I can push forward the state of the art in facial mouse software.\n\n## Software Architecture\n\nThis is a monorepo containing packages for the library (`core`), the desktop app (`desktop-app`), and the website (`website`).\n\n\nI tried npm workspaces, but it doesn't work with Electron Forge packaging. See [electron/forge#2306](https://github.com/electron/forge/issues/2306).\n\n### Core\n\nThe core library uses the following third-party libraries:\n\n- [jsfeat](https://github.com/inspirit/jsfeat) for point tracking (using Lucas–Kanade optical flow)\n\t- [MIT License](https://github.com/inspirit/jsfeat/blob/master/LICENSE)\n- [clmtrackr.js](https://github.com/auduno/clmtrackr) for fast and lightweight but inaccurate face tracking\n\t- [MIT License](https://github.com/auduno/clmtrackr/blob/dev/LICENSE.txt)\n- [facemesh](https://www.npmjs.com/package/@tensorflow-models/face-landmarks-detection) and [TensorFlow.js](https://www.tensorflow.org/) for accurate face tracking (once this loads, it stops using clmtrackr.js)\n\t- [tfjs-models: Apache License 2.0](https://github.com/tensorflow/tfjs-models/blob/master/LICENSE)\n\t- [TensorFlow: Apache License 2.0](https://github.com/tensorflow/tensorflow/blob/master/LICENSE)\n\nSome dependencies are versioned with npm and copied into `core/lib/` with `npm run in-core -- npm run copy-deps`\n\nOthers are just stored in `core/lib/` without npm versioning.\n\n#### No eval\n\nTo avoid the need for `unsafe-eval` in the Content Security Policy, I had to eliminate the use of `eval` (and `Function` construction) in `clmtrackr.js`.\n\nThe file [no-eval.js](./core/lib/no-eval.js) overrides `eval` with a function that handles the specific cases of `eval` usage in `clmtrackr.js`.\nI made a tool to generate this file by running `clmtrackr.js` while instrumenting `eval` to collect the code it tries to evaluate.\nThis tool is located in [eval-is-evil.html](./website/eval-is-evil.html).\n\nUnfortunately, when upgrading the facemesh library, I had to add back the `unsafe-eval` requirement, as it uses WebAssembly.\n\nWebAssembly is not the same as `eval`, but browsers grouped them together under the same CSP directive.\nEven if browsers widely support the more fine-grained `wasm-unsafe-eval`, old browsers would still be blocked from using the library if `unsafe-eval` is not included in the CSP.\n\n### Website\n\nThe website uses symlinks to reference the library (`core`) and shared resources (`images`) during development.\n\nWhen deploying with `npm run in-website -- npm run deploy`, it will prompt when there are any new files not that are not defined as included or excluded in [website/globs-for-deploy.js](./website/globs-for-deploy.js).\n\nIt will then be deployed to GitHub Pages using the [`gh-pages`](https://www.npmjs.com/package/gh-pages) npm package.\n\nDeploys can be rolled back by force-pushing to the `gh-pages` branch. The command `npm run in-website -- npm run deploy:rollback` rolls back the last deploy.\n\n\n### Desktop App\n\nThe desktop application's architecture is kind of *amusing*...\n\nI will explain. First, some groundwork. Electron apps are multi-process programs. They have a main process, which creates browser windows, and renderer processes, which render the content of the browser windows.\n\nIn this app, there are two renderer processes, one for the main application window, and one for a screen overlay window.\n\nThe overlay window is transparent, always-on-top, and intangible. It's used to preview dwell clicks with a shrinking circle.\n\nNow we get to the good stuff...\n\nIn a \"sane\" architecture, the overlay window, which can't receive any input directly, would be purely a visual output. The state would be kept in either the main process or the main renderer process, and it would only send messages to the overlay to draw the circle.\n\nBut I already had code for the dwell clicker, you see. I want it to behave similarly between the library and the desktop app, so I want the same timing logic and circle drawing to work in both.\n\nKeeping the state in a separate process from where the circle is rendered would mean tearing apart and rewriting my code for the dwell clicker.\n\nSo instead I simply embed the dwell clicker into the screen overlay window, business logic and all.\nIt was already going to be an entire webpage just to render the circle, since this is Electron.\nIt was never going to be efficient.\n\nSo I ended up with an architecture where the **application window controls mouse movement**, and the **screen overlay window controls mouse clicking**, which I think is *pretty epic*. 😎\n\nIt genuinely was a good way to reuse the code for the dwell clicker.\n\nOh also I made a big, screen-sized, **invisible button**, so that the dwell clicker thinks there's something to click on. Pretty silly, but also pretty simple. 🆒\n\n![](./images/software-architecture.svg)\n\n**Not pictured:** the renderer processes each have preload scripts which are more privileged code than the rest of the renderer's code. Access to system functionality passes through the preload scripts.\n\nThe architecture for normal usage of the library is much simpler.\n\nOoh, but the diagram for the desktop app interacting with web pages (including pages using the library) through the browser extension would be interesting. That's all theoretical for now though.\n\nP.S. There is a script to list IPC events: `node scripts/list-ipc-events.js`\n\nAlso, I do plan to reign in this madness, see [issue #72](https://github.com/1j01/tracky-mouse/issues/72)\n\n## Development Setup\n\n- Before cloning on Windows, make sure you have `git config --global core.symlinks true` set, or you may have issues with symbolic links.\n- [Clone the repo.](https://help.github.com/articles/cloning-a-repository/)\n- Install [Node.js](https://nodejs.org/) if you don't have it\n  - Recommended: install via [nvm](https://github.com/nvm-sh/nvm) or [nvm-windows](https://github.com/coreybutler/nvm-windows)\n  - The supported Node.js version is specified in [`.nvmrc`](./.nvmrc)\n- Open up a command prompt / terminal in the project directory.\n- Run `npm install` to install project-wide dependencies.\n\n\u003e [!NOTE]\n\u003e There's also `npm run install-all` as a shortcut to install dependencies for all packages.\n\nFor the website:\n- Run `npm run in-website -- npm install` to install the website's dependencies. (`--` allows passing arguments to the script, which is just a simple wrapper to run a command within the directory of the package.)\n- Run `npm run website` to start a web server that will automatically reload when files change.\n\nFor the desktop app:\n- For Linux, install XTest library needed for sending mouse input:\n  - On Ubuntu: `sudo apt-get install libxtst-dev`\n  - On Fedora: `sudo yum install libXtst-devel`\n  - On RHEL6.2: `sudo yum install libXi-devel`\n- For macOS:\n  - macOS 10.14 (Mojave) is the supported version\n  - You apparently need a full Xcode installation, not just the command line tools, for the native module to compile.\n  - Tested with Xcode 10.3. Old versions of Xcode can be downloaded from [xcodereleases.com](https://xcodereleases.com/)\n- Run `npm run in-desktop-app -- npm install` to install dependencies.\n- Run `npm run desktop-app` to start the app.\n- To test the CLI, run `npx tracky-mouse --help`.\n  - Alternatively, run `npm link` to make `tracky-mouse` available globally, but note that it may conflict with the installed app.\n  - Those options skip Electron Forge currently. To test the CLI through Electron Forge, run `npm run desktop-app -- -- -- --help` (Yes it's a lot of dashes. It's going through npm, then npm within a subfolder, and then Electron Forge. Each tool has its own `--help` flag, but supports `--` to pass on any following arguments as-is.)\n- Run `npm run in-desktop-app -- npm run make` to build the app for distribution. Look in the `desktop-app/out/` directory for build artifacts.\n\nFor the core library:\n- Dependencies are stored in `core/lib/`; you don't need to run `npm run in-core -- npm install` unless you plan to modify the library's dependencies and run `npm run in-core -- npm run copy-deps` to copy them into `core/lib/`.\n\n### Debugging\n\nVS Code launch configurations are provided to debug the web version in Chrome, and to debug the Electron main process.\n\nFor the screen overlay window, you can use **View \u003e Toggle Developer Tools (Screen Overlay)** from the main window's menu bar.\n\n## Quality Assurance\n\n- Run `npm run lint` to check for spelling, code, and localization issues.\n- There are no tests yet.\n- Make sure to test the first-run state (no settings saved) for both the web and desktop versions often.\n  - Web: `(()=\u003e{ const backup = localStorage[\"tracky-mouse-settings\"]; delete localStorage[\"tracky-mouse-settings\"]; localStorage[\"tracky-mouse-settings-backup\"] = backup; })()`\n  - Desktop (Windows cmd.exe): `move %AppData%\\\"Tracky Mouse\"\\tracky-mouse-settings.json %AppData%\\\"Tracky Mouse\"\\tracky-mouse-settings.json.bak`\n\n## Release Process\n\nThis section outlines the steps for releasing a new version of Tracky Mouse.\n\n\nIn [CHANGELOG.md](CHANGELOG.md), first make sure all important changes are noted in the Unreleased section, by looking over the commit history.  \n\nThe following command takes care of linting, and bumping version numbers for each package and in the changelog. It also creates a git commit and tag for the new version, and pushes the tag to GitHub, triggering the GitHub Actions workflow to create a release draft.\n```sh\nnpm run release -- $VERSION\n```\n\nDownload and install from the GitHub release draft, and test the installed desktop app.\n\n\u003e [!WARNING]\n\u003e \"Point of no return\" (spooky)\n\nFinal steps:\n```sh\n# Push to main\ngit push\n# Deploy the website\n# (this may be done at any time, but it's important to update the homepage download link)\n# (technically this should be the last step since the new download link will only work once the release draft is published...)\nnpm run in-website -- npm run deploy\n```\n\nPublish the GitHub release. This should trigger a GitHub Actions workflow which publishes the core package to npm.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1j01%2Ftracky-mouse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1j01%2Ftracky-mouse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1j01%2Ftracky-mouse/lists"}