A new engine for The Elder Scrolls V: Skyrim Special Edition that runs in the browser
(TypeScript and WebGPU). It contains no game content: everything it shows and plays (models,
textures, sounds, music, records, scripts) is read from your own installation of the game, on
your own machine. Like OpenMW for Morrowind, it is a reimplementation of
the engine only, and needs a legitimately owned copy of Skyrim Special Edition. It runs on
localhost only: nothing is uploaded anywhere.
Status: early development. The game starts as the original does — its launcher, the intro, the main menu, a new game — and its quests and scripts run in the engine's own Papyrus virtual machine, with the game's own menus (HUD, inventory, map, journal, dialogue, ...). The worlds can be walked through with their people and creatures standing in them, animated by the game's own behavior graphs. They do not act yet: AI (packages, combat) is next, so the opening's cart does not drive to Helgen. See Milestones.
- Game data: the load order (ESM, ESP, ESL and Creation Club content), BSA archives with LZ4, loose files that override archives, and the records of every plugin.
- Rendering (WebGPU): models with the game's materials, textures (BC1–BC7), alpha, decals, particles and animated objects; interiors with their lighting and image spaces; exteriors with terrain and its texture blending, LOD, grass, water, sky, clouds, weather, shadows and post-processing.
- Playing: the player in first and third person with the game's controls and movement speeds, collision and physics (Rapier) built from the models' Havok data, doors between cells and worldspaces, swimming, picking up and holding objects.
- Audio: sound effects in 3D with reverb by acoustic space, ambience, water, impacts, footsteps by ground material, and the music system with its types and crossfades.
- Actors: people and creatures dressed and skinned on their race's skeleton, with FaceGen heads; the game's Havok animations and behavior graphs (also of animated objects, from packfiles and Creation Club tagfiles); blinking, moods and lip-synced voice lines; the player's own body in third person; ragdolls and hinged objects held by their constraints, the dead lying as ragdolls; actors where their AI packages have them as their cells load (on their posts, in their ambush alcoves, away when asleep elsewhere).
- Scripts and quests: the game's compiled Papyrus scripts (PEX) in the engine's own virtual machine, with its threads, events and the game's native functions; quests with their stages, objectives, aliases and scenes, dialogue with its conditions, the story manager, the game's clock and globals.
- Menus: the game's own Scaleform menus (
interface\*.swf) played in Ruffle, the engine standing in for Scaleform: the main menu with its intro, the loading screens, the HUD, the tween menu, inventory, magic, favorites, containers, barter, the map and local map, the journal with its settings and controls, dialogue, books, crafting, lockpicking, skills and levelling up, training, sleeping and waiting, message boxes, tutorials, character creation and the console, with the game's keyboard and mouse controls.
| Milestone | What | State |
|---|---|---|
| M0 | Vite, TypeScript and WebGPU; picking the game folder | done |
| M1 | Virtual file system, BSA reader (LZ4), load order | done |
| M2 | NIF and DDS: models with materials and alpha | done |
| M3 | ESM records; interior cells | done |
| M4 | Tamriel's exterior: terrain, streaming, LOD, water, sky, weather | done |
| M5 | The player: first and third person, collision, physics | done |
| M6 | Audio: xWMA, 3D sound, the music system | done |
| M7 | Actors: skinning, Havok animations and behavior graphs, faces, lip sync | done |
| M8 | The Papyrus virtual machine: quests and scripts as in the original | done |
| M9 | The game's own menus (Scaleform through Ruffle) | done |
| M10 | AI: packages, navigation meshes, combat, detection, schedules | next |
| M11 | Gameplay: skills, perks, magic, shouts, crafting, crime, factions | |
| M12 | Savegames (own format, then importing .ess saves), mod load order |
|
| M13 | Polish: performance, memory, edge cases, the main quest from start to end |
How the engine reads each format and why it behaves as it does is written down in
docs/: file formats, rendering,
physics, audio, animation,
AI, scripting and the menus.
- Node 24 or newer
- Chrome or Edge on a desktop (WebGPU and the File System Access API)
- The Elder Scrolls V: Skyrim Special Edition (or Anniversary Edition), installed: the folder
with
Data\,Skyrim.cccandSkyrim_Default.ini
git clone <this repository> itjustworks-webengine
cd itjustworks-webengine
npm install
npm run devOpen http://localhost:5173 and choose the game's installation folder (choosing its Data
folder also works, without Creation Club content). The browser keeps access to the folder for
the next visit.
- Play is the game itself: its launcher (Options: windowed or full screen), the intro and
the game's main menu. New starts the opening, which waits for AI (M10): the cart does not
drive yet. To play freely, open the console at the main menu (
~) and typecoc Riverwood(or any cell): as in the original, a new game starts there without the opening. The brand in the top left corner leaves the game. - Sandbox opens any model, interior cell or worldspace, with a developer panel (time, weather, quest and scene states). Click the scene to play; H hides the panels.
The controls are the game's PC defaults (the control map, remappable on the journal's Controls
page): WASD to move, the mouse to look, Space to jump, Shift held to walk, Alt to sprint, Caps
Lock to always run, Ctrl to sneak, E to activate, F for the camera, Tab for the tween menu, I,
P, M and J for inventory, magic, map and journal, Q for favorites, T to wait, Esc to pause and
~ for the console. In full screen the page keeps the game's keys (Keyboard Lock); in a
window the browser releases the mouse on Esc, which pauses the game.
To start over from the folder picker (a first run), clear the site's data: in Chrome or Edge,
open the developer tools (F12) → Application → Storage → Clear site data, then reload.
http://localhost:5173/?reset does the same.
The tools and integration tests read a copy of the game's Data folder outside the repository.
Point them at it with the SKYRIM_DATA environment variable, or put the path on one line in a
file .gamedata in the repository root (ignored by git):
D:\Games\Skyrim Special Edition\Data
Without it the integration tests are skipped. With it, http://localhost:5173/?devdata skips the
folder picker: the dev server serves that folder read-only on /@gamedata/ (dev server only,
never in a build).
| Command | What it does |
|---|---|
npm install |
Installs dependencies and enables the git hooks |
npm run dev |
Dev server at http://localhost:5173 |
npm run build |
Typecheck and a production build in dist/ |
npm test |
Unit and integration tests (the latter only with game data) |
npm run check |
Typecheck, lint, formatting, tests and the game-file check |
node tools/data-info.ts |
Checks the local Data folder |
node tools/vfs-info.ts [path] |
Load order, archives, and where a file comes from |
node tools/bsa-list.ts <bsa> --verify |
Contents of an archive; verifies and extracts everything |
node tools/nif-stats.ts --parse |
Block types of all meshes; parses every block |
node tools/navmesh-info.ts --verify |
Reads every navigation mesh |
node tools/hkx-info.ts --audit |
Loads and starts every behavior graph a mesh names |
node tools/actor-info.ts <npc> |
An actor's look, skeleton and behavior graph |
node tools/package-info.ts <actor> |
An actor's AI packages and where they have it |
node tools/music-info.ts [type] |
Music types and tracks of the load order |
node tools/pex-dump.ts --verify |
Parses every compiled script; disassembles one |
node tools/papyrus-run.ts |
Runs a new game's scripts in Node and reports what they did |
node tools/quest-info.ts <quest> |
A quest's stages, objectives and aliases |
node tools/dialogue-info.ts <quest> |
A quest's dialogue topics, responses and voice files |
node tools/swf-info.ts [movie] |
The interface movies: stage, exports, imports, fonts |
node tools/controlmap-info.ts --all |
The control map's contexts and bindings |
node tools/spawn-audit.ts [filter] |
Arrives through every load door; walks through doors |
node tools/stairs-audit.ts [filter] |
Walks the player up and down every staircase of the interiors |
Every parser has a tool like these that runs it over the real game files, and is tested against them before anything is built on it.
src/
formats/ parsers (BSA, NIF, DDS, ESM, HKX, PEX, audio): no browser APIs, they run in Node
vfs/ the virtual file system: loose files over archives, load order
world/ records, cells, worldspaces, streaming, weather, navigation meshes
render/ WebGPU: shaders, terrain, water, sky, particles, LOD, post-processing
physics/ Rapier: collision from the Havok data, the character controller
audio/ Web Audio: 3D sound, reverb, music
animation/ skeletons, the Havok Behavior runtime, faces and lip sync, tree sway
scripting/ the Papyrus virtual machine and the game's native functions
ai/ AI packages: where they put actors
gameplay/ inventory, crafting, character creation, fast travel and other game rules
ui/ Ruffle and the Scaleform bridge: the game's menus, their keys and mouse
shell/ the web shell, Play (the game's menus in play) and the sandbox viewers
workers/ parsing off the main thread
tools/ command-line tools over the local game files
tests/ integration tests against the local game files
docs/ formats and engine behaviour
Game files never go into this repository, not even renamed or converted: no archives, plugins,
models, textures, sounds, scripts, interface files, INI files or anything extracted from them.
A pre-commit hook (.githooks/pre-commit, enabled by npm install) and npm run check
reject them by name, extension and content; continuous integration also checks every commit
of the history (npm run check:history).
Issues and pull requests are welcome. Please never attach game files or anything extracted
from them, also not to issues. Code, comments, docs and commit messages are in English; run
npm run check before sending a change. Engine behaviour is derived from documentation
(UESP, nif.xml, xEdit, community tools) and written down in docs/; what is not known yet is
marked with a TODO naming the milestone that settles it.
Copyright (C) 2026 stenvro
ItJustWorks-Webengine is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version (LICENSE). It is
distributed in the hope that it will be useful, but without any warranty.
Code adapted from other projects (Community Shaders, webgl-noise, Separable SSS, the SAO
reference implementation) and the dependencies with their licences are listed in
THIRD_PARTY_NOTICES.md, with the documentation the engine was
written from.
The Elder Scrolls, Skyrim and Bethesda are trademarks of ZeniMax Media Inc. This project is not affiliated with or endorsed by ZeniMax Media, Bethesda Softworks or Microsoft. It does not contain, and will never provide, any part of the game: you need your own legitimately owned copy.