A Witcher veteran's obsessive love letter to monsters, preparation, lore, and the Path.
An immersive, static dark-fantasy journal containing 136 creatures, handcrafted motion, and 172 original generated visual assets.
Read the engineering field guide · Download the 31-page PDF
Vespera began with a simple frustration: a Witcher bestiary should not feel like a spreadsheet with monster names attached to it.
I have spent years with this world—reading the books, playing the games, learning which blade to draw, which oil to prepare, which Sign buys one more second, and which creature is clever enough to make every rule unreliable. This project was built from that kind of familiarity and affection.
It is not a portfolio exercise and it is not a commercial product. It is the website I wanted to open as a Witcher fan: part field journal, part game menu, part candlelit warning left behind by another hunter.
The goal is not merely to look like dark fantasy. The goal is to reproduce the ritual:
- Enter through darkness and fog.
- Open the journal.
- Search the catalogue by name, category, weakness, habitat, or location.
- Study the creature before drawing a blade.
- Prepare the correct oil, bomb, potion, and Sign.
- Follow the trail to the next monster.
Every parchment line, slow reveal, drifting particle, creature movement, icon, and transition exists to support that feeling.
- 136 creature entries across beasts, cursed ones, draconids, elementa, hybrids, insectoids, necrophages, ogroids, relicts, specters, and vampires.
- A cinematic opening sequence with fog, ambience, medallion reveal, and a scroll-to-open journal transition.
- A six-track audio system that opens with The Trail, skips measured leading silence, continues across routes, and falls back to original Web Audio themes. Its draggable player remembers its position and provides Play/Pause and Next.
- A synthesized parchment page-turn accent shared by click, scroll, keyboard, and touch entry, guarded so one journal opening produces exactly one sound.
- A click-only silver-sword card accent on monster selection, mixed on an independent effect bus so it never ducks or pauses the soundtrack.
- A searchable parchment catalogue with sticky category navigation.
- Individual monster studies with lore, danger, habitat, locations, harvest, sword choice, oils, bombs, Signs, and known encounters.
- Continuous previous/index/next navigation plus visible and keyboard arrow switching for reading the bestiary like a real volume.
- Per-creature portrait motion derived from the monster slug and category, so the studies do not all breathe, drift, or bend in the same way.
- Responsive layouts, keyboard focus states, reduced-motion support, lazy asset loading, and a completely static production export.
This project does not use one repeated placeholder portrait. The visual library was built creature by creature.
| Artwork group | Count | Purpose |
|---|---|---|
| Monster portraits | 136 | One high-detail transparent study for every creature in the catalogue |
| Fieldcraft artwork | 33 | Witcher Signs, oils, bombs, potions, and combat effects |
| Signature artwork | 3 | The Vespera medallion, journal atmosphere, and steel/silver sword composition |
| Original generated artwork | 172 | Unique visual assets created for the experience |
| Derived catalogue thumbnails | 136 | Lightweight WebP versions produced from the full portraits for fast browsing |
The image work was iterative. Creatures were researched, prompted, reviewed, regenerated when the anatomy or silhouette felt wrong, separated from chroma backgrounds, cleaned to transparency, reframed for the journal, and converted into delivery-sized WebP assets.
Even small objects received that attention. The Vampire Oil bottle, for example, was rejected when its silhouette was damaged, rebuilt as a complete asset, cleaned again, and re-integrated. The same care went into the medallion, the steel and silver swords, Signs, bombs, oils, and category presentation.
The full portraits remain detailed on monster pages, while the catalogue uses separate 7–27 KB thumbnails. That distinction preserves the artwork without making the first page download the entire gallery.
OpenAI Codex was used as an active engineering and creative-production collaborator throughout the build.
Codex helped:
- inspect and structure the source material;
- build and validate the JSON data pipeline;
- create the Next.js architecture and reusable components;
- develop the motion language for the opening, journal, portraits, fog, and fieldcraft;
- direct and integrate the generated-image workflow;
- remove backgrounds and validate transparent assets;
- research missing bestiary details and encounter context;
- audit all 136 routes for consistency;
- diagnose navigation stalls and duplicate local processes;
- reduce public assets from roughly 189 MB to 35 MB;
- prepare the static export, documentation, and repository hygiene.
The result was not produced by asking for a website once and accepting the first answer. It came from many rounds of comparison, rejection, rewriting, regeneration, testing, and polishing—the same stubborn loop any Witcher player recognizes from preparing for a contract that looked easier on the notice board.
| Layer | Technology | Role |
|---|---|---|
| Application | Next.js 15 App Router | Static routes, metadata, and production export |
| Language | TypeScript + React 19 | Strict application and component architecture |
| Styling | Tailwind CSS + handcrafted CSS | Layout, parchment, fog, candlelight, ink, and responsive behavior |
| Motion | Framer Motion | Opening sequence, journal transition, portrait movement, and UI presence |
| Scroll animation | GSAP + ScrollTrigger | Section reveals and ink-line animation |
| Smooth scrolling | Lenis | Cinematic wheel behavior with hidden-tab and reduced-motion safeguards |
| Soundscape | HTML Audio + Web Audio API | Shuffled playlist, silence skipping, page SFX, and silver-sword interactions |
| Data validation | Zod | One consistent contract across every monster JSON file |
| Icons | React Icons | Structural fallback glyphs and interface symbols |
| Data pipeline | Python, PyMuPDF, RapidOCR, ONNX Runtime | Local document rendering, OCR, extraction, and normalization |
| Image pipeline | OpenAI image generation + Pillow | Original visual production, alpha cleanup, resizing, and WebP delivery |
| Development collaborator | OpenAI Codex | Architecture, implementation, research, asset workflow, QA, and optimization |
| Deployment | Vercel | Global CDN delivery of the fully static production export |
There is no database, CMS, authentication layer, analytics dependency, or runtime bestiary API. The website consumes static JSON and exports as portable HTML, CSS, JavaScript, and WebP assets.
app/ Next.js routes, metadata, and global visual system
components/bestiary/ Catalogue and creature-detail experiences
components/experience/ Opening, ambience, medallion, motion, and scrolling
data/<category>/ One validated JSON document per creature
lib/ Zod schema and server-side data access
public/monsters/generated/ 136 optimized full creature portraits
public/monsters/thumbs/ 136 lightweight catalogue thumbnails
public/icons/fieldcraft/ 33 optimized Signs, oils, bombs, and effects
public/images/ Signature medallion, journal, and sword artwork
scripts/ Extraction, enrichment, validation, and optimization
generateStaticParams creates every creature route at build time. The browser
never receives a database connection or waits for monster data from an API.
The catalogue receives compact summaries instead of complete records. Offscreen
cards use content-visibility, full portraits load only on their creature
routes, and expensive layout measurement was removed from the complete
136-card grid.
More implementation detail is available in docs/ARCHITECTURE.md.
The complete build retrospective—including the PDF/OCR method, data normalization, image batches, difficult bugs, code paths, temporary diagnostic snippets, performance work, audio iterations, testing, and deployment—is available as:
Requirements:
- Node.js 20+
- npm
- Python 3.11+ only for the data, optimization, and static-preview scripts
git clone https://github.com/soufianfallah/Vespera-The-Bestiary.git
cd Vespera-The-Bestiary
npm install
npm run devDevelopment runs at http://localhost:3000.
Vespera uses a six-track soundtrack:
public/audio-local/Kaer Morhen (From The Witcher 3 - Wild Hunt).mp3
public/audio-local/The Fields of Ard Skellig (Midnight).mp3
public/audio-local/1-11. Bad News Ahead Full.mp3
public/audio-local/1-13. CS001 Geralt and Yen.mp3
public/audio-local/01. The Trail.mp3
public/audio-local/12. The Nightingale.mp3
public/audio-local/ is excluded from Git to keep large binary media outside
the source repository. Direct Vercel deployments include the local files;
builds without them automatically use Vespera's procedural themes. Every page
load requests The Trail first, measured leading silence is skipped, and the
draggable player provides Play/Pause and Next controls. The six-track playlist
wraps continuously. Playback is requested on load and retried on the first
interaction when a browser's audible-autoplay policy blocks it.
npm run typecheck
npm run lint
npm run data:validate
npm run build
npm run previewThe optimized production preview runs at http://localhost:3001.
| Command | Purpose |
|---|---|
npm run check |
Type-check, lint, and build the complete application |
npm run preview |
Serve the exported production site on port 3001 |
npm run assets:optimize |
Build WebP portraits, thumbnails, icons, and featured assets |
npm run assets:favicon |
Rebuild browser and Apple icons from the Vespera medallion |
npm run data:validate |
Validate every committed monster document |
npm run data:extract |
Build structured creature data from a local source PDF |
npm run data:enrich |
Apply reviewed research and enrichment data |
To rebuild repository-ready artwork and remove validated source PNGs:
python scripts/optimize_assets.py --prune-sourcesThe original local reference PDF is intentionally excluded from Git. The committed JSON and optimized assets are sufficient to build and deploy the website.
Monster information lives outside React components. Each creature follows the same schema, category normalization, navigation behavior, and rendering path. That separation keeps the experience handcrafted without turning the codebase into 136 hardcoded pages.
Vespera was built because this world still has the power to make preparation feel meaningful.
The bestiary in The Witcher is not decoration. It is the difference between meeting a monster and understanding one. It turns folklore into tactics and fear into preparation. This project tries to honor that idea—not by copying a game menu, but by building an original journal with the same patience, menace, and respect for the hunt.
If you notice an unnecessary detail, it is probably there because a Witcher fan argued with himself about it for far too long.
Vespera is an unofficial, personal, non-commercial fan project. The Witcher and its related intellectual property belong to their respective rights holders. This project is not affiliated with or endorsed by CD PROJEKT RED, CD PROJEKT, or Andrzej Sapkowski. See NOTICE.md.

