Note · January 08, 2026
Inside My PhiGolf Journey
From GPS to playable holes, and from frames to swing feedback at PhiNetworks.
From GPS to playable holes, and from camera frames to swing feedback: my PhiGolf work at PhiNetworks.
My scope at PhiGolf split into two tracks:
- Map generation: course data to terrain to hole-by-hole gameplay.
- Swing analysis: camera capture to pose and swing signals to feedback UI.
Course Generation Pipeline (Unity): Data In, World Out
A golf course is not a static scene. It is a generated environment built from multiple inputs:
- GPS and vector shapes (fairway, green, hazards, boundaries).
- Elevation (when available).
- Rules and object placement (tees, pin, cup, hole objects, trees).
- Coordinate transforms (lat/lng to Unity space).
The core work was making this pipeline traceable and debuggable, so if something is off, you can trace it.
Hole-by-hole loading (instead of one giant world).
We load courses in a structured flow that supports hole-by-hole generation. That matters because:
- Memory and runtime are easier to control.
- Object spawning and terrain painting stay scoped and testable.
Terrain generation and layer painting.
Once vectors and elevation land correctly, the job is turning them into Unity Terrain outputs:
- Heightmaps for terrain elevation.
- Alphamaps for texture layers (fairway, green, bunker, water, rough).
- Physics materials per ground type so gameplay behavior matches the surface.
This is where data becomes playable.
Random Elevation Fallback (When Real Elevation Is Missing)
This is a real production problem: some courses simply do not have reliable elevation coverage. If you require elevation and it is missing, the course is broken. So we implemented a fallback elevation so gameplay remains consistent.
The key requirement: identical results for everyone.
If random elevation is truly random, two players get different terrain for the same course. That is unacceptable.
So the random elevation must be deterministic: same course, same generated elevation on every device.
How we made it deterministic.
We generate a seed from a stable key, not from time or device randomness:
- Inputs: courseId, a fixed version, and a fixed salt.
- Output: a stable 32-bit integer seed via a deterministic hash (FNV-1a style).
Conceptually:
seed = StableHash32(courseId + "|" + elevVersion + "|" + elevSalt)
Why this works:
- courseId is constant for the course.
- version lets us intentionally change generation rules later.
- salt prevents accidental collisions and keeps the key format controlled.
- deterministic hash means identical output across platforms.
So even though the elevation is generated, it behaves like a real asset: reproducible and consistent across users.
Why it matters in testing.
This enabled testing on edge cases like SRTM coverage gaps, especially in high latitudes (common above 60 degrees north). Missing data stops being a blocker, and it becomes a controlled fallback scenario.
SDK + Live Feedback Loop
The other side of PhiGolf is the swing pipeline: capturing frames, extracting pose and swing signals, and producing feedback that feels real-time.
What this system needs to do:
- Camera capture.
- Frame processing and pose or swing inference.
- Phase detection (backswing, downswing, impact, follow-through).
- Angle extraction and scoring.
- Feedback delivery with low perceived latency.
The hard part is not getting numbers. It is making it stable, fast, and UI-friendly.
BeyondSDK integration mindset.
BeyondSDK provides higher-level swing outputs (phases, angles, scores, feedback), but the real engineering is integrating that into a product loop:
- Session handling (store last sessions, replay, detail view).
- Bridge surface (thin APIs Unity can call: list sessions, get detail, play replay).
- Latency visibility (measuring overlay latency and UX responsiveness).
- UI mapping (turn a stream of measurements into understandable UI states so "SDK works" becomes "feature works").
Model choice reality (MLKit vs MediaPipe vs SDK).
For MVP-level stability, the best system is the one that:
- Survives different lighting and backgrounds.
- Behaves consistently across devices.
- Provides clear outputs you can build UX around.
- Is debuggable when confidence drops.
The final decision is not which model is strongest. It is which stack gives the best reliability-to-effort ratio under real constraints.
What I Learned (Tech Version)
Determinism beats smart.
If a pipeline is not deterministic, debugging turns into guessing. Deterministic seed-based fallback for elevation is the perfect example: stable systems are buildable systems.
Pipelines matter more than features.
Users do not praise your coordinate transforms or JSON parsing, but they instantly feel when those are wrong.
SDK integration is architecture work.
Using an SDK is easy. Integrating it into a real product loop (sessions, replay, UI states, latency, failure modes) is the hard part.