3.7 KiB
Newbury Nights
A fan-made AR ghost-hunting web app — a tribute to the AR mechanics of the LEGO® Hidden Side™ app. The focus is the Hunter Mode AR loop: raise your phone as a ghost detector, scan the colour wheel to uncover gloom, lock on, and blast ghosts down before your battery dies.
Fan-made tribute. Not affiliated with, sponsored by, or endorsed by the LEGO Group. LEGO® and Hidden Side™ are trademarks of the LEGO Group.
What's inside
- Backend — Node + Express 5, SQLite via
better-sqlite3, bcrypt password hashing, JWT auth (8h), multer image uploads. - Frontend — Three.js (ES module import maps via CDN),
getUserMediacamera passthrough withDeviceOrientationgyro look (iOS Safari friendly),BarcodeDetectorQR scanning with a manual code fallback. - Ghost rendering — animated-GIF billboards (texture pumped via
texture.needsUpdateeach frame) when a ghost has an uploaded image; procedural Three.js wisp meshes otherwise. - Spawning — rarity-weighted: ★ common down to ★★★★ legendary.
- Admin panel (
/admin) — JWT-gated. Upload / enable / disable / delete ghost images, create sets with scan codes linked to ghost rosters (many-to-many viaset_ghosts). - Public endpoint —
GET /api/scan/:coderequires no auth and returns a set's ghost roster.
Data
The ghost roster, stats, abilities, and boss→set references are seeded from data/*.json, which are generated from the source spreadsheet by scripts/extract_ghosts.py. Three ghost types (red / yellow / blue) and four rarity tiers drive the colour-wheel and damage mechanics.
To regenerate the JSON from a spreadsheet:
python3 scripts/extract_ghosts.py path/to/Ghost_Data.xlsx data
Setup
Requires Node 18+ and build-essential on Ubuntu (so better-sqlite3 can compile its native module):
sudo apt-get install -y build-essential
npm install
cp .env.example .env # then edit JWT_SECRET and admin creds
npm run seed # creates the DB, seeds ghosts/sets, bootstraps admin
npm start
The app listens on PORT (default 3000) over plain HTTP — nginx terminates HTTPS in front of it. app.set('trust proxy', 1) is set so secure cookies work behind the proxy.
Re-running npm run seed is safe: it only seeds ghost/set data when the ghosts table is empty, so it won't clobber admin edits. It always ensures an admin user exists.
nginx
See deploy/nginx.conf.example for a reverse-proxy block. Camera and gyro APIs require a secure context, so the site must be served over HTTPS (which nginx already handles for you).
API quick reference
Public:
GET /api/scan/:code— set roster + boss for a scan codeGET /api/freehunt?n=&type=— rarity-weighted random spawnsGET /api/ghosts?type=&rarity=&boss=— public ghost index (enabled only)GET /api/abilities— ability reference
Auth:
POST /auth/login·POST /auth/logout·GET /auth/me·POST /auth/change-password
Admin (JWT required):
GET/POST /api/admin/ghosts,PATCH/DELETE /api/admin/ghosts/:id,POST /api/admin/ghosts/:id/imageGET/POST /api/admin/sets,PATCH/DELETE /api/admin/sets/:id,PUT /api/admin/sets/:id/roster
Project layout
server.js Express app + static hosting
db/index.js SQLite schema + connection
routes/ auth, public api, admin api, auth middleware
scripts/extract_ghosts.py xlsx -> data/*.json
scripts/seed.js seed DB from data/*.json + bootstrap admin
data/ ghosts.json, abilities.json, sets.json
public/ index.html (game), admin.html, css/, js/
uploads/ uploaded ghost billboards (gitignored)