Play CircuitLord's Titanfall 2 VR mod on Linux. The official installer only runs on Windows. This repo does the same job on Linux, and fixes the crashes the mod hits under Proton.
- Your normal Titanfall 2 stays unmodded. The mod lives in its own
TF2VRprofile, with its own campaign save. - Everything comes from the official release links, checked against the official checksums. This repo contains no game or mod files.
- An x86_64 Linux PC with a current distro (glibc 2.38 or newer, details). A standalone Steam Frame or other ARM device can't run this (why).
- Titanfall 2 on Steam, or from the EA app run through Faugus, Heroic, Lutris or Bottles (without Steam)
- Proton Experimental, installed in Steam: Library → search "Proton Experimental" → Install. Without Steam,
install.shdownloads a compatible Proton itself. - A VR runtime: SteamVR (Settings → OpenXR → "Set SteamVR as OpenXR runtime"), or WiVRn / Monado
- A few tools:
- Arch, CachyOS, EndeavourOS:
sudo pacman -S --needed python unzip cabextract - Debian, Ubuntu:
sudo apt install python3 curl unzip cabextract - Fedora:
sudo dnf install python3 curl unzip cabextract
- Arch, CachyOS, EndeavourOS:
- About 2 GB of free disk space
- Start Titanfall 2 once from Steam, the normal way. Sign in to the EA app, wait for the main menu, then quit.
- Run the installer:
It checks your system first and tells you if anything is missing.
git clone https://github.com/Monkellie/tf2vr-linux.git cd tf2vr-linux ./install.sh - Restart Steam, then set Titanfall 2 → Properties → Compatibility → Proton-TF2VR.
If you own Titanfall 2 on the EA app and run it with Proton through Faugus, Heroic, Lutris or Bottles, nothing here needs Steam:
- In your launcher, install the EA app and sign in, then install Titanfall 2 with it (on any drive, an SD card included) and start the game once the normal way. Then quit.
- Run the installer:
It looks through the Wine prefixes of Faugus (
git clone https://github.com/Monkellie/tf2vr-linux.git cd tf2vr-linux ./install.sh~/Faugus), Heroic and Lutris (~/Games), Bottles and~/.wine, and through Steam'scompatdataif you added the EA app to Steam as a non-Steam game. It reads where the EA app installed Titanfall 2 from each prefix's registry, so it also finds the game on another drive or an SD card. If it finds none or several, point it at yours:./install.sh --game /path/to/Titanfall2 --prefix /path/to/prefix. If you have no compatible Proton installed, it downloads GE-Proton11-1 (about 530 MB, checked against its published checksum) and builds Proton-TF2VR from it, in~/.local/share/Steam/compatibilitytools.d. - In your launcher, set the EA app's (and Titanfall 2's) Proton to Proton-TF2VR, so the normal game and VR use the same Wine. If it isn't listed, choose that folder as a custom Proton.
- Play with
tf2vr, as below. In Heroic you can also add~/.local/bin/tf2vras a native Linux app.
Starting the game from your launcher keeps starting the normal, flat game.
Put your headset on, then run tf2vr in a terminal or pick Titanfall 2 VR in your app menu. It starts SteamVR and the EA app for you, and closes the EA app when you quit.
Steam's Play button always starts the normal, flat game. VR only starts through
tf2vror the Titanfall 2 VR menu entry.
For mod settings, controls and help with the mod itself, see CircuitLord's Discord.
cd tf2vr-linux
git pull
./install.shSwitch Titanfall 2 back to your old Proton in Steam (Properties → Compatibility), or in your launcher without Steam, then run ./install.sh --uninstall. Your VR campaign saves are kept.
| What you see | What to do |
|---|---|
| The game starts flat | You used Steam's Play button. Start VR with tf2vr. |
tf2vr: command not found |
Run ~/.local/bin/tf2vr, or add ~/.local/bin to your PATH. |
install.sh prints FAIL |
Install what it names, then run ./install.sh again. |
| The game says you're logged out of Origin, then quits | Update to the latest scripts. If the EA app's window asks you to sign in, do. (why) |
Crash with code 0xC0000409 (3221226505) |
Check Titanfall 2 is set to Proton-TF2VR in Steam, then run ./install.sh --refresh-proton. (why) |
Crash in MSVCP140.dll right after the mod loads |
Run ./install.sh again. (why) |
install.sh: couldn't install the VC++ runtime |
Install cabextract and run ./install.sh again, or run protontricks 1237970 vcrun2022. |
engine.txt stops at stage=openxr_instance |
Start SteamVR (or WiVRn / Monado), check the headset is detected, and check it's set as the OpenXR runtime. |
| The game runs, but the headset shows nothing | VR needs DXVK. Check vulkaninfo --summary shows Vulkan 1.3. (why) |
| Controllers do nothing, and no hands show (Monado, WiVRn) | The mod doesn't support your controller type outside SteamVR yet. (which ones) |
A small white EA box stays on screen, or install.sh says the EA app is running |
Run tf2vr once; it clears what older versions left behind. (why) |
Another mod replaced DirectX files |
Remove dxgi.dll and d3d11.dll (ReShade and similar) from the game folder. |
write dump: Invalid parameter |
Harmless; look for the real cause in the logs below. (why) |
| The game closes about a minute after it starts, every time | Happens when your Steam library is on another drive. Update to the latest scripts. |
| Achievements don't unlock in VR | Update to the latest scripts. (why) |
| Discord shows SteamVR instead of Titanfall 2 | Update to the latest scripts. (why) |
| Steam isn't found | Run STEAM_DIR=/path/to/Steam ./install.sh. |
install.sh: Titanfall 2 not found or installed in more than one place |
Point it at your copy: ./install.sh --game /path/to/Titanfall2 --prefix /path/to/prefix. (without Steam) |
| Which Wine prefix is mine? | The one the EA app is installed in. install.sh lists those it finds. To find it yourself: grep -l Respawn ~/.local/share/Steam/steamapps/compatdata/*/pfx/system.reg (for a non-Steam game in Steam; add your other Steam libraries' compatdata paths) prints …/compatdata/<number>/pfx/system.reg, and …/compatdata/<number> is the prefix. |
install.sh: The audio fix doesn't match |
That Proton build compiles the audio code differently. Leave out --proton-base, and install.sh uses Proton Experimental or GE-Proton11-1. (why) |
Proton's Wine can't run directly on this system |
You're on an ARM device. (see here) |
install.sh: glibc … is too old |
Your distro release is too old for the current Proton. Upgrade to one with glibc 2.38 or newer. (which ones) |
Still stuck? Open an issue with these files. Collect them after the game has closed, or they'll be cut off:
~/.local/state/tf2vr/launch.logTitanfall2/TF2VR/plugins/Titanfall2VR-data/engine.txt- the newest
Titanfall2/TF2VR/logs/nslog*.txt - the newest folder in
Titanfall2/TF2VR/crashes/
Titanfall2/ is <Steam library>/steamapps/common/Titanfall2, or the folder the EA app installed it to. For a Proton log as well, launch with tf2vr --proton-log; it's saved to ~/.local/state/tf2vr/steam-1237970.log.
It follows the official installer (Titanfall2Installer.cs) step by step:
- It reads the current mod release from CircuitLord's
manifest-v3.json, and the Northstar version the official installer pins at that release. - It downloads both and checks their SHA-256 checksums.
- It runs the mod's own
asset_patcher.exeunder Proton, which builds the mod's game assets from your game files, then checks every file it built. - It lays the files out the same way: Northstar and the mod in
Titanfall2/TF2VR, andTitanfall2VRLauncher.exenext to the game.Titanfall2.exeis never touched.
On top of that, it:
- copies Proton Experimental to
compatibilitytools.d/Proton-TF2VRand applies the audio fix to that copy only - installs Microsoft's current VC++ runtime into the Titanfall 2 prefix, if the one there is too old
- adds the
tf2vrcommand in~/.local/binand the Titanfall 2 VR app-menu entry - adds
~/.local/share/tf2vr/discord-presence.py, which the launcher uses to set your Discord status
./install.sh --check runs only the checks and changes nothing. When you update, it installs a new mod release if there is one, and removes files the old version used that the new one doesn't. ./install.sh --force reinstalls from scratch.
For the audio fix. The mod captures game audio through ActivateAudioInterfaceAsync("VAD\Process_Loopback"), probably for haptics. Wine rejects that device, and the mod then crashes with 0xC0000409. polar421 found that changing one jump in Proton's mmdevapi.dll (6 bytes at offset 0x369E) sends the request to the default playback device instead, where Wine's PulseAudio driver already supports loopback capture.
The fix goes into the private copy only, after checking the surrounding bytes match, so your other games are untouched. The copy is frozen, so Steam updates can't break it. To rebuild it from a newer Proton Experimental, run ./install.sh --refresh-proton.
The fix only matches some Proton builds: Proton Experimental, Proton Hotfix and GE-Proton11-1, for example, but not newer GE-Proton releases, CachyOS or Wineland, which compile that code differently. install.sh uses the first compatible build it finds: Steam's Proton Experimental, then any compatible build in Steam's, Heroic's or umu's Proton folders. If there's none, it downloads GE-Proton11-1. --proton-base DIR picks a specific build. If the new mmdevapi.dll doesn't match the fix, the script refuses rather than guessing; keep the old copy and open an issue.
Steam (or, without Steam, your launcher) has to be set to Proton-TF2VR as well, because normal and VR launches share one Wine prefix, and switching Wine versions on a prefix breaks things.
The EA app installs msvcp140.dll 14.34 into the prefix. The mod is built with a newer compiler, and with that older runtime its std::mutex use reads a null pointer, so it crashes in MSVCP140.dll right after loading. Runtime 14.40 or newer fixes it, which is what an up-to-date Windows PC has.
Microsoft's installer sometimes does nothing under Wine. Then install.sh copies the DLLs out of it with cabextract or bsdtar, the way winetricks does. The runtime stays in the prefix after you uninstall; it's harmless.
Steam always starts Titanfall2.exe, the normal game. Setting Proton-TF2VR in Steam only picks the Proton build. The VR mod starts through Northstar's launcher instead, the way the official installer's Play button does it. tf2vr does exactly that: crash_monitor.exe starts Titanfall2VRLauncher.exe with the TF2VR profile and the arguments from the mod's launch.json.
The launcher also sets SteamGameId=1237970. Proton's steam.exe only sets up its VR bridge (the OpenXR and Vulkan data wineopenxr reads from HKCU\Software\Wine\VR) for programs it thinks Steam started.
Copying CircuitLord's installer over Titanfall2.exe to run it in Proton is a dead end, and you don't need it: install.sh does the installer's job. Verify the game files in Steam to get Titanfall2.exe back, and remove any PROTON_USE_WINED3D or WINEDLLOVERRIDES launch options you added for it.
Titanfall 2 needs a signed-in EA app, and it checks the sign-in once, when it starts. So the launcher:
- Starts the EA app and waits until it's signed in, then starts the game. Older versions waited a fixed 8 seconds. When the EA app took longer, the game decided you were logged out of Origin and quit (exit code 274).
- Tells the game which EA product it is. When Steam starts Titanfall 2, EA's launcher gives the game
ContentId=Origin.OFR.50.0001456, the EA offer your Steam copy is linked to. Without it, the game falls back to its built-in ID, the EA app can't match the session to your copy, and it refuses every achievement ("Entitlement not found" inEADesktopVerbose.log). The launcher sets the same value. The EA app still checks that you own the game. For EA-app copies it sets nothing, because the built-in ID is the one the EA store uses. - Deletes the EA app's
backgroundservice.inifirst. A session that was ended by force leaves this file behind. If the next EA background service happens to get the same Wine process ID, the EA app trusts the old port in the file and signs in 20 seconds late. - Closes the EA app and its Wine session when you quit.
proton rundoesn't return while the EA app is open, and Steam keeps showing Titanfall 2 as running. So the launcher followscrash_monitor.exeinstead, reports the game's exit code, and then ends the session. - Cleans up after Wine. With the kernel's ntsync driver, which Proton 11 uses whenever
/dev/ntsyncexists, Wine processes that are waiting when the wineserver exits never wake up again. One of them isexplorer.exe, whose tray window keeps showing the EA icon: the white box. The launcher ends whatever outlives the wineserver, after each session and before the next launch.
The launcher doesn't go through Steam, so Steam launch options don't apply, but variables you set globally (for example in /etc/environment or ~/.config/environment.d) do. It removes these, with a warning:
PROTON_USE_WINED3D: VR needs DXVK. Proton's OpenXR bridge hands the game's frames to the headset as DXVK textures, so with wined3d the headset shows nothing. If Titanfall 2 only starts with wined3d, your GPU or driver can't run DXVK, which needs Vulkan 1.3.PROTON_ENABLE_WAYLAND: the EA app and VR are only tested with Wine's X11 driver.MANGOHUD: overlays aren't tested with the EA app and VR.
Discord recognises games by their program name, and it doesn't know Titanfall2VRLauncher.exe. So all it detects during a VR session is SteamVR's status window, or crash_monitor. The mod's own Discord plugin can't fix that: it looks for Discord on a Windows pipe, and under Wine nothing connects that pipe to Linux Discord. Its [DSCRD-RPC] waiting for handshake... line in the nslog never gets an answer.
So the launcher sets your status itself, through Discord's local socket: Playing Titanfall 2 VR, with the chapter, difficulty and play time, under Titanfall 2's icon. Discord ranks a status with details like these above apps it only detected, so this one shows instead of SteamVR, for you and for your friends. It works with the Discord app (native, Flatpak or Snap) and with Vesktop, and it clears when the game closes. launch.log says Discord: showing "Playing Titanfall 2 VR" once it's connected.
SteamVR can still appear as a second activity on your own profile. To remove it completely, turn off its detection in Discord's Settings → Registered Games.
Yes. install.sh reads every library in Steam's libraryfolders.vdf, so the game can be on any drive. It also finds the game's Wine prefix when Steam keeps it in a different library than the game, which Steam does for drives it can't put prefixes on, such as NTFS or exFAT. Steam itself is found in ~/.local/share/Steam, ~/.steam/steam, ~/.steam/root, ~/.steam/debian-installation and the Flatpak location. If it's somewhere else, run STEAM_DIR=/path/to/Steam ./install.sh.
~/.local/binisn't on your PATH by default, so a plaintf2vrisn't found. Use the app-menu entry, run~/.local/bin/tf2vr, or add it to your PATH:export PATH="$HOME/.local/bin:$PATH"in~/.bashrcor~/.zshrc, orfish_add_path ~/.local/binin fish.pythonandunziparen't part of a minimal install:sudo pacman -S --needed python unzip cabextract.- A few libraries come from your system, because the scripts run Proton directly rather than inside Steam's runtime:
gnutls(the EA app needs it to sign in),vulkan-icd-loader,libx11andlibpulse.install.shchecks for them. - The audio fix needs a PulseAudio-compatible sound server. With PipeWire, install
pipewire-pulse. - Arch and CachyOS kernels ship ntsync, which made older versions of these scripts leave hung Wine processes behind after you quit. Update to the latest scripts.
Not on the headset itself. A standalone Steam Frame is an ARM device, so it runs x86 games through FEX emulation inside Steam's own runtime. These scripts start Proton's x86 Wine directly, outside Steam, which can't work there. install.sh stops with "Proton's Wine can't run directly on this system" before changing anything.
Instead, run the game on a PC and stream it to the headset:
- From an x86_64 Linux PC: follow Install on the PC and make SteamVR the active OpenXR runtime. Connect the headset to the PC's SteamVR through Steam's PC VR streaming (on the Frame, with its wireless adapter). Then start SteamVR on the PC and run
tf2vr. - From a Windows PC: you don't need these scripts. Use CircuitLord's official installer, connect the headset to SteamVR, and press Play in the installer.
Streaming a Steam Frame to Linux SteamVR hasn't been tested with these scripts yet. Linux SteamVR ships the Frame's headset and controller profiles and the Steam Link streaming driver, so it should work like any other SteamVR headset. Reports welcome.
Tested on PikaOS (Debian sid) on Wayland, with an NVIDIA GPU, a Valve Index, SteamVR 2.17.10, Proton Experimental 11.0 (2026-09-24), mod 1.0.10 and Northstar 1.31.13. The main menu and the campaign run in VR with tracked controllers and audio.
- Other headsets: polar421 has played the campaign on a Quest 3S through WiVRn with a similar setup. With these scripts, a tethered WiVRn user has got as far as the mod finding the headset and controllers.
- Other distros: anything x86_64 with glibc 2.38 or newer should work: Ubuntu 24.04+, Linux Mint 22+, Pop!_OS 24.04+, Debian 13+, Fedora 39+, and current Arch and its derivatives (see the Arch notes). Steam runs Proton inside its own runtime, which brings a newer glibc, but these scripts run Proton outside it, so your system's glibc has to be new enough. Ubuntu 22.04, Mint 21 and Pop!_OS 22.04 (2.35) and Debian 12 (2.36) are too old.
install.shchecks this. - Untested: SteamOS on x86 (Steam Deck), Flatpak Steam (
~/.var/app/com.valvesoftware.Steam/.local/share/Steam), and EA-app copies through Faugus, Heroic, Lutris or Bottles. - Two GPUs: the game has to render on a GPU that has a monitor attached, and it has to be the same GPU your VR runtime uses.
- Controllers on Monado and WiVRn: the mod has full input bindings only for Valve Index and Oculus/Meta Touch controllers, plus OpenXR's generic profile (hand position, select and menu only). SteamVR remaps any controller onto those, so on SteamVR everything works. Monado and WiVRn don't remap, so Vive wands, WMR controllers and others may get no input in the game (#2). Adding them needs a change in the mod itself; tell CircuitLord on the mod's Discord.
Reports welcome.
Nothing on its own. When the game quits with an error, crash_monitor.exe tries to save a memory dump of it, but the game has already exited, and Wine refuses. Look at the process_exit … code= line above it in monitor.txt, and at the Northstar log, for the real reason.
- CircuitLord for the Titanfall 2 VR mod and its installer (MIT). Everything here follows that installer.
- polar421 for getting the mod running on Proton first, and for finding the
mmdevapi.dllaudio fix. - Northstar, the mod loader the VR mod runs on.
This is a community project. It isn't affiliated with or endorsed by Respawn, EA, Valve or CircuitLord.
MIT: use, copy, modify, share and sell it freely, as long as you keep the license notice. The mod, Northstar and the game have their own licenses and aren't included here.