← back to gallery

Overlay Arena

Live directory and comparison matrix for every local-AI desktop overlay we can find

aidirectorylocal-aiollamadesktopmacoscomparisonhnshow-hn
Open product ↗

overlay-arena

A live directory and side-by-side comparison matrix for local-AI desktop overlays — Thuki-style hotkey assistants, MouseClaw-style pixel pets, MacGPT-class menu-bar AIs, Ollama-Pilots. Built for macOS/Windows power users who want a non-cloud Cursor/ChatGPT alternative but can't keep track of the weekly Show HN deluge.

The directory pulls real Show HN posts and Product Hunt launches at runtime, enriches each entry with live GitHub stats (stars, last commit, license), and — when an LLM key is configured — extracts a structured feature matrix from each repo's README. Two or three overlays can be compared in a permalinkable side-by-side view.

No mock data. Every overlay row points back to a real HN thread or Product Hunt launch with a timestamp.

What it does

API endpoints

All mounted under /overlay-arena. JSON. CORS open. No auth.

| Method | Path | Purpose |
|---|---|---|
| GET | /health | {ok:true} HTTP 200 |
| GET | /api/overlays | List overlays. Query: os (macos/windows/linux), oss=1, vision=1, voice=1, search=1, q=<text>, limit, offset. Sorted by score desc. |
| GET | /api/overlays/:slug | Full overlay record incl. features, repo stats, source links. |
| GET | /api/compare?slugs=a,b,c | Returns {overlays, axes} for up to 3 slugs. |
| GET | /api/digest | Last-7-day top 10. |
| GET | /api/stats | Totals + last refresh timestamps. |
| GET | /api/sources | Latest cron-run row per source. |
| GET | /api/sources/history | Last 50 cron runs. |
| POST | /api/submit | Body: {name, url, repo_url, description, contact}. Returns {id, status, linked_overlay_id}. |
| GET | /api/submissions | Public submission queue. |

Data sources

| Source | URL | Refresh | On failure |
|---|---|---|---|
| HN Algolia Search | https://hn.algolia.com/api/v1/search?tags=story&query=<term>&hitsPerPage=50 (10 queries: "local ai overlay", "ollama overlay", "menu bar ai", "ai hotkey", "desktop ai assistant", "ai pet desktop", "local llm desktop", "macos ai assistant", "show hn ollama", "local llm overlay") | Every 30 min via node-cron + once at boot | Logged to fetch_log; previous rows retained |
| Product Hunt GraphQL | https://api.producthunt.com/v2/api/graphql (filter topic="artificial-intelligence", top 50 by votes; client-side keyword filter) | Daily 03:15 + once at boot | Skipped silently if PRODUCTHUNT_TOKEN unset; error logged otherwise |
| GitHub Repos API | https://api.github.com/repos/{owner}/{repo} + /commits?per_page=1 | Every 15 min — 10 oldest-updated tracked repos, 1/sec staggered | 404 → repo_dead=1, row kept; 403 → back off until next tick |
| GitHub raw README | https://raw.githubusercontent.com/{owner}/{repo}/HEAD/README.md (with .rst/no-ext fallbacks) | When repo's HEAD SHA changes | Leaves readme_text=NULL; row remains, feature extraction skipped |
| OpenRouter (LLM feature extractor) | https://openrouter.ai/api/v1/chat/completions model anthropic/claude-haiku-4-5 | Every 20 min, up to 5 overlays where README is fresh + features stale | Skipped silently if OPENROUTER_API_KEY unset; card shows unverified badge |

The product degrades gracefully without PRODUCTHUNT_TOKEN or OPENROUTER_API_KEY — HN + GitHub alone seed the directory and stats.

Run locally

npm install
PORT=4809 node server.js
# open http://localhost:4809/overlay-arena/

Optional env (.env.example shows the keys):

PRODUCTHUNT_TOKEN=...      # enables PH scrape
OPENROUTER_API_KEY=...     # enables LLM feature extraction
GITHUB_TOKEN=...           # bumps GitHub rate limit 60/h → 5000/h

On boot, the server runs one HN scrape and starts a non-blocking GitHub refresh. The directory has real overlays within ~10 seconds.

Stack

File layout

overlay-arena/
├── server.js              # Express boot, mounts /overlay-arena
├── db.js                  # SQLite schema + prepared statements
├── cron.js                # node-cron registration + boot fetch
├── lib/
│   ├── http.js            # fetch wrapper (UA, timeout, retry)
│   ├── slug.js            # slugify + name derivation
│   ├── extract-repo.js    # github.com/owner/repo regex
│   └── score.js           # ranking + rel-time helper
├── routes/
│   ├── overlays.js        # list / detail / digest / stats
│   ├── compare.js         # /compare?slugs=
│   ├── submit.js          # POST + list submissions
│   └── meta.js            # health + sources
├── scrapers/
│   ├── hn.js              # HN Algolia multi-query
│   ├── producthunt.js     # PH GraphQL
│   ├── github.js          # repo meta + last commit + README
│   └── features.js        # OpenRouter feature extraction
├── public/
│   ├── index.html         # SPA shell
│   ├── app.js             # router + views
│   └── style.css          # dark theme
└── .env.example

Out of scope

Auth, accounts, ratings, comments, screenshots, benchmarks, latency measurements, downloads, paid feature audits, RSS/email digests, Linux-only tools. The "moderation queue" is heuristic only — no human-review UI.