Inspiration

Movie night, one remote, two households' worth of opinions. Cheeto377 has a film on the TV and the game on a phone (post). JojoKiffinNFL's partner holds the remote (post). WAVYLIFEWEBB's family gave up on a Finals game and missed a 29 point comeback (post). The tools they have today are phone alerts that ding all afternoon (MaryWittTN) and arrive before the stream shows the play (factoryjones). They do not want more alerts. They want the one that matters, on the screen the household is already looking at.

What it does

Close One watches the games a household follows and puts one short toast over whatever is playing on the TV when a game turns into a one possession finish. The toast shows the Close One mark, the game, the quarter and the clock, and never the score. Press Menu to open the cue card, press Watch to go to the game, or press Not now.

The TV learns the household's interruption budget. Watching a cue lowers the bar for that team and time slot. Not now raises it. Three Not nows inside an hour silence the night. Home reads the lesson back in plain words, for example "Hawks: bar 27, down from 30 after 1 watched."

Watch opens the game with the time left ("Joining with 2:27 left in the 4th quarter") and the pressure on every possession so far, drawn against the household's bar.

In the 632 games from Jan 15 on, Close One cues 0.48 times per game against 0.71 for the usual "within 5 points in the last 5 minutes" rule, 32% fewer interruptions, and stays silent in 431 of them. At the same cue budget with two minutes left to switch, it reaches 97 of the 104 games decided late where the best simple rule, chosen on the earlier games, reaches 93. The bar was chosen on the 603 games before Jan 15 and came out at the 0.30 the app ships with.

The household controls are one press each: an off switch, Pause tonight until the 5 am rollover, quiet hours across midnight, and a team list. Nothing is sent until the TV opts in on the third setup step.

How we built it

  • Vega app (React Native for Vega 0.83, SDK 0.24): team picker on a D-pad grid, game list with a pressure meter, cue card with Watch, Not now and Why this cue?, the learning bar, setup, settings, post-game summary. Every control is reachable by D-pad, with one first-focus target per screen.
  • Headless service com.clutch.tv.cue.service: started by the app through its pkg:// URI, holds the authenticated WebSocket, and posts a MenuToastRenderTemplate toast over the app on screen.
  • Cue server (Node): ESPN play-by-play ingest, our own win probability model, leverage per possession, per household thresholds and nightly cap. Device tokens are hashed at rest; a household sees only its own teams, cues and learning.
  • Proof around it: 62 app tests, 39 app mutations and 56 server mutations, each of which must turn the suite red; npm run eval recomputes every season figure from the cached games and npm run test:eval asserts them.
  • Open source recipe: the toast over another app, the service start and the manifest block are packaged as vega-service-toast (section 4).

Measured on the Vega Virtual Device with vega-video-sample playing in front: 20 of 20 toasts reached the screen, median 50 ms and p95 75 ms from the send. The same 20 sends with a channel name that is not a render template drew 0.

Challenges

  • A toast from a headless service has no documented route. The only documented API is a React hook, and the channel name turns out to be the render template selector. We worked it out from the type definitions and the device log, then packaged it (FL-008, FL-009).
  • Making the interruption rare is harder than making it work. Fed shuffled score margins, the same model cues 1.89 times per game and 282 times in blowouts; fed the real games, it cues 0.56 times and never in a blowout, so every cue is earned by the game state.

Accomplishments

  • A system toast over another app's playback, raised by a service the app starts itself, on Vega.
  • A cue policy that interrupts 32% less than the obvious rule on the games from Jan 15 on, reaches more late-decided games than the best simple rule at the same budget when the household has two minutes to switch (97 against 93 of 104), and whose learning sends 25% fewer dismissed cues on identical games (100 against 133) while keeping every cue a household wanted.
  • A household model with its own off switch, quiet hours and nightly cap, tested over real HTTP and WebSocket connections.
  • The whole mechanism extracted into a reusable MIT package for other Vega developers.

What we learned

On Vega, the system renderer is another process. It draws what it can fetch, and anything else is accepted by post() and silently blank. The honest metric for an interruption product is how rarely it fires, so we measure cues per game first and recall second.

What's next

Pressure meters for the NFL and Premier League, which is where 11 of the 13 asks we collected came from. A licensed play-by-play feed.

Built With

  • espn-play-by-play
  • headless-task-manager-(vega)
  • jest
  • node.js
  • node:test
  • notificationmanager-(vega)
  • react-native-for-vega
  • typescript
  • vega-sdk-0.24
  • vega-virtual-device
  • watchman
  • websocket
Share this project:

Updates

Submission history