Native VR port · Meta Quest 3
It runs on the headset. Nothing is streamed from a PC, and it isn't a flat screen floating in front of you. I rebuilt the 1999 engine as a 64-bit ARM binary so it renders real stereo through OpenXR, with the gun in your hand and the whole original game behind it.
Mod by GHWST · @GhwstVR
Pulled straight off the headset. Left eye buffer, 1680×1760 per eye.
What it is
Every map, every weapon, every bot, the menus, the mod support, online play. This is actual Unreal Tournament, not something built to look like it. What I changed is everything sitting between the game and your eyes.
Two eyes, two separate frustums, handed to the compositor as a real projection layer. You can judge distance with it.
The weapon is attached to your controller and fires where you point it. Shots come out of the barrel, not out of the middle of the model.
Moving your head moves the view in the world, so you can lean around a corner without moving your feet.
The menus sit on a panel a couple of metres out, anchored where you opened them, and you point at them with a controller. The HUD stays level with the horizon no matter how you tilt your head.
Alt-fire actually magnifies the view. It also switches your aim to your head and puts the crosshair dead centre, because aiming a scope with a controller at 8x is not workable.
There's a server browser, you can join live servers, and any maps or mods you don't have download and unpack on the headset by themselves.
Screenshots
No mock-ups, no PC renders. These came off a Quest 3 over adb. All I did was crop them.
Get it
This is only the engine. It ships zero Unreal Tournament content, so no maps, no textures, no sounds, no music. You need your own copy of the game and you hand it to the app yourself.
| Headset | Meta Quest 3. Developer mode turned on so you can sideload. |
|---|---|
| You need to own | Unreal Tournament (1999). GOTY or any full install. It's still sold in plenty of places, but you're on your own for that part. |
| Space | A full install runs about 2 GB. The importer copies your files rather than moving them, so leave room for two copies while it runs, then delete the folder you imported from. More again once you start collecting maps online. |
| Controllers | Touch controllers. The gun follows whichever hand you set as your gun hand. |
SideQuest, or adb install -r ut99quest.apk with the headset plugged in.
Two ways to do this. Both end up in the same place.
Easiest: copy your UT99 folder onto the headset, anywhere you like, as a folder or as a zip. Then launch the app. It'll tell you it can't find any game data and give you buttons for Select UT99 ZIP and Select UT99 folder. Point it at what you copied, tap Use this folder and then Allow, and it copies everything into place and launches straight into the game.
Faster if you're comfortable with adb: push the folders straight into the app's own directory and skip the picker entirely. This is where the app looks by default, and it needs no permissions and no setup.
adb push System /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
adb push Maps /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
adb push Textures /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
adb push Sounds /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
adb push Music /sdcard/Android/data/com.ghwstvr.ut99quest/files/UT99/
It comes up in stereo, in the intro cinematic. Everything after that is the normal Unreal Tournament menus and you point at them with a controller.
What it's looking for. A normal UT99 install: System, Maps, Textures, Sounds and Music, with Core.u, Engine.u and Botpack.u inside System. If you point it one folder too high or too low it'll say so and let you try again.
The game has to run from the app's own folder. Anywhere else needs storage permission, which a sideload doesn't always grant, and when it isn't granted the app can't read the files and dies at startup. Staging your copy somewhere like /sdcard/UT99Quest is fine. The importer reads it through the system file picker, which needs no permission. Just let the importer move it in rather than trying to play from there. The adb path above puts it in the right place directly.
Worth saying plainly. This is a hobby port of a 25 year old game that you sideload onto a headset. It's not a store release, there's no support line, and it will do odd things. Back up anything you care about before you point tools at it.
How it works
Unreal Engine 1 was written in 1998 for a 32-bit x86 PC with one screen. Hardly any of the real work here was VR features. Most of it was getting a rendering model that predates programmable shaders to produce something a modern compositor will accept.
The engine assumes 32-bit pointers in places that matter, like struct layouts that got saved to disk as a file format. On 64-bit those go wrong quietly instead of failing to compile.
UE1 has no view matrix, so there isn't one to give two of. The separation comes purely from where each eye sits. Anything that moves an eye has to move the head and add the eye offset back, or the 3D quietly turns flat.
The HUD, console and menus are flat 2D draws. They get mapped onto a plane sitting in the world so they have real depth, and the controller pointer runs that same mapping backwards to work out what you clicked.
The scope divides the eye tangents rather than the angles, because that's what magnification actually is. The field of view handed to the compositor is deliberately left alone. Narrowing both cancels the zoom out completely, which I found out the hard way.
Joining a modded server meant pulling tens of megabytes through UT99's 1999 file transfer. It now downloads over HTTP and unpacks .uz files on the headset. Last measurement was 45 packages, 24.8 MB down and 50.5 MB on disk, in under a minute.
Frame timing is logged per eye. Standing still it sits at the display's full 90 Hz with about 7 ms of the budget spare. A live bot match on Deck16 measures 74 to 90 fps.
Status
It's playable and I'm playing it. It isn't finished. Here's the honest split.