Repository navigation
Releases: lsmith77/ultimate-broadcast
Release list
v0.9.0
The spotter arrives: a surface for recording what actually happened in a game, not just the score. Read the deployment note at the end before upgrading.
The spotter
- A new surface, at
?view=spotter&game=<id>for a live game or?view=spotter&mode=trainingfor footage. It captures every throw, who caught it, turnovers, blocks, calls, and whether the disc was live. Spoken rather than typed, because a spotter's hands are busy and their eyes are on the field. Recognition runs in the browser — nothing is sent anywhere — through a speech engine that takes the call grammar as a constraint, so it cannot return a word that is not a legal call. - It refuses to invent. A constrained recogniser always returns something legal, so legality is no evidence; the sport is. A pull cannot happen while the disc is live, a team cannot catch its own pull, and possession never changes without a stated cause. Anything impossible is recorded as a question rather than an observation, and settled at the next stoppage. Names are checked against the vocabulary before a game, so a player whose name sounds like a throw can be given one the recogniser can hear.
- Training mode uses a video's own timestamps as the clock, so two people spotting the same footage produce captures that line up exactly — which is how a capture can be checked without anybody holding the right answer. It ships with a reference game and links to the tournament's own roster and statistics.
- Play time, honestly. It tracks whether the disc is live, kept separate from "nobody was watching": a stoppage means nothing was missed, an absence means nobody knows. Every figure carries its denominator.
- Mixed games. The spotter can take the gender ratio as a responsibility: declare the first point's, and the rest follows the prescribed pattern, including after half time. With each player's MMP/FMP known, the line picker groups by matching and counts each against the point's quota — the same picker the commentary desk shows, now drawn from shared code so the two cannot drift.
- It can keep the score. A tournament spotter most likely replaces the scorekeeper rather than joining them, so the spotter shows the scorekeeping scoreboard itself — the same two tiles as the match control phone — and a spoken goal counts exactly as a pressed one. Writing the score needs the scorekeeping code; without one the board still shows the score from the capture.
Changed
- The commentary desk draws its gender-ratio picker, matching colours and quota counts from shared code rather than its own. Nothing about it should look or behave differently; it is the same control in one place instead of two.
- The recogniser is now deployed rather than fetched again on each server. It travels with its licence and notices, since serving it makes an installation a redistributor of somebody else's Apache-2.0 work.
Fixed
- Deploying was broken entirely. A comment written between two continued lines of
deploy.shsilently truncated its own command, dropping the source and destination, so every deploy failed with an rsync usage error. A check now refuses that shape. - The speech model was re-downloaded on every load — 40MB each time, before a spotter could say a word — because the host sets no cache policy for that file type. It is now cached for a year and its address carries a version, so a new model still reaches everybody.
- Deploying from a tag took the site down.
rsync --archivecopied the temporary build directory's permissions, which were0700, making the document root unreadable and 404ing every URL with every file present.
Not there yet
- Nothing consumes a capture: it is a file the spotter downloads, and no other surface reads it. The commentary desk and the overlays cannot show anything from it until that changes.
- A coach acting as spotter should see the score without being able to write it. The scoreboard is already split that way, but nothing yet distinguishes a coach from a tournament spotter.
- The spotter is unproven. It has been used by its author against footage, not by strangers against a real game.
v0.8.0
Behaviour on air changed, so read the first entry before upgrading during an event.
- Overlays no longer put error text on air. A board that cannot load its game shows nothing at all, a working board is never replaced by a message, and a board that nothing has confirmed for about two minutes hides itself rather than keep showing a score the game may have moved past. Diagnostics are turned on by an operator — from the Studio, which needs no URL editing on a switcher and expires after ten minutes, or with
?debug=1on a laptop — and a board that is working stays silent even then. The Studio's new Feed and diagnostics panel reports whether game data is answering.docs/STUDIO.md§11a. - Squads now expire the way prepared notes always have. Standalone, this project holds a team's names because nothing upstream does — and they sat on disk indefinitely while notes about the same people went after a week. Both are now forgotten seven days after their last write, configurable with
retention_days(or--retention-daysat install): raise it for a club tracking one team all season,0to keep until deleted by hand. Hosted is unchanged, because there the squad is UltiOrganizer's.docs/DEPLOY.md§5. - A demonstration installation now demonstrates itself. Every store here takes a five-character code a crew agrees on at an event, and a visitor has no crew — so the commentary desk opened in an empty room with no FMP/MMP data, leaving a mixed game with no bands, no quota counts and no grouped picker, and match control said "ask the operator to set a code" to somebody with no operator. On a demonstration there is now one published code,
TRYME, filled in by the surfaces that want it: the desk opens in a prepared room with matchings for both mixed squads, it is linked without an operator, and match control can actually be pressed. Gated on demo mode; a real installation is unchanged.docs/DEPLOY.md§5. - The welcome message offers keep score on a phone alongside the game, mixed game and desk links — on a demonstration, where the code is published; elsewhere it is left out rather than pointing at a prompt a visitor cannot answer. A prepared room also survives an administrator editing it: the shipped squad is the floor, so clearing one note no longer takes that player's matching with it.
- Stated as a principle what the code already did: nobody is required. The crew scale runs from nobody at the field — a camera and post-production — to as many as an event can staff, with no configuration in that range privileged over the others. Now a rule in
AGENTS.md, a row at each end of the crew table, and a sentence on both front doors. deploy.sh --version v0.7.0anddeploy.sh --latestdeploy a release rather than the working directory, from a temporary git worktree, so this directory is untouched anddirtystays honest.--showprints what would be sent without sending it.docs/DEPLOY.md§4.
v0.7.0
The first tagged release. It covers everything since the project began on 2026-08-22, so this entry describes what is in it rather than what changed.
The four surfaces
- Scoreboard — a broadcast bug on a transparent 1920×1080 canvas: score, clock, timeouts, hold/break, a turnover count, and a statistic strip that says something true about the game or nothing at all. Point a browser source at one URL. It can follow a field rather than a game, so a fixed source keeps working when the schedule moves.
- Studio — a full-frame stage hosting several cards at once, and the page that decides what is on it: card placement per slot, arming before showing, automatic half-time, full-time and post-goal cards, and an export of the whole configuration.
?auto=1runs a stage with no operator at all. - Commentator — a second screen, never on air, so it can hold numbers a graphic must refuse: rosters, line sharing between desks, prepared notes, pronunciation and pronouns as declared fields, timeouts left, and a per-point view of who is on the field.
- Match control — the score and the clock from a phone at the pitch, applied on screen first and sent afterwards, plus possession, timeouts, an injury stoppage and the opening ratio behind a More panel.
Two deployment modes
Hosted on Live! by BULA, and standalone with nothing underneath — no database, no Composer, no build step. The same renderer and the same payload shape serve both, with one provider seam between them, and a fact one mode cannot know is omitted rather than sent as zero.
Standalone brings its own event authoring in a browser, squads through the commentary desk, an administrator login, and a recorded capture so every page renders with no host at all. ultimate-broadcast.org is one, on ordinary shared hosting.
Keeping score with no signal
A whole game, or a weekend of them, on a phone with no network. /k/<game> installs to the home screen, the queue and the last server answer both survive being closed, and /k/ lists every game the device is carrying with what each still owes. A goal is written as the point it completes, never as +1, which is what makes a retry, a replay after a reload or a delivery a day late safe. What cannot be delivered exports as a file. docs/OFFLINE.md.
Post-production
?at=<seconds>&goals=<n> draws one deterministic frame, through the same renderer a live broadcast uses rather than a second one, for adding a scoreboard to footage recorded without a switcher. docs/POSTPRODUCTION.md.
Running an installation
deploy.sh for a standalone site, install/ for the host rules and the first configuration, version.json so a site can be asked what it is running, a brand with a per-surface tab icon, and visitor counting that reads the web server's access log and adds nothing to the software. Relicensed to AGPL-3.0-only with a section 7 permission scoped to combination with Live! and UltiOrganizer.
Known limits, carried into this release
lines.phphas no lock around its read-modify-write, so two commentators saving different teams in the same moment can still lose one.AGENTS.mdrecords it as a gap rather than a decision.- Diagnostics are painted onto the broadcast canvas, which is useful during setup and unacceptable on air, and the page cannot tell the two apart.
docs/STUDIO.md§11 has the three options. - Conflicts between two people scoring one game are detected and reported, never merged.
docs/MATCHCONTROL.md§0b is case by case. - A score kept in match control has no route back. Hosted, the integration is the point: brackets, standings, rosters, histories and the score all come from UltiOrganizer, and a game its own Scorekeeper records is on the tournament record already. What cannot go back is a score this project kept itself — the API is six endpoints and all GET — so match control's log sits beside the record rather than in it.
docs/UPSTREAM.mdis the ask.