MCPcopy Index your code
hub / github.com/Denjino/Horizon-View

github.com/Denjino/Horizon-View @v1.0.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.0.0 ↗ · + Follow
189 symbols 380 edges 36 files 11 documented · 6% updated 45d agov1.0.0 · 2026-05-23★ 442 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Horizon View

Webcam head tracking for Forza Horizon — turn your head, the cockpit camera follows.

Horizon View watches your head with any ordinary webcam and pans the in-game camera as you look around, the way a VR headset would. It replaces the OpenTrack + AITrack + AutoHotKey stack with one calibrated app and a single hotkey. Built for ultrawide cockpit racing where small head movements should let you glance at an apex or a mirror without taking a hand off the wheel.

Created with ♥ by jmayala — questions / feedback: @madebyjmayala

v1 is Windows only (Windows 10/11, x64). The installer is unsigned, so Windows SmartScreen will warn the first time — that's expected (see Install).


Quick start

  1. Download the latest HorizonView_x.y.z_x64-setup.exe from the Releases page and run it.
  2. When SmartScreen warns, click More info → Run anyway.
  3. The app opens into a short setup wizard: grant webcam access, pick your camera, calibrate (5 dots, ~60 seconds), then set a starting sensitivity.
  4. Launch Forza Horizon, get into a car, switch to cockpit view.
  5. Press Home to turn tracking on. Look around — the camera follows your head. Press Home again to turn it off.
  6. Press F1 in-game to pop up the live-tune panel and dial it in while you drive.

That's it. The app lives in your system tray; closing the window just minimises it there.


How it works

  1. MediaPipe Face Landmarker (running locally in the app) reads your head pose from the webcam and extracts your yaw — how far you're turned left or right.
  2. A 5-point calibration learns your natural head-movement range and maps it to screen positions, so you don't have to fiddle with raw sensitivity curves.
  3. While tracking is on, the app holds the right mouse button and emits mouse movement to drive Forza's cockpit free-look, then releases it when you toggle off.

Everything runs on your machine. No telemetry, no network, no account.


Setup wizard, step by step

Step What happens
Welcome Quick intro.
Webcam access Grant the camera permission. If you deny it, enable it under Windows Settings → Privacy & Security → Camera, then retry.
Pick camera Choose your webcam and confirm you can see your face in the preview.
Calibrate Full-screen. Five dots appear (center → far-left → mid-left → mid-right → far-right). Look at each dot by turning your head — not just your eyes — then press Space (or click "Capture this point"). Wait for the "Tracker ready" indicator and a live yaw readout before you start.
Tune Set a starting sensitivity. You'll fine-tune the rest in-game.

If calibration fails ("captured points aren't strictly ordered"), you moved inconsistently between dots — just redo it, turning your head deliberately and pausing before each capture.

You can re-run calibration any time from the dashboard (Recalibrate) or the tray menu.


The controls — what every setting does

All of these live in the dashboard (the app window) and in the in-game F1 overlay. They're the same controls in both places and stay in sync.

Sensitivity 0.1× – 2.0×

Overall strength of the effect — how far the camera pans for a full head turn. Start around 0.3×. Higher = a small head turn moves the camera more.

Smoothing 0 – 100%

Trades responsiveness for stability. - Low (Responsive): the camera tracks your head almost instantly, but webcam noise can make it feel twitchy. - High (Smoothed): heavier filtering — calmer, very slightly laggier.

Internally this drives a One-Euro filter: it smooths hard when your head is still and loosens up automatically when you move, so high smoothing doesn't mean sluggish fast turns. ~80% is a good default.

Response curve Sensitive center ↔ Linear ↔ Sensitive edges

Changes how head angle maps to camera pan. - Linear (middle): 1:1 — even response everywhere. - Sensitive edges: small head movements near center barely move the camera; bigger turns ramp up sharply. Good if you want a steady forward view and only pan when you really mean to. - Sensitive center: tiny head movements register immediately; the extremes level off.

Center deadzone 0 – 30%

Snaps the camera to dead-center while your head is near neutral, so it doesn't jitter when you're looking straight ahead. Outside the deadzone, motion is smoothly re-mapped so there's no jump. If you see micro-jitter while sitting still, raise this to ~15%. This is the right fix for rest-jitter — it doesn't hurt responsiveness once you're actually turning.

Left pan / Right pan 0.2× – 3.0×

Independent strength for each side. In a cockpit you sit on one side of the car, so one mirror is "closer" (less rotation) than the other. Crank the heavier side up so a comfortable head turn reaches the far mirror. Example for a left-hand-drive car: Left ≈ 1.55×, Right ≈ 1.0×.

Recenter

Looking straight ahead but the camera isn't centered? Press Recenter while holding a neutral head pose — it makes your current pose the new "center" without re-running calibration. Useful when you've shifted in your seat during a long session. "Clear offset" undoes it.

Fine-tune jitter (dashboard → Advanced)

For power users: Min cutoff and Beta expose the One-Euro filter directly and override the Smoothing slider. Lower min cutoff = heavier smoothing at rest; beta = how fast the filter loosens during motion.


Recommended starting point

A solid baseline (the defaults a fresh install ships with):

Setting Value
Sensitivity 0.30×
Smoothing 80%
Response curve Linear
Center deadzone 15%
Left pan 1.55×
Right pan 1.00×

Then adjust to taste with the F1 overlay while driving. Use "Reset advanced to recommended" in the dashboard to get back here.


Tips & things to know

  • Use the camera preview to tune without the game. Turn on Advanced → Show camera preview window. It shows the tracked face mesh and a green ball that moves exactly as the camera would — a fast way to feel out the sliders. Drag its corner to resize it.
  • The toggle overlay needs borderless windowed mode. The on-screen "Tracking ON/OFF" toast and the F1 panel appear over the game only in Fullscreen Windowed / Borderless mode. If you don't see them, check Forza's Display settings. (Exclusive fullscreen hides all overlays, system-wide — not specific to this app.)
  • Cursor parking. When tracking turns on, the mouse cursor is moved to the bottom-center of your screen once and left there, so it stays out of the way.
  • Face lost? If the tracker loses your face (you look away, lighting drops), it gently re-centers the camera and pauses until it sees you again. Good lighting on your face makes tracking far more stable.
  • Re-tuning after big changes. If you change seating position or move your webcam, hit Recenter, or Recalibrate if it feels off.
  • Hotkeys. Toggle defaults to Home, live-tune panel to F1. The toggle key is rebindable in the dashboard.

Developer notes

  • Stack: Tauri 2 (Rust core) + React + TypeScript + Vite + Tailwind. Tracking via MediaPipe Tasks Vision (WebGL) in the webview; OS mouse output and global hotkeys in Rust.
  • Run locally: pnpm install then pnpm tauri dev.
  • Build installer: pnpm tauri buildsrc-tauri/target/release/bundle/nsis/.
  • MediaPipe assets (WASM + model) are bundled at build time by scripts/setup-mediapipe.mjs, so the app works fully offline.
  • The only real end-to-end test is running Forza; most behaviour can be felt out using the camera-preview tuning ball without launching the game.

See FUTURE.md for what's intentionally out of scope for v1.


Credits

Created with ♥ by jmayala. Questions, ideas, or bug reports → @madebyjmayala.

License

MIT.

Extension points exported contracts — how you extend this code

SliderProps (Interface)
* Shared tuning sliders. The dashboard's Advanced section and the * in-game F1 settings overlay both use these so a set
src/components/TuneSliders.tsx
Props (Interface)
(no doc)
src/routes/RebindHotkey.tsx
BackendStateSnapshot (Interface)
(no doc)
src/state/appState.ts
TrackingLoopHandle (Interface)
(no doc)
src/tracking/trackingLoop.ts
Props (Interface)
(no doc)
src/components/SensitivitySlider.tsx
AppStore (Interface)
(no doc)
src/state/appState.ts
StartOptions (Interface)
(no doc)
src/tracking/trackingLoop.ts
CameraPreviewHandle (Interface)
(no doc)
src/components/CameraPreview.tsx

Core symbols most depended-on inside this repo

updateConfig
called by 14
src/state/appState.ts
save
called by 7
src/routes/SettingsOverlay.tsx
goTo
called by 7
src/state/appState.ts
filter
called by 7
src/tracking/OneEuroFilter.ts
snapshot
called by 7
src-tauri/src/state.rs
close
called by 6
src/tracking/MediaPipeFaceLandmarker.ts
stop
called by 6
src/tracking/trackingLoop.ts
hold_rmb
called by 5
src-tauri/src/mouse.rs

Shape

Function 115
Method 39
Class 20
Interface 13
Enum 2

Languages

TypeScript63%
Rust35%
Python2%

Modules by API surface

src/tracking/trackingLoop.ts16 symbols
src/state/appState.ts16 symbols
src-tauri/src/config.rs15 symbols
src-tauri/src/commands.rs15 symbols
src-tauri/src/mouse.rs10 symbols
src-tauri/src/hotkey.rs9 symbols
src/tracking/mouseOutput.ts8 symbols
src/tracking/MediaPipeFaceLandmarker.ts8 symbols
src/routes/Onboarding.tsx8 symbols
src/routes/Calibration.tsx8 symbols
src/tracking/OneEuroFilter.ts7 symbols
src-tauri/src/state.rs7 symbols

For agents

$ claude mcp add Horizon-View \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact