OpenTrailPaper
Open-source bike computer firmware for the LilyGO T5S3 4.7" E-Paper PRO, with offline maps, GPX routes, FIT recording and Bluetooth sensors.
Build the team once. Score the game once. The stats and the broadcast read from the same plays.
Sports & fitnessGitHub @SawdizzleOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
Score youth games once on your phone: the live-stream scorebug (a transparent OBS browser source), team stats and public game pages come from the same taps. Next.js on Supabase.
Taken from the repository README
Loading the full README.
Read the full README ↗Score the game once, on your phone. The scorebug on your live stream, your team's season stats and a public page for the parents all come from the same taps.
Scoreboard is built for youth sports streamed with OBS (or any streaming software that takes a web page as a source). Baseball has the deepest pad, with player stats; football, soccer, basketball and volleyball are scored in full, situation and clock included. The stream page is a transparent 1920×1080 Browser Source that keeps its last picture through a dropped connection instead of going blank.
v5 is Scoreboard rebuilt around the team. v4 (tag v4.47) scored one game at a time for one account; v5 keeps everything the stream did and adds:
Everything v4 did on the stream is here too: the fourteen looks (Scoreboard's own, Classic, Prime Time, Blue Steel, Forever 20, Midnight, MLBFox, Classic Green, Pinball, Chalkboard, Comic, Split Flap, Receipt, Pixel RPG), v4's seventeen themes and five layouts with custom colors, synthesized sound in five packs, the walk-off and other stingers, Starting Soon, Final and Game paused scenes with venue weather, the announcement ticker, rotating sponsors, the on-air spots for our other apps, and OBS control from the pad.
v4's links keep working: /control opens the teams, /overlay?ch=<token> reaches the same stream, and /recap?game=<id> lands on that game's public page.
Scoreboard runs on a free Supabase project and Vercel. Setup takes about twenty minutes.
supabase/migrations/, in filename order (the SQL editor, or supabase db push with the CLI linked to your project). It creates the tables in public, their policies, and the team-logos Storage bucket. supabase/legacy/ is v4's schema and is not needed for a new project.https://<your-site>/** to the Redirect URLs. Sign-up confirmation and password reset emails return there. Supabase's built-in email sender is rate-limited; for more than a handful of staff, add your own SMTP under Auth → Emails.NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY (Project Settings → API). Locally, copy .env.example to .env.local.insert into private.settings (key, value) values ('invite_code', 'your-code')
on conflict (key) do update set value = excluded.value;
then Authentication → Hooks → Before User Created → Postgres function public.hook_before_user_created. Staff invite links always get through.A team is run by its staff and followed by everyone else:
| Who | How they join | What they get |
|---|---|---|
| League admin | Creates a league at /org/new, or takes an admin link | Acts as team admin on every team in the league, posts to every family, sees the league calendar |
| Team admin, head coach, coach | A staff invite link (five seats) | The team: roster, schedule, people, stream and scoring |
| Scorer | A scorer invite link (a volunteer seat, apart from the five) | Lineups and batting order, a game's start time, and the pad. Coaches put scorers on each game |
| Broadcaster | A broadcaster invite link (a volunteer seat) | The stream: the scorebug link, looks, sponsor and On Air during the game, with an OBS and Mevo setup guide (Stream > OBS and Mevo setup) |
| Parent/Guardian | The team's family link (Team > People), then a coach approves | Calendar, one-tap RSVP for their own players, announcements, the parents' chat, their child's card |
| Fan | Follow on the public team page | Game times in their calendar, game start and final notifications |
Players have no accounts, and there are no private messages: every conversation sits in a channel the coaches can see and moderate (Announcements, Parents, Staff). A removed message leaves every screen and stays in the database for the league to review.
Calendar. Practices, meetings and events sit beside the games. Everyone gets a private subscription link (Calendar > Add to my calendar) for Apple, Google or Outlook; it checks they are still on the team every time it is fetched.
Notifications. Set the VAPID keys and SUPABASE_SECRET_KEY (see .env.example). On iPhone, notifications work for Scoreboard on the Home Screen (iOS 16.4 and later), turned on from the card on Home or Today. For game start and final pushes, enable pg_net (Database > Extensions) and set:
insert into private.settings (key, value) values
('site_url', 'https://your-site'), ('push_hook_secret', 'the PUSH_HOOK_SECRET value')
on conflict (key) do update set value = excluded.value;
/sign-up and confirm the email. On a phone, Share → Add to Home Screen opens Scoreboard full screen.The broadcaster's full walkthrough, with OBS and Mevo, is in the app under Stream > OBS and Mevo setup. In short:
Sources → + → Browser, URL the Stream screen's link, 1920 × 1080, 30 FPS. The page is already transparent.
Uncheck Shutdown source when not visible, and check Control audio via OBS so the sounds reach the stream.
Page permissions decide how much of OBS the pad may drive; with none, everything on the stream still runs.
| Page permissions | Adds |
|---|---|
| No access | The scorebug, cards, scenes, moments and sound |
| Basic | Save replay, and auto-clip of big moments |
| Advanced | Cameras (OBS scenes) and the replay buffer |
| Full | Go live and record |
Cameras: one OBS scene per camera, each with the same Scorebug source added through Add Existing. Never a second copy: two sources mean doubled sound and two clips per replay.
Replay buffer: Settings → Output → Replay Buffer at 90 seconds. Auto-clip waits for the celebration before saving (a minute after a walk-off).
Place and size the bug on the Stream screen (nine positions and a size slider), not by stretching the source.
Baseball: BALL, STRIKE, FOUL and IN PLAY as full-height keys; MORE for hit by pitch, catcher's interference, intentional walk and balk; tap a runner for a steal, caught stealing, pick-off or advance. Ball four pushes the forced runners and offers Fix runners. In play asks what happened, who fielded it and where everyone ended up, pre-filled with the likely result. Undo takes back the last play and its stats, as many times as you like. The count bar opens Fix the situation: score, inning, outs, count, pitch count, hits and errors, bases, the batter, the time-limit clock, Demo (the game plays itself, for trying the stream), End game and Start over. On a laptop: B S F I M U, and 1 2 3 H E for hits.
Football: possession, downs and distance, touchdown, PAT and two-point try, field goal, safety, turnover, kickoff, timeouts, quarters and the quarter clock. Soccer: goals, cards, halves, stoppage time and a count-up match clock. Basketball: one, two and three, team fouls with the bonus, timeouts, periods and the clock. Volleyball: rally points with the serve, aces, sets to 25, 21 or 15.
On Air on the pad is what is on the stream right now:
npm install
cp .env.example .env.local
npm run dev # http://localhost:3000
npm test # rules, stats, and every migration on an in-process Postgres
npm run typecheck
npm run lint
npm run build
/dev/kit draws every screen from made-up data with no database: ?v=today, roster, player, season, staff, game, lineup, pad, scorepad&sport=football, stream, public-game, and ?v=overlay for the stream over a stand-in camera (&look=primetime, &card=batter, &theme=retro&style=scorebox, &sport=football, &status=final, &scene=starting (final&win=1, paused&reason=rain, mid, &ss=standard)). It returns 404 in a production build.
| Path | What it is |
|---|---|
supabase/migrations/ | The schema, named by the version the live project recorded. Tests apply every file to PGlite and check each rule as the role a real request arrives as |
app/actions.ts, lib/data.ts | Every write (as the signed-in user; Postgres policies decide) and every read |
lib/rules/ | v4's rules engines, carried over unchanged with their tests: baseball (logic.js, roster.js), football, soccer, basketball, volleyball |
lib/scoring.ts, lib/stats.ts, lib/gamechanger.ts | Who a play was about and what it added to their line; rates from counts; GameChanger imports |
lib/onair.ts, lib/broadcast.ts | What the stream says, and its settings, commands and clocks |
components/pad/ | The pads, On Air, the game clock and the OBS panel |
components/overlay/ | The Browser Source (/overlay/<token>): Stage draws the feed, Scenes the full-frame scenes and ticker, Fx the stingers, ObsBridge OBS |
components/overlay/looks/ | The looks, one component and stylesheet each on the BugProps contract. Scope every selector under the look's root class: generic names leak |
components/overlay/themes.css | v4's themes and layouts for Scoreboard's own bug |
lib/fx/ | v4's sound engine and weather, carried over unchanged |
public/bugs/, public/ads/ | v4's bug pages (reference and thumbnails, at /bugs) and the on-air spots |
apply_play writes the game's new state, a log entry with the state before it (so Undo is exact), and one stat row per player it touched. A play sent against an older version of the game is refused, so two phones can never write over each other.overlay_feed(token), which returns only a version stamp when nothing has changed, so a page left running all afternoon costs next to nothing. The pad nudges it over Realtime after each save; it also polls.set_broadcast), so two people never undo each other; a moment or an OBS action is a stamped command (send_cmd) the stream acts on once.window.obsstudio, and reports back by its token.MIT, see LICENSE. The fonts Barlow Condensed, Martian Mono and Anton are under the SIL Open Font License (public/fonts/OFL*.txt).