Climbing gyms
Belay
Scan the tape at the start hold. Log the send before you are off the mat.
Route tracking, setting and competitions for a climbing gym. A QR sticker at the start hold opens the route on a phone with no account needed, the send feed on the wall display updates live, and the setting team can see which of their routes nobody is touching.
- Next.js 16
- TypeScript
- Supabase
- PostgreSQL
- Row-level security
- Realtime
- Private file storage
- Generated share cards
- Magic-link sign-in
- Log a send
- 3 taps
- Send feed
- Live
- Ladder
- Live routes only
from a QR sticker, no account
wall display and phones, same socket
resets when the wall does
What was going wrong
A gym's routes lived on a whiteboard by the desk and in the setters' heads. Members tracked their own climbing in a notes app, so the gym had no idea which lines were being used, and nobody found out a route was too hard until it had been up for six weeks. Comp day was a spreadsheet and a shouted running total.
What we built
Put the route itself on the internet, reachable from the tape. Everything else follows from that: a member can log a send without an account decision, so they do; the gym gets traffic data for free; the setters get a strip list built from numbers. The competitive layer ranks on the routes currently on the wall, which means it resets itself every reset. Comps run on the same data with a judging screen on top.
What it looks like
Every screen shown on a desktop and on a phone, because half of these are used standing up.
The gym, this week
What is new on the wall, what is busy, and where your own climbing has got to. The first screen a member sees after signing in.
/gym
Desktop capture
1440 × 900
Mobile capture
390 × 844
A route, from the sticker
The page the QR code opens. Grade, setter, the send list, and one button — reachable by somebody who has never made an account.
/r/[slug]
Desktop capture
1440 × 900
Mobile capture
390 × 844
The screen by the mats
Sends as they land, the day's numbers and what went up this week. Built to be read across a room and left running all day.
/display
Desktop capture
1440 × 900
Mobile capture
390 × 844
The ladder
Ranked on the routes currently on the wall. Points from a stripped section do not count, which is what stops it becoming a seniority list.
/gym/ladder
Desktop capture
1440 × 900
Mobile capture
390 × 844
Comp scoring
The judge's screen during a competition — attempts in, standings out, on a tablet held in one hand.
/gym/comps/[id]/judge
Desktop capture
1440 × 900
Mobile capture
390 × 844
What it does
QR at the start hold
A sticker on the tape opens that route's page. No app to install and no account to make one.
Live send feed
Sends appear on the wall display and on everyone's phone as they happen, over a socket.
Setting workload
Setters see route age, traffic and which lines nobody has touched since they went up.
Comps that a judge can run
Multiple formats, a scoring screen built for a clipboard, and standings that update between attempts.
The ladder
Ranked on the routes currently on the wall, so it resets itself every time the gym strips a section.
Streaks, bests and badges
Your own history is the opponent — first V5, ten sessions in a month, a project finally sent.
Who reaches for it, and when
A member between attempts
Scans the tape, sees the grade and who else has done it, logs the send in three taps with chalk still on their hands.
The setting team on a Tuesday
Strips the lines nobody climbed and keeps the ones with traffic, off numbers rather than off memory.
The desk on comp day
Runs scoring from a tablet, and the standings on the big screen are the same standings the judge is looking at.
What makes this one different
The ladder only counts live routes
Points from a wall that was stripped in March are not an achievement, they are a head start. Ranking on what is currently up is the product decision the whole competitive layer is built around.
Realtime that respects permissions
The send feed subscribes as the signed-in member, so the same row-level rules that guard a page guard the socket. It is easy to ship a live feed that is silent for everyone but an admin; this one was tested the other way round.
Two pages with no session at all
The QR route page and the wall display never have a signed-in user and never will. They are the only two things in the app allowed past the normal auth path, and everything a member does goes the long way round.
Grades are integers until they are rendered
V10 sorting before V9 is the classic bug in every climbing app. The scale never becomes a string until the moment it is printed on the screen.
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.
Realtime
Changes pushed to open screens over a socket, filtered by the same rules.
Private file storage
Photos and receipts behind signed URLs, namespaced so policies can check them.
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 Belay?
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.