{"id":49342291,"url":"https://github.com/pedrohprior/oryxlab","last_synced_at":"2026-04-27T05:01:04.547Z","repository":{"id":354075935,"uuid":"1222040959","full_name":"PedroHPrior/oryxlab","owner":"PedroHPrior","description":"DPS calculator and build optimizer for Realm of the Mad God — 1500 items, 100 sets, 19 classes, real engine, beam-search optimizer, RealmEye import.","archived":false,"fork":false,"pushed_at":"2026-04-27T03:29:41.000Z","size":9142,"stargazers_count":0,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T03:35:38.329Z","etag":null,"topics":["build-optimizer","dps-calculator","gaming-tool","pwa","react","realm-of-the-mad-god","rotmg","theorycrafting","typescript","vite"],"latest_commit_sha":null,"homepage":"https://oryxlab.up.railway.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PedroHPrior.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.yml","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"agents.md","dco":null,"cla":null}},"created_at":"2026-04-27T01:41:52.000Z","updated_at":"2026-04-27T03:29:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/PedroHPrior/oryxlab","commit_stats":null,"previous_names":["pedrohprior/oryxlab"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/PedroHPrior/oryxlab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PedroHPrior%2Foryxlab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PedroHPrior%2Foryxlab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PedroHPrior%2Foryxlab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PedroHPrior%2Foryxlab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PedroHPrior","download_url":"https://codeload.github.com/PedroHPrior/oryxlab/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PedroHPrior%2Foryxlab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32323215,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"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":["build-optimizer","dps-calculator","gaming-tool","pwa","react","realm-of-the-mad-god","rotmg","theorycrafting","typescript","vite"],"created_at":"2026-04-27T05:00:57.326Z","updated_at":"2026-04-27T05:01:04.517Z","avatar_url":"https://github.com/PedroHPrior.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n  \u003cimg src=\"public/favicon.svg\" width=\"92\" alt=\"OryxLab logo\" /\u003e\n\n  # OryxLab\n\n  **A theorycrafter-grade DPS calculator and build optimizer for Realm of the Mad God.**\n\n  [![CI](https://github.com/PedroHPrior/oryxlab/actions/workflows/ci.yml/badge.svg)](https://github.com/PedroHPrior/oryxlab/actions/workflows/ci.yml)\n  [![License: MIT](https://img.shields.io/badge/license-MIT-amber.svg)](LICENSE)\n  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6.svg?logo=typescript\u0026logoColor=white)](tsconfig.json)\n  [![Tests](https://img.shields.io/badge/tests-165%20passing-brightgreen.svg)](tests)\n  [![Issues welcome](https://img.shields.io/badge/feedback-welcome-purple.svg)](https://github.com/PedroHPrior/oryxlab/issues/new/choose)\n\n  ### [→ Try it live ←](https://www.oryxlab.app/app)\n\n\u003c/div\u003e\n\n---\n\nOryxLab answers the only ROTMG question that actually matters in the field:\n*\"is this drop an upgrade?\"* It runs a calibrated DPS engine over **1,500\nreal items**, **100 ST sets**, and **19 classes**, lets you slot builds\nside-by-side, runs a beam-search optimizer for any class in under 100ms,\nand pulls your live RealmEye loadout so each character can be benchmarked\nagainst its own best-in-slot.\n\n```\n┌──────────────────────────────────────────────────────────────────────────┐\n│                                                                          │\n│   1,500 items   ·   100 sets   ·   19 classes   ·   100% real data       │\n│                                                                          │\n│   Engine validated against community calculators (Crystal Wand, Doom     │\n│   Bow, Staff of Esben — all within ±10%).                                │\n│                                                                          │\n│   Sub-100ms beam-search optimizer in a Web Worker.                       │\n│                                                                          │\n│   PWA. Offline-capable. Schema-versioned localStorage.                   │\n│                                                                          │\n└──────────────────────────────────────────────────────────────────────────┘\n```\n\n## Highlights\n\n|  | Feature |\n|---|---|\n| 🎯 | **Calibrated DPS engine** — pure-functional, ATT/DEX modifiers, hit-rate by range, multi-shot direction, AoE/piercing, weapon procs, ability cycle-time bottlenecks, set bonuses, status uptime, party-buff stacking |\n| ⚙️ | **Beam-search optimizer** — top-6 candidates per slot × 5 slots = 7,776 enumerated combos under hard constraints (DEF≥X, HP≥Y, max UTs, no talisman, weapon-type lock). Runs in a Web Worker |\n| 🆚 | **Side-by-side comparator** — up to 6 builds with cards / focus / table modes. DPS-vs-defense chart, scenario presets for major bosses (Oryx 3, Void Entity, Lost Halls, Shatters, etc) |\n| 🛒 | **Quick compare** — pick 2–4 items in the catalog, get a verdict (*\"Crystal Wand wins on DPS, +47% vs Doom Bow\"*), DPS curve, ✦-marked winner per metric |\n| 👤 | **RealmEye import** — drop your username, get every character with current loadout, one-click compare each against optimizer suggestions |\n| 🛠️ | **Build editor** — live stat-source breakdown (base / items / exalts / buffs), exalt sliders with caps, undo/redo (⌘Z), notes per build |\n| 📚 | **Catalog** — virtualized list of 1,500 items, filters (type/class/tier/rarity/mechanic) with live counts, item detail modal with proc tooltips |\n| 💾 | **Persistence** — schema-versioned localStorage with migration path, share-state via URL (gzip + base64) |\n| 📲 | **PWA** — installable, offline-capable, dark-mode persisted |\n| 🛡️ | **Production hardening** — Helmet headers, rate limiting on RealmEye proxy (10 req/min/IP), CORS allowlist, gzip compression, 8s outbound timeout |\n\n## Tech stack\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eFrontend\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003eReact 19 · TypeScript (strict) · Vite 7 · Tailwind v4 · React Router 7 · Web Worker\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eBackend\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003eExpress 5 · Helmet · express-rate-limit · gzip compression · Node 22\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eEngine\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003ePure-functional TypeScript · zero-dep · 100% deterministic\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eTests\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003eVitest · Testing Library · jsdom · 165 tests across engine / components / routes / validation\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eBuild / Deploy\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003eDocker (multi-stage) · Railway · GitHub Actions CI · vite-plugin-pwa\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cb\u003eData\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003e1,500 items · 100 sets · 19 classes — all scraped from RealmEye, validated against in-game values\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Architecture\n\n```\n┌─────────────────────────── Browser ────────────────────────────┐\n│                                                                │\n│   React Router  ┬── ComparatorRoute    ──┐                     │\n│                 ├── CatalogRoute       ──┤                     │\n│                 ├── OptimizerRoute     ──┤   useOryxLab()      │\n│                 ├── InventoryRoute     ──┤   ↑                 │\n│                 └── BuildEditorRoute   ──┤   │                 │\n│                                          │   ▼                 │\n│                       OryxLabContext ◄───┴── OryxLabApp        │\n│                              │                                 │\n│              ┌───────────────┼─────────────────────────┐       │\n│              ▼               ▼                         ▼       │\n│         engine/dps     engine/optimizer          app/storage   │\n│         (pure)         (Web Worker)              (localStorage,│\n│                                                   versioned)   │\n│                                                                │\n└─────────────────────────────────┬──────────────────────────────┘\n                                  │  fetch /api, /data\n                                  ▼\n┌─────────────────────────── Express ────────────────────────────┐\n│                                                                │\n│   Helmet · CORS allowlist · rate-limit · gzip                  │\n│                                                                │\n│   /api/health                                                  │\n│   /api/items, /api/items/:id, /api/classes, /api/classes/:id   │\n│   /api/inventory/realmeye-import  (proxy + parse)              │\n│                                                                │\n│   Static: dist/* + /data/{items,classes,sets,balance}.json     │\n│                                                                │\n└────────────────────────────────────────────────────────────────┘\n```\n\n## DPS engine\n\nThe pure-functional core in `src/engine/dps.ts`:\n\n```ts\nimport { computeDerivedStats } from \"@/engine/dps\"\n\nconst result = computeDerivedStats({\n  build,        // class + equipped items + exaltations\n  scenario,    // target def + statuses + party buffs\n  classDef,    // base stats and caps\n  itemMap,     // catalog\n  itemSets,    // optional set definitions\n})\n// → { dps, dpsAtZeroDef, ehp, att, dex, wis, def, hp, mp,\n//     timeToKill1k, dpsCurve: number[17] }\n```\n\nWhat it models:\n\n- **ATT linear modifier** `att / 50`\n- **DEX RoF modifier** `0.5 + 1.5 × min(dex, 100) / 75`\n- **Hit rate by weapon range** (≥8 tiles → 1.0, falling off below)\n- **Multi-shot directional factor** (0.80 for katanas)\n- **AoE multiplier** (1.6×) and piercing/wavy/parametric/boomerang (1.10×)\n- **Class multipliers** — Trickster 1.6×, Summoner 1.4×, Necromancer 1.15×, etc\n- **Party buffs** — additive flat stats + multiplicative damage (Paladin Seal, Warrior Helm, Bard Inspire/Crescendo/Encore)\n- **Status effects** — Exposed +20%, Cursed +25%, Armor Broken bypasses defense\n- **Weapon proc damage** — `procRate × procDamage` per shot (Crystal Wand shards, Conducting Wand bolts)\n- **Ability damage** — spells / skulls / quivers contribute `damage / cycleTime`, bottlenecked by `mpCost / mpRegen`\n- **Inflict-status tags** — apply uptime-weighted bonus when scenario doesn't already have the status\n- **Self-buff tags** — small DPS multipliers for berserk / damaging / inspired / speedy\n- **Set bonuses** — full-set stat contributions\n- **Min damage rule** — at least 10% of raw damage even at high defense\n\nEvery tunable constant lives in [`product/data/balance.json`](product/data/balance.json) so the community can iterate on values without touching code.\n\n## Quickstart (local dev)\n\n```bash\ngit clone https://github.com/PedroHPrior/oryxlab.git\ncd oryxlab\nnpm install\nnpm run dev:all      # Vite on :3000 + Express on :3001\n```\n\nOpen \u003chttp://localhost:3000/app\u003e.\n\n```bash\nnpm test             # 165 tests\nnpm run typecheck    # tsc strict\nnpm run lint         # eslint\nnpm run build        # produces dist/\n```\n\n## Deployment\n\n**Live target:** Railway with a Dockerfile builder (Nixpacks's cache-mount\nEBUSY bug forces us off the default).\n\n```bash\n# One-process container that serves SPA + API on $PORT.\ndocker build -t oryxlab .\ndocker run -p 3001:3001 oryxlab\n```\n\nRequired env vars in production (optional in dev):\n\n| Variable | Purpose |\n|----------|---------|\n| `PORT` | Bind port (Railway provides automatically) |\n| `NODE_ENV` | Set to `production` (auto-detected from `dist/` presence) |\n| `ALLOWED_ORIGIN` | Comma-separated CORS allowlist |\n\n## Repo layout\n\n```\noryxlab/\n├── src/\n│   ├── app/                Top-level state, routing, storage, error boundary, logger\n│   │   ├── OryxLabApp.tsx\n│   │   ├── ErrorBoundary.tsx\n│   │   ├── api.ts          Static-or-backend fetch client\n│   │   ├── storage.ts      localStorage with versioned migrations\n│   │   ├── share.ts        URL state encoding (gzip + base64)\n│   │   ├── logger.ts       Namespaced logger (silent in prod for non-errors)\n│   │   ├── sw.ts           Service-worker registration\n│   │   ├── useBuildHistory.ts   ⌘Z undo/redo\n│   │   └── routes/         5 route components\n│   ├── engine/\n│   │   ├── dps.ts          Pure-functional DPS engine\n│   │   ├── optimizer.ts    Beam-search build optimizer\n│   │   ├── optimizer.worker.ts    Web Worker wrapper\n│   │   └── optimizer-client.ts    Main-thread client with sync fallback\n│   ├── shell/components/   AppShell, MainNav, ScenarioBar, OryxLogo\n│   └── sections/\n│       ├── _shared/        ItemSprite, ClassPortrait, TierBadge, …\n│       ├── comparator/     BuildColumn, FocusView, TableView, DpsCurveChart, SlotPicker\n│       ├── build-editor/   Live stat-source bars, exalt panel, calc steps\n│       ├── catalog/        ItemCard, ItemDetailModal, QuickComparePanel, FilterRail\n│       ├── optimizer/      ClassPicker, ConstraintsPanel, ResultCard\n│       └── inventory/      OwnedSummaryCards, RealmEyeImportPanel, CharactersPanel\n├── server/index.mjs        Express server (API + static SPA)\n├── scripts/\n│   ├── scrape/             RealmEye scrapers (full + incremental rescrape)\n│   └── fixups/             One-shot data migrations / canon enforcement\n├── product/data/           Source-of-truth: items / classes / sets / balance JSON\n├── public/data/            Bundled JSON shipped to clients (trimmed for size)\n└── tests/                  Engine + components + routes + validation suites\n```\n\n## Contributing\n\nOryxLab is a single-maintainer project. **Code changes (PRs) are not accepted\nfrom external contributors at this time** — but bugs, data corrections, and\nfeature ideas are extremely welcome.\n\n| What you have | Where to file it |\n|---------------|------------------|\n| 🐛 Found a bug | [Bug report template](https://github.com/PedroHPrior/oryxlab/issues/new?template=bug_report.yml) |\n| 📊 Wrong stat / class / proc | [Data correction template](https://github.com/PedroHPrior/oryxlab/issues/new?template=data_correction.yml) (fast-tracked) |\n| 💡 Idea for a feature | [Feature request template](https://github.com/PedroHPrior/oryxlab/issues/new?template=feature_request.yml) |\n| 💬 Want to discuss | [GitHub Discussions](https://github.com/PedroHPrior/oryxlab/discussions) |\n| 🔒 Security issue | Email **pedrohpk17@gmail.com** — see [SECURITY.md](SECURITY.md) |\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for what makes a good issue.\n\n## License\n\n[MIT](LICENSE). Realm of the Mad God is © Deca Games — OryxLab is an\nunofficial fan tool, not affiliated with Deca Games or RealmEye. Item sprites\nare served from RealmEye's CDN.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n  \u003csub\u003e\n    Built with ☕ in 🇧🇷 by \u003ca href=\"https://github.com/PedroHPrior\"\u003e@PedroHPrior\u003c/a\u003e.\u003cbr/\u003e\n    If OryxLab saved you from a bad fuse roll, consider \u003ca href=\"https://github.com/PedroHPrior/oryxlab\"\u003estarring the repo\u003c/a\u003e.\n  \u003c/sub\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpedrohprior%2Foryxlab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpedrohprior%2Foryxlab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpedrohprior%2Foryxlab/lists"}