DEV Community

Cover image for Bringing Local Slalom Races Online with Canoe Slalom Live
Karlis
Karlis

Posted on

Bringing Local Slalom Races Online with Canoe Slalom Live

DEV Weekend Challenge: Community

This is a submission for the DEV Weekend Challenge: Community.

The Community

Canoe and kayak slalom has a strong grassroots scene: small clubs, volunteer judges, parents on the riverbank, and athletes who travel long distances for a single race run. Yet many local events still run on paper start lists, handwritten penalties, and spreadsheets that never leave the timing laptop.

I built Canoe Slalom Live for that community: local organizers who want something lightweight they can run on a single laptop at the course, judges who need a simple way to record runs, commentators who want quick access to athlete stories, and spectators who just want to understand what is happening on the water in real time.

What I Built

Canoe Slalom Live is a tiny live-timing and results web app for canoe/water slalom competitions. It runs as a Go web server with an embedded SQLite database and server-rendered HTML, so an organizer can start it on a laptop at the riverside and let everyone else follow the event from their phones.

The app provides a public event page with a start list grouped by category, a live leaderboard with penalties, a photo gallery, dedicated commentator and athlete profile views, plus a mobile-friendly judge panel for entering runs and penalties. It focuses on making results understandable for spectators while giving judges and commentators tools that fit how small events actually work.

It’s a single Go + SQLite binary that can run on a small VPS or services like Google Cloud Run.

Demo

GitHub repository (code + README + screenshots):

GitHub logo AcaciaMan / canoe-slalom-live

Weekend-built sports app for canoe/water slalom meets. Supports creating events, managing start lists and athlete bios, recording runs and penalties, and showing a live leaderboard for the whole community.

🛶 Canoe Slalom Live

Live timing and results for canoe/water slalom competitions. Built for grassroots events — run it on a laptop at the riverside, let spectators follow on their phones.

Short introduction video https://github.com/user-attachments/assets/90fe481d-032e-493f-acd4-5c0e1be7cca1

Quick Start

# Prerequisites: Go 1.22+, GCC (for SQLite CGo driver)
go mod tidy
go run main.go -seed

# Opens at http://localhost:8080
# Judge panel at http://localhost:8080/judge/events/demo-slalom-2026
Enter fullscreen mode Exit fullscreen mode

To enable authentication for judge routes:

# Windows
set ADMIN_TOKEN=secret123
go run main.go -seed

# Linux/macOS
ADMIN_TOKEN=secret123 go run main.go -seed

# Judge panel: http://localhost:8080/judge/events/demo-slalom-2026?token=secret123
Enter fullscreen mode Exit fullscreen mode

Stack

  • Gonet/http with Go 1.22+ pattern routing, html/template for server-rendered HTML
  • SQLite — embedded via github.com/mattn/go-sqlite3 (CGo), WAL mode, single data.db file
  • Vanilla HTML/CSS/JS — no build step, no framework, no bundler

Pages

Route Page Description
/events/{slug} Event Start list grouped by category with athlete bios
/events/{slug}/leaderboard Leaderboard Live rankings with penalty sparklines and auto-refresh
/events/{slug}/photos Photo Gallery Grid of event

Short intro video (live walk-through of the seeded demo event):
https://github.com/user-attachments/assets/90fe481d-032e-493f-acd4-5c0e1be7cca1

To try it locally:

bash
go mod tidy
go run main.go -seed
# Opens at http://localhost:8080
# Judge panel at http://localhost:8080/judge/events/demo-slalom-2026
Enter fullscreen mode Exit fullscreen mode

Code

The full source code is available here:

GitHub logo AcaciaMan / canoe-slalom-live

Weekend-built sports app for canoe/water slalom meets. Supports creating events, managing start lists and athlete bios, recording runs and penalties, and showing a live leaderboard for the whole community.

🛶 Canoe Slalom Live

Live timing and results for canoe/water slalom competitions. Built for grassroots events — run it on a laptop at the riverside, let spectators follow on their phones.

Short introduction video https://github.com/user-attachments/assets/90fe481d-032e-493f-acd4-5c0e1be7cca1

Quick Start

# Prerequisites: Go 1.22+, GCC (for SQLite CGo driver)
go mod tidy
go run main.go -seed

# Opens at http://localhost:8080
# Judge panel at http://localhost:8080/judge/events/demo-slalom-2026
Enter fullscreen mode Exit fullscreen mode

To enable authentication for judge routes:

# Windows
set ADMIN_TOKEN=secret123
go run main.go -seed

# Linux/macOS
ADMIN_TOKEN=secret123 go run main.go -seed

# Judge panel: http://localhost:8080/judge/events/demo-slalom-2026?token=secret123
Enter fullscreen mode Exit fullscreen mode

Stack

  • Gonet/http with Go 1.22+ pattern routing, html/template for server-rendered HTML
  • SQLite — embedded via github.com/mattn/go-sqlite3 (CGo), WAL mode, single data.db file
  • Vanilla HTML/CSS/JS — no build step, no framework, no bundler

Pages


























Route Page Description
/events/{slug} Event Start list grouped by category with athlete bios
/events/{slug}/leaderboard Leaderboard Live rankings with penalty sparklines and auto-refresh
/events/{slug}/photos Photo Gallery Grid of event







Key routes:

  • /events/{slug} – event overview with start list and athlete bios
  • /events/{slug}/leaderboard – live leaderboard with auto-refresh and penalty sparklines
  • /events/{slug}/photos – photo gallery with photographer credits
  • /events/{slug}/commentator – big-screen commentator view
  • /events/{slug}/athletes/{id} – athlete profile with run history and linked photos
  • /judge/events/{slug} – judge panel for recording runs (auth-protected via admin token) ​

How I Built It

Stack and architecture
I used Go 1.22 with net/http pattern routing and html/template for fully server-rendered pages, backed by a single SQLite database file accessed through github.com/mattn/go-sqlite3. There is no build pipeline or frontend framework: just vanilla HTML, CSS, and a tiny bit of JavaScript for auto-refresh.

The project is split into small packages: domain for core types (events, athletes, runs, sponsors, photos), store for SQLite queries, and handler for public and judge HTTP handlers plus authentication and logging middleware. Templates live in templates/ and are composed around a shared layout with a simple nav bar linking Event, Leaderboard, Photos, and Judge.

Data model and scoring

The SQLite schema has seven tables: events, categories, athletes, entries, runs, sponsors, and photos. Entries link athletes to an event and category with a bib number; runs store raw time, touches, missed gates, and computed total time, and a UNIQUE(entry_id, run_number) constraint prevents duplicate runs. Scoring follows standard ICF rules: +2 seconds per gate touch, +50 seconds per missed gate, ranking athletes by their best single-run total time.

UX for each role

For spectators, the leaderboard auto-refreshes every 10 seconds and highlights medals, “NEW” runs, time-behind-leader, and tiny CSS sparklines that compare raw time vs penalties. Commentators get a dedicated view that shows the latest result in huge type with bio and penalties plus current top three for each category, optimized for projection. Judges use a mobile-friendly panel with large tap targets, penalty steppers, confirmation before saving, recent runs, and run-status indicators so they can work quickly in a noisy outdoor environment.

Safety and polish

To keep things safe without overbuilding auth, judge routes are protected with an ADMIN_TOKEN environment variable and a simple session cookie once the token is used. There is server-side validation for time ranges and maximum penalties, basic security headers, and structured request logging with method, path, status, and duration. I also seeded a complete demo event (Demo Slalom 2026) so anyone cloning the repo can see realistic data, categories, and sponsors immediately

Top comments (0)