Among Demons is a Node.js/Express prototype for a server-authoritative demon collection and dungeon-run game. The frontend is static HTML/CSS/vanilla JavaScript served from public/app; the backend is an Express API backed by MySQL.
The current loop is:
- Register or log in.
- Start a Dungeon with two demons chosen from six tokenized draft starters and/or your permanent collection.
- Arrange the active team into front/back positions.
- Run automatic server-simulated battles.
- Recruit defeated demons into the temporary Dungeon team, skip recruitment, cash out between fights, or clear floor 20.
- Earn XP and Souls, then save eligible demons into the permanent collection.
Combat, RNG, reward generation, XP, Souls, run status, and collection writes are server-authoritative. The browser displays state and stages player choices, but it must not calculate gameplay outcomes.
| Area | Technology |
|---|---|
| Runtime | Node.js |
| Server | Express 4 |
| Database | MySQL via mysql2/promise |
| Config | dotenv |
| Frontend | Static HTML, vanilla JavaScript |
| Styling | Bootstrap 5, Bootstrap Icons, custom CSS |
amongdemons.com/
|-- public/
| |-- api/
| | |-- account/ # Player progression endpoints
| | |-- admin/ # Prototype admin placeholders
| | |-- auth/ # Register, login, session profile
| | |-- data/ # Source game data JSON
| | |-- demons/ # Permanent collection endpoints
| | |-- game/ # Public static game-data endpoints
| | |-- lib/ # Shared backend game/auth/db modules
| | `-- runs/ # Dungeon run endpoints
| `-- app/
| |-- css/
| |-- images/
| `-- js/
|-- api.md # Older planning notes
|-- idea.md # Original game design notes
|-- package.json
|-- README.md
`-- server.jsInstall dependencies:
npm installCreate .env in the project root:
DB_HOST=your_mysql_host
DB_PORT=3306
DB_NAME=your_database
DB_USER=your_user
DB_PASSWORD=your_password
PORT=3000Start the server:
npm run devOr run without nodemon:
npm startOpen http://localhost:3000.
| Script | Command | Purpose |
|---|---|---|
npm start |
node server.js |
Start the Express server |
npm run dev |
nodemon server.js |
Start with automatic restarts |
| Route | Description |
|---|---|
/ |
Redirects to /demons/type/1, or to a type page when ?type= is present |
/demons/type/:page |
Public static demon browser shell |
/dungeon |
Main Dungeon run UI |
/hunt |
Legacy redirect to /dungeon |
/login |
Login page |
/register |
Registration page |
/play |
Account dashboard and compact collection view |
/account |
Alias for /play |
/collection |
Full authenticated collection browser with filters, sorting, and missing slots |
/summon |
Authenticated Souls/summon placeholder page |
/rank |
Redirects to /rankings |
/rankings |
Leaderboard sorted by level by default |
/rankings/souls |
Leaderboard page using the Souls sort |
Most gameplay API endpoints require an authenticated player. Send either:
Authorization: Bearer <token>or:
x-player-token: <token>POST /api/auth/register creates a new account. POST /api/auth/login logs in an existing account; for prototype convenience, it also creates an account when the username does not exist. Passwords use PBKDF2-SHA512 with per-user salts. Session tokens are stored in player_sessions.
The frontend stores the token and cleaned player object in localStorage under:
amongdemons-sessionAll API routes are mounted under /api.
| Method | Route | Description |
|---|---|---|
POST |
/auth/register |
Create a player account and session |
POST |
/auth/login |
Log in, or create a prototype account if missing |
GET |
/auth/me |
Return the authenticated player |
| Method | Route | Description |
|---|---|---|
GET |
/account/progression |
Return level, XP, Souls, and unlocks |
GET |
/demons |
List owned permanent demons |
GET |
/demons/:id |
Return one owned permanent demon |
POST |
/demons/save |
Save one final-floor demon reward after completing floor 20 |
| Method | Route | Description |
|---|---|---|
GET |
/runs/start-options |
Return six draft starters, a short-lived signed draft token, and the player's collection |
POST |
/runs/start |
Start a new Dungeon run from two draft or collection demons |
GET |
/runs/current |
Return the player's current active/completed pending run |
GET |
/runs/:id |
Return one run state owned by the player |
POST |
/runs/:id/formation |
Update front/back positions before battle |
POST |
/runs/:id/battle |
Simulate the next battle server-side |
POST |
/runs/:id/reward |
Mark a reward as claimed |
POST |
/runs/:id/recruit |
Stage or commit recruitment choices and advance to the next floor |
POST |
/runs/:id/cashout |
Leave between fights, save one eligible demon, and claim earned XP/Souls |
POST |
/runs/:id/end |
End a run and grant accumulated XP/Souls |
| Method | Route | Description |
|---|---|---|
GET |
/game/demon-types |
Return demon type, role, stat, targeting, and ability data |
GET |
/game/demons |
Return demon asset mappings |
GET |
/leaderboard?sort=level|xp|souls |
Return up to 100 players sorted by level, XP, or Souls |
POST |
/admin/demon-balance |
Prototype placeholder; returns 501 and does not mutate data |
- Starting options are generated as six choices from starter type IDs
1,2, and3, withcommon,uncommon, orrarerarity. - Draft starter choices are protected by an HMAC-signed token and expire after 15 minutes.
- Starting a new run closes any open runs for that player.
- New Dungeon runs start with exactly 2 demons.
- The active Dungeon team can contain up to 6 demons.
- The player can use permanent collection demons as starting Dungeon demons.
- Floor 1 starts at 2 demons, then team and enemy size grow by 1 per floor until capping at 6 on floor 5.
- Enemy teams are 2 demons on floor 1, 3 on floor 2, 4 on floor 3, 5 on floor 4, and 6 from floor 5 onward.
- Floors 1 through 3 use the starter type pool; later floors unlock more types based on floor.
- From floor 4 onward, enemy generation applies floor pressure that biases later floors toward higher type IDs and higher rarities while keeping each type's base
spawnWeight. - Legendary and mythic enemy rarities can only appear from floor 10 onward.
- After clearing floor 10, the player may call in one collection demon as a one-time reinforcement while editing the team for floor 11.
- Floor 20 always includes type
11as the first enemy. - After a win before floor 20, defeated enemies become recruit rewards.
- Between fights, the player may stage a whole team, recruit one demon, swap demons, skip recruitment, or cash out.
- Cashing out between fights saves one eligible new demon and grants accumulated XP/Souls.
- Clearing floor 20 marks the run
completedand offers final rewards from the surviving team plus final enemies. - Only one final-floor demon can be saved through
/api/demons/save. - The permanent collection has one slot per demon type and rarity, for 66 total slots. Saving another demon with the same type and rarity replaces that slot.
Combat is automatic and simulated in public/api/lib/combat.js.
- Living demons gain
attackMeter += speedeach tick. - When
attackMeter >= 100, the demon acts and the meter resets. - Battles stop when one side has no living demons, or after the 1000 tick safety limit.
- Front-row targeting prefers living front-row enemies and falls back to any living enemy.
- Team state is cloned for battle, then persisted from the simulator result.
- The API returns both a combat log and before/after snapshots for UI replay.
- Poison effects tick slowly over time, can stack without a per-target cap, and are cleared from teams between winning floors.
Implemented ability kinds include:
| Ability | Behavior |
|---|---|
basic_attack, heavy_attack, slow_crushing_attack, ranged_execute, fast_execute, aoe_attack |
Damage using configured targeting rules |
poison |
Applies stacking poison to high-HP targets |
heal |
Heals the living ally with the most missing HP |
retaliate |
Does not proactively attack; returns configured thorns damage when hit |
chaotic_attack |
Hits a random target from its configured pool for random damage |
- Demon type definitions live in
public/api/data/demon-types.json. - Demon image mappings live in
public/api/data/demons.json. - Full demon images live in
public/app/images/demons. - Thumbnail images live in
public/app/images/demons/thumbnails. - Page/background/logo assets live in
public/app/images. - There are 11 demon types and 6 rarity tiers:
common,uncommon,rare,epic,legendary,mythic.
Treat files in public/api/data as source game data. API code reads them at runtime but should not mutate them.
The API initializes required tables on first API use. public/api/lib/schema.js creates missing tables and performs additive schema checks for older local databases.
| Table | Purpose |
|---|---|
players |
Account credentials, level, XP, Souls, unlocks |
player_sessions |
Bearer/session tokens and expiration support |
player_demons |
Permanent owned demon collection |
runs |
Dungeon state, rewards, combat history, and status |
Run state and rewards are stored as JSON text in the runs table.
| File | Purpose |
|---|---|
public/app/js/session.js |
Shared session storage and authenticated API helper |
public/app/js/navigation.js |
Public demon browser navigation |
public/app/js/index.js |
Static demon type browser |
public/app/js/auth-ui.js |
Login and register forms |
public/app/js/play-ui.js |
Account dashboard, progression, compact collection, admin check |
public/app/js/collection-ui.js |
Full collection filters, sorting, and missing-slot display |
public/app/js/summon-ui.js |
Authenticated Souls/summon placeholder state |
public/app/js/rankings-ui.js |
Leaderboard UI |
public/app/js/hunt-ui.js |
Dungeon UI, battle replay, drag/drop, recruitment, cashout, final save |
public/app/js/demon-cards.js |
Shared demon card rendering |
public/app/js/api-test.js |
Manual API test page helper |
| File | Purpose |
|---|---|
public/api/lib/auth.js |
Password hashing, token creation, auth middleware |
public/api/lib/async-errors.js |
Express async error forwarding |
public/api/lib/combat.js |
Server-side combat simulator |
public/api/lib/db.js |
MySQL connection pool and .env loading |
public/api/lib/demon-factory.js |
Demon generation, rarity selection, stat rolls |
public/api/lib/game-data.js |
JSON game-data readers |
public/api/lib/hunt-enemies.js |
Dungeon enemy pool and floor sizing |
public/api/lib/rng.js |
Deterministic seeded RNG helpers |
public/api/lib/run-demons.js |
Run demon normalization and reset helpers |
public/api/lib/runs.js |
Run loading, serialization, and persistence helpers |
public/api/lib/schema.js |
Database initialization and additive schema checks |
Check the server entrypoint:
node --check server.jsCheck backend API files in PowerShell:
Get-ChildItem -Recurse -Filter *.js public\api | ForEach-Object { node --check $_.FullName }Check frontend scripts in PowerShell:
Get-ChildItem -Recurse -Filter *.js public\app\js | ForEach-Object { node --check $_.FullName }Initialize or verify the database schema from the command line:
node -e "require('./public/api/lib/schema').initializeSchema().then(() => { console.log('schema ready'); process.exit(0); }).catch((error) => { console.error(error); process.exit(1); })"api.mdis older planning documentation and does not fully reflect the current implementation.idea.mdcontains original gameplay notes, not a strict implementation contract.- Some internal code still uses
huntnaming while the user-facing experience is nowDungeon. /summonexists as an authenticated page, but summon spending/creation is not implemented yet./admin/demon-balanceis intentionally a non-mutating placeholder.
All rights reserved. Copyright 2026 Among Demons.