An unofficial head tracking mod for Half-Life 2 that moves the view with your head while your mouse or controller keeps aiming, driven by a webcam, phone, or any OpenTrack compatible tracker, with no VR headset required.
- Decoupled look and aim - your head moves the camera the frame is rendered from, while the mouse or controller still controls where you shoot. The player's own eye angles and position are never written, so weapon fire, traces, physics and NPC behavior are unchanged.
- 6DOF tracking - yaw, pitch and roll, plus positional lean, peek and duck.
- Works with any OpenTrack compatible tracker - free options available for PC, iOS and Android
- Half-Life 2 on Steam (app 220). The supported routes are the default
hl2_completecampaign andsteam_legacy'shl2campaign, validated with the client DLLs dated 27 June 2025. - A tracking source that sends the OpenTrack UDP protocol, such as OpenTrack with a webcam.
- 64-bit Windows 10 or 11. The game itself is a 32-bit process, so the mod and its loader are both x86.
The mod discovers and validates the required functions, layouts and interfaces at startup, including on an unlisted build. If those checks fail, it leaves the camera unchanged and logs the reason. This does not guarantee compatibility with every future game update.
Download Lopari, choose Half-Life 2, and click Play with head tracking.
- Download the installer ZIP (
HalfLife2HeadTracking-v<version>-installer.zip) from the Releases page. - Extract it anywhere.
- Double-click
install.cmd. It finds Half-Life 2, places the loader and the mod in<game>\bin\, and reports what it did. - Configure OpenTrack (or your tracker app) to send UDP to
127.0.0.1:4242. - Launch the game.
If the installer cannot find your copy of the game, point it at the folder yourself. Either pass the path as an argument:
install.cmd "D:\Games\steamapps\common\Half-Life 2"or set the override environment variable before running it:
$env:HALF_LIFE_2_PATH = "D:\Games\steamapps\common\Half-Life 2"
.\install.cmdBoth expect the folder that contains hl2.exe.
For placing the files by hand, or when using the Nexus ZIP
(HalfLife2HeadTracking-v<version>-nexus.zip), which contains the deploy
subtree only and no loader.
- Download Ultimate ASI Loader and take
dinput8.dllout ofUltimate-ASI-Loader.zip. That asset is the x86 build; the x64 one cannot load into Half-Life 2. - Rename it to
winmm.dlland put it in<game>\Half-Life 2\bin\. Source loads its DLLs out ofbin\, so a copy next tohl2.exeis never loaded and nothing happens. If another mod already put an Ultimate ASI Loader proxy in that folder, leave it alone and skip this step. - Put
HalfLife2HeadTracking.asiin that samebin\folder. The Nexus ZIP mirrors the game folder, so extracting it over<game>\Half-Life 2\lands it there for you. - Launch the game once. The mod writes
CameraUnlock.iniandHeadTracking.lognext tohl2.exe.
In OpenTrack, set Output to UDP over network and enter host 127.0.0.1,
port 4242. Map yaw, pitch and roll, and X, Y and Z as well if you want
positional tracking. Press Start, then launch the game.
Centering is done in your tracker. Use OpenTrack's Center bind, SteamVR's reset, or the CENTER button in your phone app.
- Connect the headset to the PC over Air Link, Virtual Desktop or a link cable.
- Start SteamVR and let it finish setting the headset up.
- In OpenTrack, set Input to the SteamVR tracker.
- Leave Output on
UDP over network, host127.0.0.1, port4242.
Set OpenTrack's Input to neuralnet tracker. It tracks your face from a
plain webcam, with no markers, clip or IR hardware. Even lighting on your face
is what it needs most.
The mod accepts one thing: OpenTrack UDP packets on port 4242. A phone app is
usable here if it sends that protocol itself, or ships a PC-side companion that
does. Check your app against that first.
For an app that does send it, what decides the wiring is how much filtering it
does before the packet leaves the phone. An app that filters on-device can point
straight at this PC's LAN address (run ipconfig to find it) on UDP port
4242. A raw or lightly filtered feed sent direct will jitter, because the
mod's smoothing is sized to take the edge off a clean signal rather than to
rescue a noisy one. That app should send into OpenTrack instead, on a spare port
such as 5252, with OpenTrack's output going to 127.0.0.1:4242 so its filters
and curves clean the feed up first.
The test is quicker than the reading: send direct, hold your head still, and if the view drifts or shakes, route it through OpenTrack.
I made Headcam so decent tracking was free for anybody with a phone already in their pocket. It filters on-device, so it can send direct. Any other app that filters enough works exactly the same way.
A phone on WiFi is a remote connection and gets RemoteSmoothing. So does a
tracker running on this same PC that sends to the machine's LAN address instead
of 127.0.0.1, because the classifier sees a transport and not a machine. Only
loopback counts as local.
Each action has a list of keys, and any key in it fires the action. By default each list holds a nav-cluster key and a chord, so use whichever your keyboard has:
| Action | Default keys | Setting |
|---|---|---|
| Toggle tracking | End, Ctrl+Shift+Y |
ToggleKey |
| Cycle tracking mode | PageUp, Ctrl+Shift+G |
CycleTrackingModeKey |
| Toggle yaw mode | PageDown, Ctrl+Shift+H |
YawModeKey |
Page Up / Ctrl+Shift+G cycles tracking mode:
- 6DOF, rotation and position together
- Rotation only, positional tracking off
- Position only, rotational tracking off
- Back to 6DOF
Page Down / Ctrl+Shift+H switches yaw between horizon-locked (yaw around the
world up axis, the default) and camera-local (yaw composed with the camera's
current pitch and roll).
The tracking mode and the yaw mode are saved to CameraUnlock.ini the moment
they change, so the next launch starts in the mode you left it in. End turns
tracking on and off for the session only and saves nothing: whether tracking is
on at launch is EnableOnStartup.
Every key in the three lists, the chords included, can be changed or removed
under [Hotkeys] in CameraUnlock.ini, for example ToggleKey=F8, Ctrl+Shift+Y.
Hotkeys only fire while the Half-Life 2 window has focus.
The mod reads its settings from CameraUnlock.ini in the game folder, and creates the file when it starts and finds none. Edit it with any text editor.
A setting set to default takes its value from Defaults.ini, which every head tracking mod that keeps its settings in CameraUnlock.ini reads. Head tracking mods that keep their settings in another file do not read it. Writing a value in place of default changes that setting for this game only. When the mod saves a setting that a hotkey changed in game, it writes the new value in place of default, so that setting no longer follows Defaults.ini in this game until you set it to default again.
Defaults.ini is %AppData%\CameraUnlock\Defaults.ini on Windows; $XDG_CONFIG_HOME/CameraUnlock/Defaults.ini on Linux, or ~/.config/CameraUnlock/Defaults.ini where XDG_CONFIG_HOME is not set, under Wine and Proton too; and ~/Library/Application Support/CameraUnlock/Defaults.ini on macOS. The mod's log, where it writes one, names the file it read.
When the mod starts and finds no Defaults.ini, it creates one holding the built-in values, unless Windows runs the game as a packaged app. The mod never changes Defaults.ini after that. Edit it with any text editor.
The built-in value of each setting set to default below:
UdpPort=4242EnableOnStartup=trueWorldSpaceYaw=trueRotationEnabled=trueLocalSmoothing=0.0RemoteSmoothing=0.15PositionEnabled=truePositionLimitX=0.3PositionLimitY=0.2PositionLimitYDown=0.2PositionLimitZ=0.4PositionLimitZBack=0.1ToggleKey=End, Ctrl+Shift+YCycleTrackingModeKey=PageUp, Ctrl+Shift+GYawModeKey=PageDown, Ctrl+Shift+HLightMultiplier=1.5
With every setting at its default, the file reads:
; Half-Life 2 head tracking settings.
; Comments start with ; and go on their own line. Text after a value is part of the value.
; Hotkeys are key names such as End, PageUp or Ctrl+Shift+Y. Separate several with commas; leave empty for none.
; A setting set to default takes its value from Defaults.ini, which every head tracking mod
; that keeps its settings in CameraUnlock.ini reads: %AppData%\CameraUnlock\Defaults.ini on
; Windows, $XDG_CONFIG_HOME/CameraUnlock/Defaults.ini (normally ~/.config/CameraUnlock) on
; Linux, under Wine and Proton too, and ~/Library/Application Support/CameraUnlock/Defaults.ini
; on macOS. The log names the file it read. Change a setting in Defaults.ini to change it in
; every game that has it set to default, or write a value here instead of default to change it
; for this game only.
[CameraUnlock]
; Written by the mod. Leave this section in place.
ConfigFormat=1
[Network]
; UDP port the mod receives tracker data on (OpenTrack protocol).
UdpPort=default
[General]
; true: head tracking is on when the game starts. ToggleKey turns it on and off.
EnableOnStartup=default
; true: yaw turns around the world's up axis. false: around the camera's own up axis.
WorldSpaceYaw=default
; true: turning your head turns the view.
; Tracking mode at startup, with PositionEnabled. The mode hotkey changes both.
RotationEnabled=default
[Smoothing]
; Smoothing when the tracker runs on this PC. 0 is the least, 1 the most.
LocalSmoothing=default
; Smoothing when the tracker is another device on the network, such as a phone.
; 0 is the least, 1 the most.
RemoteSmoothing=default
[Position]
; true: moving your head moves the view.
; Tracking mode at startup, with RotationEnabled. The mode hotkey changes both.
PositionEnabled=default
; How far, in metres, leaning left or right can move the view.
PositionLimitX=default
; How far, in metres, raising your head can move the view.
PositionLimitY=default
; How far, in metres, lowering your head can move the view.
PositionLimitYDown=default
; How far, in metres, leaning forward can move the view.
PositionLimitZ=default
; How far, in metres, leaning back can move the view.
PositionLimitZBack=default
[Hotkeys]
; Turns head tracking on and off.
ToggleKey=default
; Changes the tracking mode: rotation and position, rotation only, position only.
CycleTrackingModeKey=default
; Switches yaw between the world's up axis and the camera's own (WorldSpaceYaw).
YawModeKey=default
[Light]
; How far the light turns for each degree your head turns.
; 1 matches the view, 0 keeps the light on your aim.
LightMultiplier=default
[View]
; Field of view in degrees, as the game's fov_desired: horizontal, at 4:3, and the mod
; widens it for your screen as the game does. 0 leaves the game's own. Otherwise 30 to
; 150, which fov_desired's own 75 to 120 does not bound. A zoom still narrows the view by
; the factor it always did. Applies only while head tracking is on.
Fov=0.0
; Field of view the weapon in your hands is drawn with, in the same degrees. A wider Fov
; leaves the weapon looking oversized: lower this to shrink it. 0 leaves the game's own.
FovViewmodel=0.0
[Debug]
; true: write HeadTracking.log beside hl2.exe, new at every launch, with the launch before
; kept as HeadTracking.prev.log. It records the game build, the tracker connection and
; the view the mod draws. Attach it to a bug report.
LogToFile=trueFov accepts 30 to 150, or 0 for off, and is applied as a ratio against your
current FOV rather than written flat over it, so weapon zooms, the suit zoom and
scripted camera moves still work and scale by the same factor.
The settings are read once at startup, so restart the game after editing the file.
Read HeadTracking.log, next to hl2.exe, first. It is rewritten from empty
every launch, so it holds the session you just played and nothing older; the
launch before it is kept as HeadTracking.prev.log, which is where a crashed
session ends up once you relaunch. The log records the client fingerprint and
discovery result, whether the hooks installed, the tracker connection and which smoothing
is in force. Attach it to a bug report.
Mod not loading (no log file at all)
- Confirm
winmm.dllis in<game>\Half-Life 2\bin\, not next tohl2.exe. Source only loads DLLs out ofbin\. - Confirm you took the x86 Ultimate ASI Loader. The x64 build cannot load into a 32-bit process.
- Confirm
HalfLife2HeadTracking.asiis in that samebin\folder, spelled as shipped, and thatLogToFileunder[Debug]inCameraUnlock.inihas not been set tofalse.
Log says discovery was rejected
- A required function, layout or live interface could not be validated. Keep the specific rejection message from
HeadTracking.logand check the Releases page for an update. - The standalone Episode One (app 380), Episode Two (app 420), Lost Coast and Deathmatch campaigns are outside this mod's supported routes. Play the episodes inside app 220's
hl2_completecampaign.
No tracking response
- Check the tracker is running with its output set to UDP
127.0.0.1:4242, and that your firewall is not blocking that port. - Press
End(orCtrl+Shift+Y) to toggle tracking on, and checkEnableOnStartupinCameraUnlock.inihas not been set tofalse. - If the log says
UDP port 4242 busy, receiver will retry in background, another app is holding the port. Close it and keep playing; the mod retries twice a second.
Jittery or unstable tracking
- Raise
RemoteSmoothinginCameraUnlock.iniif the tracker is a phone or another device on the network, or route it through OpenTrack so its filters can clean the feed up. - For webcam tracking, improve the lighting on your face.
- If the view flicks between two positions, two apps are both sending to port 4242. The log names both addresses; close the one you are not using.
Leaning or turning moves the view the wrong way
- The mod applies the head pose as your tracker sends it, with no inversion of its own. Flip the axis in your tracker app.
- If yaw feels wrong only when looking steeply up or down, toggle between horizon-locked and camera-local yaw with
Page Down(orCtrl+Shift+H).
Download the new release and run install.cmd again. Your settings in CameraUnlock.ini are kept.
Run uninstall.cmd. This removes the mod DLLs and its log files, and leaves
CameraUnlock.ini in place, so a reinstall keeps your settings. The mod loader
(Ultimate ASI Loader) is only removed if the installer put it there. Use uninstall.cmd /force to remove it anyway.
Requires pixi, CMake 3.20 or newer, and Visual Studio with the x86 C++ toolset. Half-Life 2 is a 32-bit process, so the build targets Win32.
git clone --recursive https://github.com/itsloopyo/half-life-2-headtracking.git
cd half-life-2-headtracking
pixi run build-release
pixi run test
pixi run packagepixi run package produces the installer and Nexus ZIPs in release\. No copy
of the game is needed to build.
- Discord: Loop's Head Tracking Hangout - setup help, bug reports, and new-release announcements
- Lopari - free Windows launcher with one-click install and launch for the released head-tracking mods
- Headcam - free app that turns your iPhone or Android phone into the head tracker
MIT License - see LICENSE for details.
The mod statically links and redistributes third-party components under their
own licenses. Each is listed with its version and full upstream notice in
THIRD-PARTY-NOTICES.md, and both release ZIPs carry
LICENSE and THIRD-PARTY-NOTICES.md alongside the binary. The demo clip at
the top of this page contains Half-Life 2 footage, which remains the property of
Valve and is not covered by the MIT license above.
- Valve, developer and publisher of Half-Life 2
- Ultimate ASI Loader - ASI loader (MIT)
- OpenTrack - head tracking protocol and software (ISC)
- MinHook - function hooking library (BSD-2-Clause)
- cameraunlock-core - shared tracking pipeline (MIT)
This mod is not affiliated with, endorsed by, or supported by Valve. It requires a legitimately purchased copy of the game. Use at your own risk.