An interactive, gamified résumé. Roll a cube across a 3D tile grid (Three.js); a static HTML résumé rendered from JSON serves as the no-JavaScript fallback. One Vite build, no Python.
Demo — Game · Static résumé
| Stack | Entry | Purpose |
|---|---|---|
| Game (primary) | index.html + src/ |
Three.js + TypeScript puzzle résumé, built with Vite. |
| Static fallback | resume.html + src/resume/render.ts |
Rendered from assets/resume.json at build time by a Vite plugin; what recruiters land on without WebGL/JS. |
assets/resume.json is the single source of truth: the game's src/game/levels.json
references it with JSONPath ($…) strings, resolved at build time by the
resume-refs Vite plugin in vite.config.ts.
All static assets live in assets/ — the models (assets/models/), the avatar
(assets/harishankar.jpeg), and resume.json. It is Vite's publicDir, so it is served
in dev and copied into dist/ on build (no manual copy step in the workflow).
npm install
npm run dev # Vite dev server at http://localhost:5173 (game + /resume.html)
npm run build # tsc --noEmit + vite build → dist/ (game index.html + resume.html)Deployment is manual: run npm run build to produce dist/ (the game
index.html and resume.html side by side, with models/, the avatar, and
resume.json alongside), then publish dist/ to your host. CI
(.github/workflows/ci.yml) runs the quality checks only; it does not deploy.
Source layout under src/:
core.ts— shared primitives: theDirectionandTileKindtypes,DIRECTION_DELTA, thenoise()hash, and thePALETTEcolour map.engine/— Three.js infrastructure:scene.ts(camera/lighting/renderer) andmodels.ts(OBJ/MTL loading).game/— gameplay:layout.ts(Three.js-free board parsing/validation),grid.ts(level + tile rules),player.ts(rolling cube),tile.ts,contact.ts,decoration.ts(animated tile overlays),effects.ts(projectiles/swirls).
Built by the same npm run build (and live under npm run dev): the static-resume Vite
plugin renders assets/resume.json into resume.html, so it ships as plain HTML — no Python.
- Content: edit
assets/resume.json(JSON Resume schema). - Layout: edit
src/resume/render.ts(the body markup; unit-tested inrender.test.ts) orresume.html(the page shell + CSS). The avatar is a plain<img>frombasics.picture. - Styling uses the shared design system via
/design-system/tokens.css(with fallbacks).
The puzzle ladder is produced offline by the level-gen/ crate (solver + campaign +
curator) and wired into the game with level-gen/wire_ladder.py. See
level-gen/README.md for the workflow and
levels/README.md for the candidate → curated → wired lifecycle.
Quick reference for the three stacks:
# TypeScript game
npm run typecheck && npm test && npm run build
# Python helper (level-gen/wire_ladder.py)
pip3 install -r requirements-dev.txt
ruff check . && ruff format --check .
# Rust level generator
cd level-gen && cargo fmt --check && cargo clippy --lib --bins --tests -- -D warnings && cargo testCI (.github/workflows/ci.yml) runs the same checks on every PR and push to master.