Esports organisations
Frametime
Six time zones, one scrim time, and the clip that shows the mistake.
Scrim scheduling and VOD review for a competitive team. Availability is collected in each player's own clock and resolved across time zones and daylight saving, and the clip library is searched by the mistake you are looking for rather than by match date.
- Next.js 16
- TypeScript
- Supabase
- PostgreSQL
- Row-level security
- Generated share cards
- Magic-link sign-in
- Scrim times
- Per-member zone
- Coverage
- Before you book
- Ladders
- One per role
instants, formatted server-side
unknowns excluded, not counted as no
normalised, no overall board
What was going wrong
A roster spread across six time zones scheduling scrims in a Discord thread, where every proposal turned into forty messages of clock arithmetic and somebody still turned up an hour out after the clocks changed. Review was a folder of unlabelled VOD links, so the same mistake got made for a month because nobody could find the four clips that showed it.
What we built
Store instants, collect availability in local terms, and do the resolving in one tested module rather than in everyone's head. A proposed slot comes with a coverage figure before it is sent. The clip library is built on tags that are normalised on save, so the library does not fracture into "Mid Control" and "mid-control", and the filters live in the URL so a coach's view is something they can paste to the team.
What it looks like
Every screen shown on a desktop and on a phone, because half of these are used standing up.
The week ahead
Scrims, confirmations and what is still unanswered — every time already in the reader's own zone.
/team
Desktop capture
1440 × 900
Mobile capture
390 × 844
Availability across zones
Six players' local evenings laid over one another, with the overlap that a scrim can actually be booked in.
/team/availability
Desktop capture
1440 × 900
Mobile capture
390 × 844
The clip library
Found by the mistake rather than the date. Multiple tags narrow, and the filter state is in the address bar.
/team/clips
Desktop capture
1440 × 900
Mobile capture
390 × 844
Ladders by role
Each role ranked on the metric that role is judged on, normalised so the boards are worth the same.
/team/ladder
Desktop capture
1440 × 900
Mobile capture
390 × 844
What it does
Availability in your own clock
"Free Tuesday evenings" means your Tuesday evening. The app resolves it against everyone else's.
Coverage before you book
Who can actually make a proposed slot, with unknowns excluded rather than counted as a no.
Clip library by tag
Multi-tag search resolved in the database, so "mid-control" and "late-rotate" narrows instead of listing.
Per-role ladders
A support player is not ranked on kills. Each role has its own board on its own metric.
Public tryout form
A link with no account behind it, feeding straight into the manager's list.
Match cards
A generated image per scrim result, for the moment someone pastes it into Discord.
Who reaches for it, and when
A manager booking Thursday
Sees a coverage figure for the slot before sending it, rather than finding out at scrim time that two players are asleep.
A coach mid-review
Filters the library to one mistake, then pastes the address into team chat — the filters are in the URL, so everyone sees the same seven clips.
A player at a bootcamp
Reads times in their own set zone, not the laptop's, which is wrong the moment they get on a plane.
What makes this one different
No wall-clock time is ever stored
A scrim is an instant, rendered into each member's own zone. 19:00 is not a time; it is a time somewhere. Availability is the one deliberate exception, and resolving it is where daylight saving bites.
Times are formatted on the server
The browser knows the device's zone, which is not the member's when they are at a bootcamp in another country. Rendering server-side is the only way that comes out right.
There is no overall ladder, on purpose
One board per role, each normalised within the role so topping the controller board is worth what topping the duelist board is worth. An overall board would rank people on somebody else's job.
Unknown availability is not unavailability
Members who have set no windows are left out of the coverage figure rather than counted as a no, so the number stays honest for a team that has not all filled it in.
Built with
What each piece is doing here — the people who read this section are usually the ones who would have to maintain it after us.
Next.js 16
The application framework. Pages render on the server by default.
TypeScript
Strict mode everywhere. A field renamed in one place fails to compile in the others.
Supabase
Postgres, authentication, file storage and realtime in one managed service.
PostgreSQL
The database. Constraints and permissions live here, not in the interface.
Row-level security
Who can read which row is decided by Postgres. A UI bug cannot leak data.
Generated share cards
Per-record images drawn on the server for anything that gets pasted into a chat.
Magic-link sign-in
An emailed link instead of a password nobody can remember.
Want something like Frametime?
It does not have to be your industry — the underlying problem is nearly always the same. Tell us how the work moves through your business and we will tell you what it would take.
Every project is quoted on its own. Turnaround starts at two weeks.