Follow these matches

New matches start ticked. Untick any you don't want to hear.

Match filter checkboxes

No matches loaded yet.

Matches

Press Start to load matches.

Diagnostics

Announcement log

No announcements yet.

To do

  • P3: wake-on-sleep behaviour now reads current state instead of flooding catch-up announcements. Test cross-platform on Wednesday during Seniors R1: close laptop mid-match, reopen 5 minutes later, confirm "One match in play. Round 1. Drago 3, Farebrother 2." reads instead of every frame change since sleep. Also test iOS Safari (visibility events) and Android Chrome (pageshow.persisted).
  • P2: verify Seniors round numbers as the tournament progresses. R1 = round 7 confirmed (Wed 6 May data). R2 / QF / SF / F currently assumed as 8 / 9 / 10 / 11 — verify when matches at those stages first appear in the API and adjust the lookup tables in formatRound, formatRoundSpeech, framesToWinTarget, nextRoundMilestone, reachedMilestone, deciderPhrase, and semifinalRoundFor.
  • P2: persistent header / SPA-style navigation. Currently clicking Info or Contact does a full page reload, which kills the announcer's audio, polling timer, and speech queue. The header (with Start/Stop, settings, current match panel) should stay put when navigating between Home / Info / Contact - only the content area below changes. Recommended approach: progressive enhancement (option C from the discussion) - intercept nav link clicks with JS, fetch the new page's content via fetch(), swap the content area in-place. Falls back to full reload if JS is off. Each page keeps its own worker route for direct URL access. Care needed for: browser back/forward (use history.pushState), per-page inline scripts running on swap (Info/Contact have their own IIFEs - either re-run or skip the script swap and just swap markup), focus management for accessibility (announce route change to screen readers).
  • P2: handle the Seniors black-ball shootout properly. Tied 3-3 / 6-6 / 9-9 currently fires "Tied at three-all. Black-ball shootout for the match." which is right for the moment — but the next poll the API will likely report a 4-3 score (or similar) and the win announcement will read as if a normal frame was won. Worth confirming what snooker.org reports post-shootout (frame 7 with single-point score? Just sets WinnerID?) and if needed announcing the shootout winner explicitly: "X wins the shootout" rather than "X took frame 7".
  • P3: persistent server-side log of announcements (KV-backed) so we can review what was said when nobody had the page open. Categorise by trigger (score change vs interval vs manual).
  • P4: consider enabling HSTS in Cloudflare SSL/TLS settings. Closes one of the Security Insights warnings. Held off because it's effectively irreversible (browsers cache the policy for the configured max-age) and the marginal protection over "Always Use HTTPS" is narrow. If enabling: start with max-age 1 month, do NOT tick preload, do NOT tick includeSubdomains until thought through.
  • P4 (April 2027): refresh the Expires date in serveSecurityTxt() before May 2027. Currently set to 2027-05-05T00:00:00Z. RFC 9116 requires the date to be in the future; an expired security.txt reads as neglected.

Tips & credits

Keep this tab open in the background. Your browser must remain open for speech to work, and on mobile the screen may need to stay on.

Data credit: All match data is provided by snooker.org, maintained by Hermund Ardalen. This tool would not exist without his generosity in providing API access.