FooGolf user guide

The guide for people who own a FooGolf device. It is written to be read by a customer, not by a developer, but the reference sections are deliberately precise enough for a technical user to debug against.

Started 2026-09-15 with the Analytics settings chapter, joined the same day by Extensions and risk. Other chapters will be added as the guide grows; the list below is the intended shape.


Finding your way around

The device has six buttons and no touch screen, so it is worth learning what each one does once. Only three of them ever change what is on screen from a menu.

ButtonOn the main menuIn a settings list or an information page
OKopens the thing you are onaccepts, or opens the row you are on
BACKmoves one cell to the leftgoes back one page
HOMEgoes to what you are playinggoes to what you are playing
▲ ▼ ▶move around the cellsmove up and down the rows

HOME always takes you back to your game

Wherever you are — a menu, a settings list, an About page, a scorecard — HOME takes you straight back to whatever you were playing, ready to carry on from where you left off. You never have to walk back out the way you came in. (A very small number of pages use every other button for something, so there HOME is simply the way out of that page.)

When you press HOME from the main menu you will see the menu slide across to the cell of the game or drill you are playing before it opens. That is deliberate: it shows you where on the menu your game lives, so you can find it again. Press HOME a second time if you would rather not wait.

Inside a game or drill, HOME does the opposite — it takes you to the menu, landing on that game’s own cell.

BACK leaves you where you were

Backing out of a settings list returns you to the exact cell you opened it from, not to the game you are playing. So you can change one setting, come back out, and carry straight on along the row you were looking at.

The main menu is a grid

Cells are arranged in rows — Games, Drills, PC sims, Browser sims, Launch monitors, Online play, Attach, Adjustments, Settings (firmware 0.8.658 and later; a seven-row grid with one Sim golf row from 0.8.642 to 0.8.657). The Browser sims and Launch monitors rows appear once a browser game or a launch monitor driver is installed. Every game and drill is an extension written in Lua: the Games and Drills rows hold the ones you have installed (FooGolf’s own games and drills are on the store, and so is anything you or anyone else writes). Every game that keeps score has a Scoring row on its own Settings (Stroke play / Match play / Skins - one choice shared by all of them, also under Settings > Game settings). Every drill that rolls a ball has a Drill feedback row on its own Settings (Real time - the ball rolls, the default - or Instant - it appears where it stops). Extensions are installed, updated and removed at Settings > Manage extensions. Online play holds FooGolf network (hosting and joining); Attach holds what the device is connected to - Screen casting, your Bluetooth Launch monitor, the Camera and your Analytics endpoint; Adjustments fits the device to your set-up - Physical green (distance calibration) and Environment / club test; Settings > Connect to WiFi joins a network, and once the password is accepted the device restarts and comes up on that network, so a change of network is a clean start (firmware 0.8.691). Each row begins with its name on the left; move onto the name with BACK. The Games row’s name is the FooGolf ball logo, and pressing OK on it opens About FooGolf.

A cell with a white outline around it is the game or drill you are playing now. A cell with a dashed outline is the one OK would act on. A small yellow pencil on a cell or row means something inside it has been changed from the factory setting; open it and the last row, Non-default settings, lists exactly what — and offers Restore defaults to put the whole list back.

You can make the cells bigger or smaller at Settings > Settings > Visual settings > Cells on view (1, 2 or 3 cells across the screen; 2 is the default).

Settings

The Settings cell on the Settings row holds Game settings, Visual settings, Audio settings, Device (the sensors’ Noise threshold - how far a sensor reading must move before the device treats it as a swing, shown as “noise” on the waveform pages), Over-the-air updates and About. Path/lie difficulty (Game settings) sets how wide the raised band under the path/lie arrow is on the shot readout: the higher it is, the harder it is to keep your path and lie inside the band. It lives on Game settings only, not on each drill’s list.

The Dev cell (tools built for FooGolf’s own bench) is hidden. A supporter may give you your device’s factory access code (every device has its own); typing it at Settings > Settings > Factory access shows Dev, and Dev > Disable factory access hides it again.

Nothing plays while the device is talking

The device’s own speaker is close enough to the sensors to disturb them at high volume, so the device does not watch for a putt while it is making any sound (speech, a chime, a key tick). A putt struck while it is talking is not captured - wait for it to finish.


Extensions and risk

Games and Drills grid rows; Settings → Manage extensions. Firmware 0.8.516 and later; called “scripts” until 0.8.548; the rows were called Lua games and Lua drills from 0.8.624 to 0.8.641.

Your device can run drills and games written in Lua by other people, and (firmware 0.8.630) drivers for Bluetooth launch monitors. You install one by typing its five-character share code, or pick it from Settings > Manage extensions > Available (the extensions FooGolf has approved for the public list, plus any default you removed). From then on a drill or game is a cell on your Games or Drills row; a driver lets you pair its monitor (see Launch monitors below). That is genuinely useful — and it means running code you did not write, so it is worth understanding what a extension can reach before you install one.

An extension is not reviewed for you

The FooGolf extension store checks that an extension compiles and that its manifest is sane. That is all. Approval for the public list (Available) is a light look by FooGolf at the author’s description — not a security audit — and a share code installs any extension at all, listed or not. Treat a share code from a stranger the way you would treat any small program from a stranger.

What an extension can do

  • Own the screen, the buttons and the sound while it is the active experience, and read every shot you hit while it is running.
  • Send your shots to your analytics endpoint — if you configured one. A extension declares what it claims you are aiming at, and the device forwards the shot. See the warning below.
  • Write to a player’s trend history for its own drill: the one number per completed round that the Trends graph shows.
  • Read your players’ names, handicaps and impact ratios, and your device-wide settings (stimp, path/lie difficulty, and so on).
  • Keep its own settings on the device.

What an extension cannot do

  • It cannot invent a measurement. Speeds, angles, path and lie all come from the device’s own sensors. An extension chooses which shots it reports and what it says they were aimed at; it cannot make up the numbers.
  • It cannot send anything anywhere on its own. The only outbound path is the analytics endpoint you typed in, at most one message per shot. An extension cannot open its own connection, reach the internet, or contact its author.
  • It cannot touch your player list — no adding, renaming, deleting or activating players.
  • A drill cannot rate you; a game can only if it says so. Only a game that declares itself handicap-modifying (its putts are straight at the hole, as in Course play - firmware 0.8.624) feeds your handicap, and only for the putts your own players hit. A badly-written or careless game that makes that claim can distort your handicap the same way a badly-written extension can distort your analytics — so the advice below applies to games too: install a handicap-modifying game only from an author you trust (FooGolf’s own Course play is one).
  • It cannot brick the device. An extension that crashes ends on an error page and stops; HOME always gets you out, and removing the extension removes it completely.

Launch-monitor drivers

A driver (firmware 0.8.630) is an extension that speaks one launch monitor’s Bluetooth protocol. It can only talk to the monitor you paired and report shots and a status line; it has no screen, no sound, no network and no access to your players or settings, and it runs whenever the monitor is connected (firmware 0.8.636: whenever it is paired). Unlike a drill, the shot numbers it reports are its own: a careless or malicious driver can send GSPro shots your monitor never measured. The device checks that each value is in a sane range, nothing more. Install drivers from authors you trust, and watch GSPro for the first few swings.

The realistic risks

Nothing dramatic — the sandbox is tight. What a poorly written or careless extension can do is quietly spoil your own data:

  • Unreasonable shots in your analytics. An extension declares the target itself. One that reports a target you were not actually putting at, or claims the wrong distance, puts junk rows in your own data set — and you will not notice until you go looking at it. Every forwarded row carries "game": "extension:<id>", so you can always tell an extension’s rows from a built-in game’s, and filter or drop them.
  • Unreasonable entries in your player records. An extension writes the trend number for its own drill. A buggy one can fill that drill’s history with nonsense. Controls → Players → → Delete… → Delete trend data… clears it.
  • A drill that simply scores wrongly and tells you you are better (or worse) than you are. There is no way for the device to check an extension’s arithmetic.

Updates and default extensions

Firmware 0.8.551 and later.

Every time the device starts (after its audio pack check) it asks the extension store about each extension you have installed and brings your copies up to date: a newer version is downloaded, and an extension its author has taken down is removed. The start-up page shows the progress (“Extensions - Updating 1 of 3”) and then the outcome (“2 of 2 extensions updated”, “Up to date”). Your settings and trends for an updated extension are kept. Settings > Manage extensions > Check for updates does the same at any time.

FooGolf marks a few extensions as default: every device installs them at that check. You can remove one (firmware 0.8.632); FooGolf then leaves it out until you install it again from Available. Everything else in this chapter applies to them too - they run in the same sandbox with the same limits.

Installed and Available

Firmware 0.8.632 and later. Settings > Manage extensions opens with two lists, each with its count:

  • Installed (N) - everything on the device. The light at the start of a row is bright green for an extension you installed and light green for a default. An amber asterisk after the name means the store has a newer version. OK on one offers Update to version N (when there is one) and Remove.
  • Available (N) - what you could install. Orange: an extension you have not installed. Red: a default you removed. Purple: a new default that FooGolf flagged after your device started - it installs at the next start or Check for updates, or now from here. OK shows its details; OK again installs it.

Opening Available fetches the store’s list again, so it needs WiFi to be current; without WiFi it shows the last list fetched since the device started.

Staying safe

  1. Install extensions from people you have some reason to trust.
  2. Watch the first round. Does the distance it shows match what it says, and do the scores make sense?
  3. If you forward shots to your own server, check what a new extension sends before you keep its rows — filter on game and eyeball the target.distance_ft values.
  4. Remove an extension you do not trust: Settings → Manage extensions → Installed, OK on it, Remove. Its settings and trend history go with it.
  5. Your own analytics endpoint is under your control: Settings → Analytics settings → Clear IP address stops everything, extensions included.

Analytics settings

Attach → Analytics endpoint. Firmware 0.8.525 and later; it was Settings → Analytics settings until 0.8.568, Attached software until 0.8.628.

What it does

Your device can send every shot to a server you run, on your own network, so you can keep your shot data and analyse it however you like — a spreadsheet, a database, a dashboard, a machine-learning experiment, whatever you want.

Nothing is sent until you type in an address. FooGolf does not process this data and the FooGolf cloud is not involved: the device talks straight to your server over your own home network.

Each shot is sent once, as a single JSON HTTP POST, and the device carries on immediately — forwarding never delays your putting.

Turning it on

You need a server on your home network that accepts POST /foogolf/shot and answers with a 2xx status code. There is a ready-to-run example at the end of this chapter.

  1. Make sure the device is connected to WiFi (Online → WiFi).
  2. Go to Attach → Analytics endpoint.
  3. IP address — type your server’s address on your network (for example 192.168.1.50). As soon as you commit the address the device runs a connection test and shows you the result.
  4. Port — the port your server listens on. It starts at 8080, which is the port the example server uses.
  5. Connection test — sends a single test message. You are looking for “Endpoint reached!”.
  6. Status — opens a page showing the endpoint, whether the last send worked, and how many shots have been sent, failed or dropped.
  7. Clear IP address — forgets the address and turns forwarding off again. The port is kept, so switching back on is one step.

Which shots are sent

Only shots hit at a known target, because analytics about a shot with no target would not mean anything. That is a decision each game and drill makes for itself, and it is deliberately conservative:

ExperienceSent?Target reported
Course play (stroke play / match play / skins)Yesthe hole, at the current distance to it
Close-in puttingYesthe hole, at the current distance to it
Lag puttingYesthe station, at its distance
Metrics onlyNothere is no station to aim at
Make distanceNothe aim is “as far as possible”, not a distance
Arc strength consistencyNonothing is aimed at
Face rotation consistencyNonothing is aimed at
Face angleNonothing is aimed at
BreakoutNonothing is aimed at
Swing path and lieNonothing is aimed at
Mini golfNothe ball banks off walls, so the shot is not a straight putt at the target
Extensions (Lua drills and games)If the extension says sowhatever target the extension declares — read Extensions and risk
GSPro / GSPro connector / Sim sharingNothe simulator owns the shot

In an online round a device only reports its own players’ putts. A putt the device is going to ignore because it is another device’s turn is never reported.

The message

One POST to http://<your address>:<your port>/foogolf/shot, with Content-Type: application/json. A real shot looks like this (formatted here for readability; the device sends it on one line):

{
  "schema": "foogolf.shot.v1",
  "type": "shot",
  "device_id": "3f2b91c4-...",
  "device_name": "Ferocious ferret",
  "firmware": "0.8.525",
  "time": 1757913600,
  "uptime_ms": 412345,
  "game": "lag",
  "game_name": "Lag putting",
  "player_id": 3,
  "player_name": "Andrew",
  "target": { "kind": "station", "distance_ft": 26.00, "hole": 3, "stroke": 1 },
  "shot": {
    "club_speed_mps": 1.830,
    "ball_speed_mps": 3.020,
    "impact_ratio": 1.650,
    "face_angle_deg": -0.60,
    "face_rotation_deg_per_in": 0.420,
    "attack_angle_deg": 1.10,
    "left_inner_proportion": 0.503,
    "right_inner_proportion": 0.497
  },
  "stimp": 10
}
FieldMeaning
schemaalways foogolf.shot.v1. Check it, and ignore messages you do not recognise.
typeshot for a real shot, test for the Connection test message.
device_idthe device’s permanent id. Stable for the life of the device.
device_namethe name shown on the device’s status page. May be empty on a brand-new device.
firmwarefirmware version that produced the message.
timeUNIX epoch seconds, or 0 if the device’s clock has not synced yet. Use your own server clock if this is 0.
uptime_msmilliseconds since the device booted. Always present, so it is the reliable way to order shots within a session.
gamestable identifier of the game or drill: golf_course, close, lag. An extension’s rows read extension:<its share code> (firmware 0.8.553 and later; the manifest id before that), so they are always distinguishable from a built-in game’s. (It was script:<its id> on firmware before 0.8.549.) New ones may appear; do not assume the list is closed.
game_namethe human name, for display.
player_idthe player’s id in the device’s own player list. Stable until that player is deleted.
player_namethat player’s name.
target.kindhole or station.
target.distance_ftstraight-line distance from the ball to the target, in feet, at the moment of the shot.
target.holehole or station number, 1-based. 0 means not applicable.
target.strokewhich stroke this was at that target, 1-based. 0 means not applicable.
shot.club_speed_mpsmeasured putter head speed at impact, metres per second.
shot.ball_speed_mpsclub_speed_mps × impact_ratio.
shot.impact_ratiothe ball/club speed ratio the device used for this player.
shot.face_angle_degface angle at the ball. Negative = closed/left, positive = open/right.
shot.face_rotation_deg_per_inface rotation through the impact gate. Positive = opening.
shot.attack_angle_degangle of attack.
shot.left_inner_proportion, shot.right_inner_proportionthe raw sensor proportions behind the path-and-lie readout.
stimpthe green speed the shot was judged on.

The Connection test sends the same envelope with "type": "test" and none of the game / player / target / shot fields.

Rules and restrictions

These are enforced in the device, both when you save the setting and again immediately before every single send. If a message is not arriving, work through this list first — it is almost always one of these.

Address

  • Only a literal IPv4 address and a port. Hostnames are not accepted — there is nowhere to type one, and DNS is deliberately not used.
  • Only private (RFC1918) addresses:
    • 10.0.0.0/8 — 10.x.x.x
    • 172.16.0.0/12 — 172.16.x.x to 172.31.x.x
    • 192.168.0.0/16 — 192.168.x.x
  • Port must be 1–65535.
  • Everything else is refused with an error on screen. The device never quietly clamps a value or substitutes a different one: if the address is refused, the old setting is still in force. Explicitly refused:
    • 0.0.0.0/8 (unspecified)
    • 127.0.0.0/8 (loopback — that is the device, not your PC)
    • 169.254.0.0/16 (link-local; means DHCP failed)
    • 224.0.0.0/4 (multicast)
    • 255.255.255.255 (broadcast)
    • any public address
  • If a previously-saved address stops being allowed (after a firmware update that tightens the rules, say), it is not used and forwarding stays off.

HTTP

  • Plain HTTP only. Never HTTPS. Do not put a TLS-only reverse proxy in front of the endpoint.
  • Redirects are never followed. Any 3xx response counts as a failed send. Serve the endpoint directly at the path — no 301 from / to /foogolf/shot, no trailing-slash redirect, no HTTP→HTTPS redirect. This is the single most common cause of “the test says it failed but my server log shows the request arrived”.
  • The response body is ignored entirely. Only the status code is read, and it must be 2xx. You cannot send anything back to the device.
  • Connect timeout ~2 s, total request timeout ~2 s. A server that takes longer than that to answer will look unreachable. Answer 200 first and do your work afterwards.
  • No keepalive: each shot opens and closes its own connection.

Rate and failures

  • Exactly one POST per shot. There is no polling, no heartbeat, no reconnect loop. If your server sees repeated traffic, it is not this feature.
  • A send that fails is retried at most twice, after 1 s and then 3 s, and is then dropped. There is no long-term store-and-forward: a shot missed while your server was down is gone.
  • The queue holds 4 shots. If it fills up (server slow or down while you keep putting), the oldest queued shot is dropped.
  • Failures are silent on the device except for the status: Settings → Analytics endpoint → Status shows Last send failed, with the HTTP code when there was one, and the sent / failed / dropped counts. Settings → Controls → Device status carries the same line.
  • Sending runs on its own background task. It can never delay shot detection, the screen, or the sound.

Troubleshooting

What you seeAlmost always means
The address is refused when you commit itIt is outside 10.x / 172.16-31.x / 192.168.x. The error page names the rule.
Cannot connect to <address>:<port>Nothing is listening there. Wrong IP, wrong port, server not started, server bound to 127.0.0.1 instead of 0.0.0.0, or a firewall on the server machine.
Connection test fails but your server log shows the requestYour server answered a 3xx or a non-2xx code. Redirects are never followed. Answer 200 on POST /foogolf/shot.
... answered HTTP 404The path is wrong. It must be exactly /foogolf/shot.
... answered HTTP 405Your route only accepts GET. Accept POST.
... answered HTTP 500Your handler threw. Answer 200 before you parse, then parse.
Test passes, but no shots arriveYou are in an experience that does not report (see the table above). Try Lag putting or Course play.
Some shots arrive, some do notYour server is slower than the ~2 s timeout, or it was briefly unreachable and those shots were dropped after their two retries. Check the dropped count on the Status page.
Nothing arrives after the device’s IP changedNothing to do with the device: give your server a fixed address (a DHCP reservation on your router). If your server’s IP moves, the setting stops matching.
Everything worked yesterday and not todaySame cause as above nine times out of ten. Check the address on the Status page against your server’s current address.
Status says OffNo address is set (or a saved one is no longer allowed). Re-enter it.

An example server

Dependency-free — Python 3 only. It answers 200 immediately and appends each shot to a CSV, which is enough to start analysing in a spreadsheet.

#!/usr/bin/env python3
"""Minimal FooGolf analytics endpoint. Run on your LAN: python3 foogolf_sink.py"""
import csv, json, os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

PORT = 8080
CSV_PATH = "foogolf_shots.csv"
FIELDS = ["time", "uptime_ms", "device_name", "game", "player_name",
          "target_kind", "distance_ft", "hole", "stroke",
          "club_speed_mps", "ball_speed_mps", "face_angle_deg",
          "face_rotation_deg_per_in", "attack_angle_deg", "stimp"]


class Handler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"          # keeps the 2 s budget comfortable

    def do_POST(self):
        if self.path != "/foogolf/shot":
            self.send_error(404)
            return
        body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
        # Answer FIRST: the device gives up after about two seconds.
        self.send_response(200)
        self.send_header("Content-Length", "0")
        self.end_headers()
        try:
            msg = json.loads(body)
        except ValueError:
            return
        if msg.get("schema") != "foogolf.shot.v1":
            return
        if msg.get("type") != "shot":
            print("test message from", msg.get("device_name"))
            return
        t, s = msg.get("target", {}), msg.get("shot", {})
        row = {
            "time": msg.get("time"), "uptime_ms": msg.get("uptime_ms"),
            "device_name": msg.get("device_name"), "game": msg.get("game"),
            "player_name": msg.get("player_name"),
            "target_kind": t.get("kind"), "distance_ft": t.get("distance_ft"),
            "hole": t.get("hole"), "stroke": t.get("stroke"),
            "club_speed_mps": s.get("club_speed_mps"),
            "ball_speed_mps": s.get("ball_speed_mps"),
            "face_angle_deg": s.get("face_angle_deg"),
            "face_rotation_deg_per_in": s.get("face_rotation_deg_per_in"),
            "attack_angle_deg": s.get("attack_angle_deg"),
            "stimp": msg.get("stimp"),
        }
        new = not os.path.exists(CSV_PATH)
        with open(CSV_PATH, "a", newline="") as f:
            w = csv.DictWriter(f, FIELDS)
            if new:
                w.writeheader()
            w.writerow(row)
        print(row)

    def log_message(self, *a):
        pass                                # keep stdout to the shots


if __name__ == "__main__":
    # 0.0.0.0, not 127.0.0.1 - the device is a different machine.
    ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()

Run it, note the machine’s LAN address (ipconfig on Windows, ip addr on Linux, ifconfig on a Mac), and enter that address and 8080 on the device.

Notes and limits

  • Extensions (Lua drills and games) can forward shots since firmware 0.8.530, but only if the extension declares a target. The extension decides which shots and what they were aimed at, so read Extensions and risk before you keep a new extension’s rows.

  • Shots with no target are never forwarded, by design. If you want raw per-shot measurements with no target at all, that is a different feature and it does not exist yet.

  • There is no store-and-forward. If your server is down, those shots are lost. Run the server, then putt.


Distance calibration

Adjustments → Physical green (Settings → Device settings → Distance calibration until 0.8.628). Firmware 0.8.552 and later; the page below is firmware 0.8.564 and later.

What it does

Every game and drill that rolls a ball predicts how far your putt travels from three things: the club head speed the device measured, your impact ratio (ball speed divided by club head speed, a property of you and your putter) and the stimp you set. The device measures club speed, not ball speed, so your impact ratio has to come from a putt whose roll you measured. Distance calibration does that: hit one putt, measure it, and the device proposes the ratio that would have made its prediction match.

The prediction is the device’s number: it is what the drills score, what the games roll and what a browser sim is told. A PC simulator such as GSPro or Muni Golf receives the ball speed and rolls the ball by its own physics. A real green or a simulator that rolls longer or shorter than the device predicts is a different stimp - set that first, under Settings → Game settings, then calibrate each player’s ratio. (Firmware before 2026-10-26 also carried a global “X factor” multiplier on every prediction; it is gone.)

Impact ratio
What it isthe ball/club speed ratio of one player’s putter
Who it affectsthat player only
Range1.40 to 1.80

How to use it

  1. Hit a putt in any game or drill. Measure how far the ball actually rolled, in feet.
  2. Go to Adjustments → Physical green → Calibrate from last shot. The page is headed Calibrate from last shot and shows, for that last putt: the Player and the Impact ratio and Stimp the prediction used, the measured Club speed and the Predicted distance. These are there to be read — there is nothing to press on them.
  3. Underneath is a menu of two choices. Adjust impact ratio is greyed out and the cursor skips it until you have entered a measurement: you can see it is coming, but it has nothing to work with yet.
  4. Enter measured distance — type the distance you measured on the number pad (digits, one decimal point, Cancel, OK). Feet, 0.1 to 300. You come straight back to the page, and the other choice lights up.
  5. Adjust impact ratio for <name> shows the ratio that player would need. OK changes only that player, BACK keeps their old ratio. Either way you land back on this page.
  6. Hit another putt to check. A new putt replaces the last shot and clears the measured distance — so the Adjust choice greys out again, even while you are looking at the page.

The device never rounds or clamps a value you enter: anything outside the range is refused with a message and nothing changes. If a measurement would need a ratio outside 1.40 to 1.80, the page tells you what it would have needed — check the measurement and the stimp first.

About is a row of its own in Adjustments → Physical green, next to the one that opens this page.

Things to know

  • The impact ratio is stored on the player record, exactly as if you had edited it under Players.
  • Analytics messages carry the ratio as shot.impact_ratio and the green as stimp (see the Analytics chapter), so a server that recomputes distances has everything it needs.
  • Calibrate on a flat run of typical length. A single putt is a single sample: if a second measurement disagrees, split the difference rather than chasing each one.

Online play

Several FooGolf devices can play together. One device is the host; the others join it as guests with the host’s code (Online play > FooGolf network > Host details shows the code, and the host’s screen flashes “Give your host code XXXX to your guests” when it starts hosting; guests use Join a host). How it behaves:

  • Free ranging or linked. A device is on its own until it starts hosting or joins a host. A guest has exactly one host; a host can have any number of guests.
  • Tethered guests follow the host. Guests join tethered: whatever the host starts (a drill, a game, a sim session) they start too, with the host’s settings. A guest that starts something itself, or changes one of those settings by hand, becomes untethered and is back under its own control. It stays a guest, and Resume tethering (or the host’s Re-tether all guests) brings it back.
  • Games. When the host starts a game (Course play, Mini golf, a game extension), every tethered guest’s active players are in it, up to four players in all. A host cannot start a game with more, and nobody can activate a fifth player into one. Once a shot has been hit the game is under way: nobody joins, re-tethers or changes their active players until it ends. A guest that untethers mid-game leaves it: stroke play’s board shrinks, match play and skins are re-scored (one player left plays Par).
  • If the host goes down (turned off, no WiFi) its guests stay linked but untethered and go back to what they were doing before; when it comes back up it re-tethers them all. If the host chooses Stop hosting, its guests are on their own again. A guest that restarts comes back linked; if the host’s game was under way meanwhile it comes back untethered and does not rejoin.
  • Sim sessions. Only the host (or a free-ranging device) can start GSPro; tethered guests play into the host’s session from their own device.

GSPro and the FooGolf GSPro connector

What it does

The GSPro cell on the PC sims row (the Sim golf row until firmware 0.8.657) sends your putts into GSPro. Since 2026-09-22 there is one GSPro experience, and the recommended way to connect it is the FooGolf GSPro connector, a small open-source program that runs on the gaming PC. With it there is nothing to type on the device but a code: no IP addresses, no Windows Firewall rules, no port forwarding, and no USB cable between the device and the PC. Any device in an online-play group can aim, and every shot from the group goes to the sim in order.

The connector holds the connection to GSPro itself. It looks for a port 8888 connector first (the webcam putting listener some launch-monitor connectors provide); if one is running it uses it and never touches GSPro Connect. Otherwise it connects to GSPro Connect on port 921 and keeps that one connection open for the whole session, which is why a device restart no longer needs “Reset GSPro Connect”.

Setting it up

  1. On the PC, download foogolf-gspro-connector.exe from github.com/abraae/foogolf-gspro-connector (Releases) and run it. Windows shows “Windows protected your PC” because the program is not code-signed yet: click More info, then Run anyway. The source is public.
  2. The connector window shows a six-character code under Connected FooGolf devices.
  3. Start GSPro (Open API licence) or a port 8888 connector’s webcam putting. The connector’s GSPro row turns green and names the port it holds.
  4. On the device: PC sims > GSPro > Play. The first time it asks for the code (type it, OK); the device remembers it, and Connector code on the same list changes it later. Play starts a sim session: the cell reads GSPro: Connected with a green light, the connector window names your device. Keep GSPro as the front window on the PC while you play; the aim keys go to whatever window is in focus.

A sim session holds the connector while it is connected, one device at a time. Starting anything else on the device ends the session and frees the connector. If the connector or the PC goes down mid-session the device stays in the session, GSPro: Disconnected, and reconnects by itself the moment the connector is back - unless another device started a sim session on it first, in which case yours ends and the device goes back to Course play (a host takes its tethered guests with it). A guest of another device cannot start a sim session; tethered guests share their host’s.

If you press Play while the connector is not running, the device says so and offers [OK] to continue (firmware 0.8.628 and later): OK starts the sim session anyway, GSPro: Disconnected, and it connects by itself once you start the connector. The GSPro page can only be left with HOME - the legend at the top says so.

Lag practice

The top row of the GSPro page’s blue list is Lag practice (firmware 0.8.628 and later; GSPro only - a Browser sim’s page has no such row since 2026-10-12, its game shows the distance itself). GSPro shows you a 46-foot putt; you want a feel for 46 feet before you hit it. Choose Lag practice and the device’s screen shows nothing but the distance of your last putt in giant figures (-- until the first one); each putt blanks the screen, then its distance appears with rings rippling out of the digits. These putts do not go to GSPro - they only drive this display, and they tell you nothing about face angle, only distance. Every device in an online group practises on its own: each sees only its own putts. The aim keys do nothing on this page; [OK] returns to the GSPro page.

To start the connector with Windows, run it once from a terminal with --startup on; --startup off removes it. Do not run it “as administrator”.

The connector window

The window has two panels. The left one lists the parts of the integration, one row each, green when connected and red when not; the right one is the log with the newest line at the bottom. Up and Down pick a row; OK (Enter) opens a small menu for it that starts with Cancel and then lists only what applies right now.

RowShowsOK menu
Connected FooGolf devicesThe device whose sim session holds the connector, by name, “(host)” while it is hosting online play, with its guests listed underneath. “none” plus the code while no session holds it - including when the holding device was turned off or stopped answering.Regenerate code mints a new code; a device holding the connector keeps its hold and picks the new code up by itself, anyone else needs the new code.
FooGolf cloudconnected, connecting or reconnecting. Stuck on reconnecting means the PC has no internet.nothing, it reconnects by itself
GSProconnected on port 921 (GSPro Connect), connected on port 8888 (a port 8888 connector), or not connected with the reason.Connect looks for GSPro again; Disconnect drops the link and stops looking. GSPro Connect will not take another client after that until you reset it in GSPro.

The log warns when both a port 8888 connector and GSPro Connect are reachable at the same time. Close one of them: GSPro Connect only reads the first program that connected. F10 quits.

Things to know

  • A port 8888 connector takes putts only. While the connector holds port 8888 the club rows and Repeat shot leave the selector (Repeat aim, Reset aim and Mulligan stay) and launch-monitor swings are not forwarded. Start GSPro Connect instead of the port 8888 connector to get them back.
  • One connector, one sim session, one GSPro. A second device that presses Play while yours holds the connector is told “Connector in use by ” and stays where it was. Two players on two devices should be a host and a guest in online play, not two sessions.
  • Status (the first row of the GSPro list) shows the session, the PC, the connector, who holds it and whether GSPro is connected on the PC. “Connector online - GSPro not connected on the PC” means the connector is running but GSPro (or the port 8888 connector) is not.
  • There is no other way to reach GSPro from the device: since 2026-09-23 the direct connection by IP address is gone.
  • The code is the only secret. Anyone with it can start a sim session on your GSPro. Treat it like a Wi-Fi password.
  • Aim taps are batched. Hammer Left twenty times and Right five times and the connector presses Left fifteen times; the batch is sent after a third of a second’s pause, or every two seconds while you keep tapping.

Browser sim

The Browser sims row: one cell per installed game (firmware 0.8.658 and later; Sim golf > Browser sim from 0.8.631 to 0.8.657).

What it does

A golf game that runs in a web browser - on a phone, a tablet, a TV or a PC - fed by your FooGolf. The device serves the game itself over your WiFi, so there is nothing to install on the computer and no internet is needed once the game is on the device. Your putts, the club selector’s shots, your guests’ shots and an attached launch monitor’s full swings all go to the game.

Games are extensions (kind “Browser sim game”): anyone can write one and publish it, and every game’s source is open, like every other extension.

Setting it up

  1. Install a game: Settings > Manage extensions > Available (or Install by code). The first game also brings down the web runtime (three.js, about 170 KB) - once.
  2. On the Browser sims row, OK on the game, then Play. The sim page shows “Open http://
    :9203 in a browser” and a QR code of it. While a game is playing its cell reads “Page open” or “No page” under its name.
  3. Open that address in a browser on the same WiFi (or scan the QR). It is the device’s own page (below): it opens on the game, the device says “Page open”, and you can play.

The device’s page

http://<address>:9203/ is one page per device (firmware 0.8.669 and later), there whenever the device is on WiFi - not only while a game runs. Since firmware 0.8.674 it has nothing of its own around the picture - the whole window is one of two things:

  • The game - the browser game the device is playing (only while one is). The game’s drop-down at the top right (its name with a chevron) lists the browser games on the device and, last, FooGolf device.
  • The device’s screen - the device’s own screen with its six buttons under it, so you can drive the device from the phone: start any game, drill or sim, change a setting. Opening it starts Screen casting on the device, exactly as the Attach row’s cell does, and the cast stays on until you stop it on the device (it comes back after a restart too). While a browser game is playing, Back to the game at the top returns to it; start a browser game on the device and the page shows it by itself. The page opens on the game whenever one is playing (it takes the game over from any other tab), and on the device’s screen otherwise.

Anyone on your WiFi who opens the page can press the device’s buttons, as anyone could already while a cast was running.

Things to know

  • A session is connected while a page is open. With no page open the device keeps the session and says “No page open - shot not sent” when you hit one. Close the tab and open it again at any time; it reconnects by itself.
  • One page at a time. The device shows the game on one page only. Open the address in a second tab, or on another phone or PC, and that one takes over; the first shows “In use in another tab” with a Use here button that takes the game back.
  • The club selector, Repeat shot, the aim keys, Reset aim and Mulligan work as they do for GSPro - the game decides what each one means. The Menu button top right of every game offers exactly the same rows as the device’s sim page, in the same order (the device sends them to the page), and a row picked there does exactly what the device’s row does. The sim page has no Lag practice row for a Browser sim (the game shows your distances itself).
  • The same readout in every game. After each putt the device’s own shot widget - the face angle, the pace in your Speed display unit, the rotation rate and the path/lie arrow at your Path/lie difficulty - appears bottom right of the game page, exactly as it appears on the device; a club or launch monitor shot shows its ball data there instead. The Menu, this readout and the full screen button beside the Menu are FooGolf’s, the same in every game (firmware 0.8.671 and later); full screen fills the device’s page with its tabs still showing. M opens the Menu, F toggles full screen.
  • Switching browser games. The game’s name sits top right of the page with a drop-down arrow (firmware 0.8.672 and later): open it and pick any other browser game installed on the device, and the device starts it and the page follows. The device page’s Apps tab still reaches every other kind of app.
  • The arrow keys aim. With the game page focused, Left and Right move the aim one step, as the device’s bottom-left and bottom-right buttons do.
  • Putts carry your calibration. Each putt goes to the game as ball data (ball speed = club speed x your impact ratio) together with the device’s own predicted roll at the green speed the game asks for, so a game that uses it rolls the ball exactly as far as the device’s own drills would.
  • Guests follow a host’s Browser sim like GSPro: their putts, club shots and aim reach the host’s game. A guest cannot start one.
  • Guest or hotel WiFi often stops devices on it talking to each other; if the page never connects, try your home network.
  • Writing a game: on foogolf.com, Apps > My apps > Create an app, then edit it in the app page’s Source editor (Save as new version) and press Play it - the FooGolf emulator (foogolf.com/emulator/) runs the same firmware in Chrome and always plays the version you just saved. The author’s page is foogolf.com/lua/web-games (golfclaude/docs/lua/web-games.md); the design is golfclaude/docs/browser-sim-design.md.

Launch monitors (Bluetooth)

The Launch monitors row > the cell named after your monitor’s driver, “Garmin R10” (firmware 0.8.658; the cells ended the Sim golf row from 0.8.647 to 0.8.657, one generic “Launch monitor” cell from 0.8.630 to 0.8.646, on the Attach row until 0.8.637).

What it does

Pair a Bluetooth launch monitor once and its full swings go into your sim - GSPro, or the Browser sim - in the same stream as your putts, so the sim sees one launch monitor. Each kind of monitor needs a driver - an extension anyone can write and publish. The Garmin Approach R10’s driver is on the public list.

Setting it up

  1. Install the driver: Settings > Manage extensions > Available, pick the driver (its kind reads “launch monitor”), OK to install. It appears as a cell named after it on the Launch monitors row, never on the Games or Drills rows. With no driver installed there is no Launch monitors row at all.
  2. Put the monitor in pairing mode (the R10: switch it off, then hold its button until the LED is solid blue - flashing blue means it is looking for its phone and will refuse).
  3. Launch monitors > Garmin R10 > Pair a launch monitor: the page lists the monitors in range that this driver recognises. OK on yours. “Paired with …” becomes the page’s heading and the page stays while the device connects and wakes the monitor, showing each step as it happens; when the monitor reports Ready the device returns to the Garmin R10 list on its own (firmware 0.8.650). BACK leaves the page early; the link keeps working by itself. Only one monitor is paired at a time: pairing through another driver’s cell replaces the old pairing, and the LED and the Unpair row move to that cell.
  4. Start a sim session - PC sims > GSPro > Play, or a game on the Browser sims row. The device connects to the monitor as the session starts - Searching, Connecting, Waking, Ready - and every swing it measures goes to the sim.

Things to know

  • The monitor is connected whenever it is paired (firmware 0.8.636; it was connected only during a sim session from 0.8.630 to 0.8.635). It is kept awake, so it is ready the moment a sim session starts, at the cost of its battery; Unpair releases it. Its battery level shows on the driver’s status page. The cell shows the state under the driver’s name - “Not ready” while the monitor is asleep or aligning, “Ready” when the next swing will be reported - and the status row adds the driver’s own words. Drills and games never take a launch monitor’s shots, and nothing reaches any experience while a menu is open.
  • One monitor at a time. Pairing another replaces the first.
  • A driver error (“Driver error: …”) stops the connection until you start the next sim session; update or remove the driver if it keeps happening.
  • Unpair (same menu) forgets the monitor so its own app or a PC can have it.
  • A monitor paired before firmware 0.8.630 has to be paired again once its driver is installed.

Firmware updates

Your device updates itself. When it starts up, and whenever it joins your Wi-Fi, it asks FooGolf whether there is a newer version. If there is, it installs it, restarts once, and carries on — you are not asked, and there is nothing to do.

You can change that at Settings > Settings > Over-the-air updates:

Always updateThe default. A new version installs by itself at start-up or when the device joins Wi-Fi. The screen shows INSTALLING UPDATE with a progress bar, then the device restarts. It takes a couple of minutes.
Ask playerThe device shows you the new version and waits. Press OK to install it, BACK to leave it for now. It will offer again next time it starts.
DisabledNew versions are never installed.

Choose Ask player if you do not want a restart in the middle of a session — for example if the device is set up for a group and you would rather update it afterwards.

The device never checks on a timer. It checks when it starts up, when it joins Wi-Fi, and nowhere else, so an update can never interrupt play.

If a release will not install

Occasionally a release fails — a weak Wi-Fi signal during the download, or a version that does not start up properly and is automatically rolled back to the one you had. The device tries three times and then leaves that version alone. It stays completely usable, keeps checking, and installs the next release normally. You will never find it restarting over and over.

Settings > Device status shows what happened:

Auto update: Always update
   v0.8.556 blocked after 3 tries

If you want it to try that version again, open Settings > Settings > Over-the-air updates and pick a mode (re-picking the one you are already on is enough) — that starts the three tries over.

Other things on this page

  • Over-the-air updates counts as a customised setting when it is not on Always update: the Settings cell gets the yellow edit pencil, and Non-default settings > Restore defaults puts it back.
  • Extensions update separately, at start-up, and are covered in Updates and default extensions.

Rendered from golfclaude/docs/user-guide.md at 8ef01dd, 2026-10-26. The Markdown in the repository is the source; this page follows it on every push.