{"id":49868234,"url":"https://github.com/willwade/scan-engine-lab","last_synced_at":"2026-05-15T04:04:45.128Z","repository":{"id":335566356,"uuid":"1146313913","full_name":"willwade/scan-engine-lab","owner":"willwade","description":"A client side only html/javascript switch scanning demo ","archived":false,"fork":false,"pushed_at":"2026-02-24T17:20:13.000Z","size":18180,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-24T20:59:00.271Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/willwade.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2026-01-30T22:49:52.000Z","updated_at":"2026-02-24T17:22:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/willwade/scan-engine-lab","commit_stats":null,"previous_names":["willwade/garyd-scanner-demo","willwade/scan-engine-lab"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/willwade/scan-engine-lab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2Fscan-engine-lab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2Fscan-engine-lab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2Fscan-engine-lab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2Fscan-engine-lab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willwade","download_url":"https://codeload.github.com/willwade/scan-engine-lab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willwade%2Fscan-engine-lab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33053150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-15T02:00:06.351Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-05-15T04:04:44.472Z","updated_at":"2026-05-15T04:04:45.118Z","avatar_url":"https://github.com/willwade.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# scan-engine-lab (aka Gary Derwent Scanning Demo!)\n\nA client-side switch scanning simulator built with TypeScript and Vite.\n\nThis project is named after and inspired by **Gary Derwent**, an Occupational Therapist who created a seminal Flash-based switch scanning demo in the late 1990s. His original work helped demonstrate the mechanics and clinical applications of switch scanning for assistive technology. This project aims to replicate and extend those concepts using modern web technologies.\n\n## Features\n\n*   **Multiple Scanning Strategies:**\n    *   Row-Column\n    *   Column-Row\n    *   Linear \u0026 Snake\n    *   Quadrant (Group Scanning)\n    *   Elimination (Binary/Quaternary)\n    *   Continuous (Mouse emulation)\n*   **Predictive Scanning:** Integrates PPM (Prediction by Partial Matching) to reorder targets based on probability.\n*   **Configurable Settings:** Adjustable scan rate, acceptance time, post-selection delay, and more.\n*   **Audio Feedback:** Web Audio API generated feedback for scanning steps and selections.\n*   **Responsive Design:** Grid layout that adapts to container size.\n\n## Usage\n\n### Controls\n\nThe simulator mimics a multi-switch setup using keyboard keys:\n\n| Switch Function | Default Key(s) | Description |\n| :--- | :--- | :--- |\n| **Switch 1 (Primary)** | `Space`, `Enter` | **Select** or **Hold**. Activates the currently highlighted item or group. |\n| **Switch 2 (Secondary)** | `2` | **Step** or **Back-up**. Manually moves to the next item or goes back a level. |\n| **Switch 3 (Utility)** | `3` | **Home/Reset**. Restarts the scan sequence or clears input. |\n| **Switch 4 (Cancel)** | `4` | **Cancel**. Stops the current action. |\n| **Settings Toggle** | `S` | Shows/Hides the configuration overlay. |\n\n### Configuration (URL Parameters)\n\nThe application can be configured via URL parameters, making it easy to embed in iframes with specific presets.\n\n**Example:** `?rate=800\u0026strategy=row-column\u0026ui=hidden`\n\n| Parameter | Values | Description |\n| :--- | :--- | :--- |\n| `ui` | `hidden` | Hides the settings button and overlay (useful for embedding). |\n| `rate` | `number` (ms) | Sets the scan speed in milliseconds (e.g., `1000`). |\n| `strategy` | `row-column`, `linear`, `snake`, `quadrant`, `group-row-column`, `elimination`, `continuous`, `probability` | Sets the initial scanning strategy. |\n| `content` | `numbers`, `keyboard` | Sets the grid content type. |\n| `lang` | `en`, etc. | Sets the language for prediction and keyboard layout. |\n| `layout` | `alphabetical`, `frequency` | Sets the keyboard layout mode. |\n| `view` | `standard`, `cost-numbers`, `cost-heatmap` | Visual debug modes to analyze scanning cost. |\n| `heatmax` | `number` | Sets the maximum value for the heatmap visualization. |\n\n### Visualizing Scan Cost (Heatmap)\n\nThe simulator includes visualization modes to help analyze the efficiency of different scanning strategies and layouts.\n\n1.  Open the **Settings** menu (click the gear icon or press `S`).\n2.  Scroll down to the **Visualization** section.\n3.  Change **View Mode** to:\n    *   **Cost (Numbers):** Displays the \"cost\" (number of steps) to reach each item.\n    *   **Cost (Heatmap):** Colors items from Green (low cost) to Red (high cost).\n4.  Adjust **Heatmap Max Cost** to change the color scale sensitivity. Default is 20.\n    *   *Tip:* If the grid is large, increase this value to see a smoother gradient.\n\n## Packages (npm)\n\nThis repo publishes two packages that can be used independently in other apps:\n\n1. `scan-engine` — headless scanning engine (strategies, timing, selection).\n2. `scan-engine-dom` — DOM helpers for continuous scanning overlays + hit testing.\n3. `react-scan-engine` — tiny React wrapper (`\u003cScanner\u003e` + `\u003cScannable\u003e`) for `scan-engine`.\n\n### Install\n\n```bash\nnpm install scan-engine scan-engine-dom\n```\n\nFor React compatibility-style usage:\n\n```bash\nnpm install react-scan-engine scan-engine\n```\n\n### React Wrapper (react-scan-engine)\n\n`react-scan-engine` intentionally mirrors the familiar `react-scannable` component shape:\n\n```tsx\nimport React from 'react';\nimport { Scannable, Scanner } from 'react-scan-engine';\n\nclass Example extends React.Component {\n  state = {\n    isActive: true,\n  };\n\n  render() {\n    return (\n      \u003cScanner\n        active={this.state.isActive}\n        config={{ scanPattern: 'row-column', scanTechnique: 'block', scanRate: 900 }}\n      \u003e\n        \u003cScannable\u003e\n          \u003cbutton\u003eCLICK\u003c/button\u003e\n        \u003c/Scannable\u003e\n      \u003c/Scanner\u003e\n    );\n  }\n}\n```\n\nCompatibility details:\n\n- `active` maps to engine start/stop.\n- `Scannable` children are scanned in DOM order.\n- Defaults: `Enter`/`Space` select, `ArrowRight`/`ArrowDown` step, `Backspace` reset, `Escape` cancel.\n- You can pass advanced engine config via the `config` prop.\n\n### Headless Scanning (scan-engine)\n\n```ts\nimport { LinearScanner } from 'scan-engine';\n\nconst surface = {\n  getItemsCount: () =\u003e items.length,\n  getColumns: () =\u003e 8,\n  setFocus: (indices: number[]) =\u003e highlight(indices),\n  setSelected: (index: number) =\u003e flash(index),\n};\n\nconst configProvider = {\n  get: () =\u003e ({\n    scanRate: 800,\n    scanInputMode: 'auto',\n    scanDirection: 'circular',\n    scanPattern: 'linear',\n    scanTechnique: 'point',\n    scanMode: null,\n    continuousTechnique: 'crosshair',\n    compassMode: 'continuous',\n    eliminationSwitchCount: 4,\n    allowEmptyItems: false,\n    initialItemPause: 0,\n    scanLoops: 0,\n    criticalOverscan: { enabled: false, fastRate: 100, slowRate: 1000 },\n    colorCode: { errorRate: 0.1, selectThreshold: 0.95 },\n  })\n};\n\nconst scanner = new LinearScanner(surface, configProvider, {\n  onSelect: (index: number) =\u003e console.log('Selected', index),\n});\n\nscanner.start();\n```\n\n### Continuous Scanning (scan-engine + scan-engine-dom)\n\n```ts\nimport { ContinuousScanner } from 'scan-engine';\nimport { ContinuousOverlay, resolveIndexAtPoint } from 'scan-engine-dom';\n\nconst container = document.querySelector('.grid') as HTMLElement;\nconst overlay = new ContinuousOverlay(container);\n\nconst surface = {\n  getItemsCount: () =\u003e items.length,\n  getColumns: () =\u003e cols,\n  setFocus: (indices: number[]) =\u003e highlight(indices),\n  setSelected: (index: number) =\u003e flash(index),\n  resolveIndexAtPoint: (x: number, y: number) =\u003e resolveIndexAtPoint(container, x, y),\n};\n\nconst scanner = new ContinuousScanner(surface, configProvider, {\n  onContinuousUpdate: (state) =\u003e overlay.update(state),\n  onSelect: (index: number) =\u003e console.log('Selected', index),\n});\n\nscanner.start();\n\n// Cleanup when switching modes:\noverlay.destroy();\n```\n\n## Developer Documentation\n\n### Prerequisites\n\n*   Node.js (latest LTS recommended)\n*   npm\n\n### Installation\n\nClone the repository and install dependencies:\n\n```bash\ngit clone \u003crepository-url\u003e\ncd scan-engine-lab\nnpm install\n```\n\n### Running Locally\n\nStart the development server:\n\n```bash\nnpm run dev\n```\n\nThe app will be available at `http://localhost:5173` (or the port shown in the terminal).\n\n### Building\n\nBuild the project for production:\n\n```bash\nnpm run build\n```\n\nThis generates the static assets in the `dist/` directory.\n\n### Releasing npm packages\n\nThis repo publishes:\n\n1. `scan-engine`\n2. `scan-engine-dom`\n3. `react-scan-engine`\n\nRecommended release flow:\n\n1. Prepare versions and build artifacts:\n\n```bash\nnpm run release:prepare -- patch\n```\n\nUse `patch`, `minor`, `major`, or an explicit version (for example `0.2.0`).\n\n2. Commit the version changes and create a git tag.\n3. Publish all packages in dependency order:\n\n```bash\nnpm run release:publish\n```\n\nIf you want one command for everything (prepare + publish):\n\n```bash\nnpm run release:full -- patch\n```\n\n### Tech Stack\n\n*   **Language:** [TypeScript](https://www.typescriptlang.org/)\n*   **Bundler:** [Vite](https://vitejs.dev/)\n*   **Audio:** Web Audio API (No external assets required)\n*   **Prediction:** `@willwade/ppmpredictor`\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillwade%2Fscan-engine-lab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillwade%2Fscan-engine-lab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillwade%2Fscan-engine-lab/lists"}