A client-side companion web app to create, refine, and share perk builds for Dead by Daylight. Pick a role, filter by tags, lock/ban perks, and let the optimizer propose up to 4 perks using synergies and exclusion rules (e.g., no double Exhaustion).
-
Zero backend – everything runs in the browser (no login, no user uploads).
-
Multi-source dataset with cache
- public APIs,
- fallback to
public/perks.json, - minimal embedded fallback.
Cached in
localStoragefor 6 hours (dbd-api-cache-v8).
-
Optimizer – ranking by tags + synergy + anti-synergy + mutex with bonuses (tier / rate / killer focus).
-
Fast UI – search & filters, Lock/Ban, dynamic suggestions.
-
Randomiser – 4-perk random builds that respect bans/mutex/no-duplicates.
-
Image export (1200×628) – automatic CORS fallbacks and proxy for perk icons.
-
Accessibility – keyboard toggles (Enter/Space), ARIA for dialogs and hints.
-
Perfect for static hosting – GitHub Pages / Netlify / Vercel.
- Vite + React + TypeScript
- Tailwind CSS v4 (official
@tailwindcss/viteplugin) - Canvas 2D for the shareable image
- localStorage for settings + API cache
On load:
-
Try cache from
localStorage(TTL 6h). -
Fetch APIs with
cache: "no-store":- Survivor:
GET https://dennisreep.nl/dbd/api/v3/getSurvivorPerkData?description=true - Killer:
GET https://dennisreep.nl/dbd/api/v3/getKillerPerkData?description=true
- Survivor:
-
Killer enrichment – for each killer owner compute
meta.topForKillersviaGET .../getKillerData?killer=<slug>. -
If APIs fail → fallback to
GET /perks.json. -
If that fails too → use the minimal in-code dataset.
To invalidate the cache, bump
API_CACHE_KEY(current:dbd-api-cache-v8).
Expected shape:
{
"version": "2025-09-10",
"perks": [
{
"id": "sprint_burst",
"name": "Sprint Burst",
"role": "survivor",
"tags": ["chase", "exhaustion"],
"synergy": ["Resilience"],
"anti_synergy": ["Lithe", "Dead Hard", "Balanced Landing", "Overcome"],
"desc": "Gain a burst of speed when you start running.",
"icon": "https://.../sprint_burst.png",
"meta": { "owner": "Meg Thomas", "tier": "A", "rate": 4.1 }
}
]
}Notes
-
synergy/anti_synergyare optional; the optimizer also works with tags only. -
Some tags are derived automatically from name/description:
- Survivor →
exhaustion - Killer →
scourge_hook
- Survivor →
-
For killers, when available the code augments
meta.topForKillersas[{ "slug": "<killer>", "rank": 1, "usage": 4.2 }, ...].
const MUTEX_TAGS = {
survivor: ["exhaustion"],
killer: ["scourge_hook"]
};If a picked perk contains a mutex tag, the optimizer avoids another perk with the same mutex tag.
For each candidate perk:
- Selected tags match: +10 per matching tag
- Synergies (
synergy) with locked/current perks: +8 each - Anti-synergies (
anti_synergy) against current perks: −12 each - Mutex conflict (same mutex tag as current): −100 (hard avoid)
- Tier bonus:
S:+10, A:+6, B:+3, C:+0, D:-2, E:-4, F:-6 - Rate bonus: clamp
rateto[0..5], then(rate − 2.5) * 3 - Killer focus: if enabled and present in
topForKillers,bonus = max(0, 14 - (rank - 1) * 2)(rank 1 → +14, rank 2 → +12, …) - Tiny tie-breaker favoring shorter names
The final build contains up to 4 perks, respecting bans, locks, and mutex.
-
Role switch (Survivor / Killer)
-
Search & Filters
- Owner (dynamic label: Killer or Survivor), Tier, Min Rate
- Clickable tag chips
-
Perk list
- Click to toggle description (collapsible)
- Lock / Ban buttons
-
Optimizer (right column)
- Shows up to 4 suggestions
- Killer focus (killer role only)
- Share build → export PNG
-
Randomiser modal
- Re-roll 4-perk build by role, respects bans/mutex/no-dupes
-
Persistence in
localStorage(role, selected tags, locked, banned, filters)
-
Size: 1200×628
-
Flexible layout: each card’s height is measured dynamically (title/meta/tags) so rows align even with long titles.
-
Icons: rounded-corner draw, compact size; single-line ellipsis for long titles/tags.
-
CORS-safe icon loading sequence:
- Standard CORS
crossOrigin="anonymous" referrerPolicy="no-referrer"- Proxy:
https://images.weserv.nl/?url=<host/path>
- Standard CORS
-
Sharing: uses Web Share API with a file when available, otherwise triggers PNG download.
Requirements: Node 18+
# install dependencies
npm install
# dev server
npm run devTailwind v4 is already configured via the Vite plugin. In your main CSS:
@import "tailwindcss";npm run buildOutput goes to dist/.
Add scripts to package.json:
{
"scripts": {
"build": "vite build",
"predeploy": "npm run build && node -e \"require('fs').copyFileSync('dist/index.html','dist/404.html')\"",
"deploy": "gh-pages -d dist -b gh-pages"
}
}Then:
npm run deployGitHub Settings → Pages:
- Source: Deploy from a branch
- Branch:
gh-pages/ root
For a project site (https://USERNAME.github.io/REPO_NAME/), set in vite.config.ts:
export default defineConfig({
base: '/REPO_NAME/',
plugins: [react(), tailwind()]
})and read the dataset with:
fetch(import.meta.env.BASE_URL + 'perks.json')If the site lives at the domain root:
vite.config.ts:base: '/'- dataset fetch:
fetch('/perks.json', { cache: 'no-store' }) - Configure Settings → Pages → Custom domain and your DNS
(CNAME
www→USERNAME.github.io, A-records at apex → GitHub Pages IPs) - Enable Enforce HTTPS
-
404 for
vite.svgorperks.jsonon Pages → usuallybaseor absolute pathsvite.config.ts→base: '/REPO_NAME/'index.htmlfavicon →href="%BASE_URL%vite.svg"- fetch →
import.meta.env.BASE_URL + 'perks.json'
-
CORS errors for icons during export Normal if the host doesn’t expose CORS. The code tries no-referrer and then uses images.weserv.nl as a proxy.
-
Unchecked runtime.lastErrorin console This typically comes from a browser extension, not the site. -
Empty dataset after APIs The app logs a warning and falls back to
/perks.json, then to the minimal embedded dataset. -
Layout not centered / spacing issues Outer wrapper uses
flex justify-center, inner usesmx-auto. Tune grid gaps and paddings via Tailwind classes in JSX.
public/
perks.json
src/
App.tsx
main.tsx
index.css # @import "tailwindcss"
scripts/
make_perks_from_dennisreep.mjs # optional: dataset generation
vite.config.ts
Example script (HTML scraper with cheerio):
# generate/update public/perks.json
npm run make-perksConfigure sources in scripts/make_perks_from_dennisreep.mjs.
You can also maintain perks.json manually or from other structured sources.
PRs and issues are welcome. If you change tags, (anti)synergies, or scoring heuristics, please include a short rationale in the PR.
MIT — see LICENSE.
DBD Build Optimizer is a fan-made project, not affiliated with Behaviour Interactive. All trademarks and game content are property of their respective owners.