← All posts

How I Built the NBS App for Our Badminton Group (With a Little Help From My AI Assistant)

12 Oct 2026

I play social badminton with a group called Nova BadmintonSpace, or NBS for short. We're a private group that plays at PeakPlay Badminton Centre in Laverton North, here in Melbourne. I wanted the group to have a proper home online. So I built a small web app for us.

This post is about how that came together: what I built, the choices I made along the way, and what it was like working on it with my AI assistant.

A bit about NBS

NBS is organised into three groups: Meteors, Comets and Pulsars. We run sessions on:

  • Monday, 6–9pm
  • Wednesday, 6–8pm
  • Friday, 6–9pm (doubles)

Play is flexible. You can come for as little as an hour, then add time in half-hour steps, and the cost is shared out after the session. Registration happens through WhatsApp, and new people usually find us and join through Messenger.

It's a simple setup, and it works because it's simple. I wanted the app to respect that.

Where it started: Baddy Buddy

Before NBS had a website, I made a separate little tool called Baddy Buddy (baddy-buddy.netlify.app) to help split costs after a session. With flexible play times, splitting the cost fairly is a bit fiddly, so a tool for it made sense.

Baddy Buddy showed me that small, focused tools can really help a group like ours. It also got me thinking about what else could be easier: a proper home for the group, information for newcomers, and eventually, tournaments.

Writing a spec first

Rather than jumping straight into code, I started with a written spec. This is where my AI assistant was most useful early on. We'd talk through an idea, write it down, poke holes in it, and update the document.

The spec went through versions v0.1 to v0.19 as decisions were made. Ideas got added, refined or dropped along the way. The rule I settled on was simple: the latest decision wins. If v0.9 said one thing and v0.11 said another, v0.11 was the truth. That kept us both on the same page and stopped old ideas from creeping back in.

Having the spec meant that when we did write code, we were building something already agreed on, not working it out as we went.

Later on, I moved the spec into the code repository itself, under docs/spec/, with every version kept side by side. I also made a rule for myself: every change has to update the plans, the docs and the status file, and bump the spec when a decision changes. It sounds like extra work, but it means the documents never drift away from what the app actually does.

The to-do list now lives in GitHub issues, with labels for features, defects and decisions I still need to make, grouped under the November tournament and the work after it. It's much easier to see what's left than a long list at the bottom of a document.

What the site does

The site is live at novabadmintonspace.com. It has three main pages:

  • Home: what NBS is and how to get involved
  • Socials: our regular sessions and how flexible play works
  • Tournaments: we have an internal tournament coming up in late November 2026

There's also a testimonies section, so people can see what the group is like from the members themselves.

Behind the scenes, there are organiser-only admin tools for running tournaments:

  • Tournament setup
  • Eligibility checks: to play in a tournament, you need to have attended 4 sessions in the 60 days before it
  • Pairing players
  • Recording scores and showing a leaderboard
  • Importing WhatsApp lists, since that's where registration already happens

That last one matters to me. I didn't want to change how people sign up. The app fits around what the group already does.

The import got smarter, too. Before anything is saved, it now shows a preview of how each name in the WhatsApp list matches a player already in the app. If two names could be confused with each other, it stops and asks me rather than guessing. Fixing a wrong match afterwards is far more painful than a quick check up front.

Roles: owner and organiser

As more people help run things, not everyone needs the same powers. There are now two levels:

  • Owner/admin: creates tournaments, changes settings, and starts, finishes or deletes them
  • Organiser: does the day-to-day work, like adding players, importing attendance, adding late entries and recording scores

A "Show to public" switch

Each tournament now has its own Show to public switch. New tournaments start hidden, so I can set one up, test it and check everything before anyone else sees it. If something ever looks wrong on the day, I can hide it again with one tap while I sort it out. It's a small feature that gives me a lot of peace of mind.

A full test, and 13 fixes

Before trusting the tournament tools with a real event, I ran a full test of the whole flow: setting up, entries, the draw, scoring and finishing, from both the organiser's side and the public's side, using made-up test players. That turned up 13 defects. Some were things like a double tap saving a score twice, a weak phone signal leaving a save hanging, odd numbers of players in a round robin, and entry deadlines. All 13 are fixed now, each with a test so it stays fixed. I'd much rather find these on a quiet Tuesday night than in the middle of the tournament.

The tech, kept simple

I deliberately kept the stack small:

  • Plain HTML, CSS and vanilla JavaScript using ES modules. No framework and no build step. What's in the repo is what runs in the browser.
  • Supabase for the backend: a Postgres database. Regular visitors never need an account.
  • Hosting on AWS: S3 and CloudFront, with our own domain through Route 53 and an HTTPS certificate.

The hosting has its own small story. I built the prototype on Netlify, which was great for getting something up quickly. When the Netlify credits ran out, I moved the site to AWS and kept Netlify as a fallback. I set an AWS budget of about A$5 a month, with alerts, so there are no surprises. For the pilot, the cost has been close to zero apart from the domain.

Privacy first

This is a community of real people, and I wanted to be careful with their information. A few choices I made early on:

  • Names are shown as first name plus last initial.
  • No payment details, phone numbers or exact attendance counts are public.
  • Payment details never go in the code.

Deciding these things early, and writing them into the spec, made a lot of later decisions easier.

Looking after security

Even for a small group site, I wanted security done properly from the start. Admin sign-in is protected with two-step verification, and the rules about who can see and change what are enforced behind the scenes, not just on the screen. We also did a full security review of the app, and every issue it found was either fixed or reviewed and signed off.

Every change goes through automatic checks before it can reach the live site, and the live site is checked regularly too. None of it is fancy, but it's the kind of thing that's easy to skip on a hobby project, and I'm glad I didn't.

Testing

Working with my AI assistant made it much easier to write tests as we went. The project now has:

  • Over 130 unit tests using Node's built-in test runner
  • A Puppeteer browser smoke check
  • About 300 Playwright browser tests, run in Chrome-style, Firefox and Safari-style (WebKit) browsers, on both desktop and phone-sized screens

One rule I held to: tests never touch the live database. I didn't want a test run messing with real tournament data or member records.

The bug that only happened on my iPhone

One evening, the app got stuck on "Loading…" on my iPhone. It worked fine on my computer, and it had worked on the same phone the day before, which made it extra puzzling.

My first guess, that pages were loading too slowly, turned out to be wrong, and the "speed-up" we shipped for it made things worse, so I rolled it back straight away. To stop guessing, I added a small on-screen message that, if loading takes too long, shows which step it's stuck on and any error. My screenshot of that message gave us the answer: a "Cannot access 'store' before initialization" error.

The cause was a modern JavaScript feature called a top-level await in one shared file. Most browsers handled it fine, but Safari's engine on the iPhone tripped over it when that file was loaded in two different ways at once. The fix was small: load that piece on demand instead. We also added tests that block that pattern, and any JavaScript features too new for older iPhones.

The lessons for me: test on the real phones your people use, don't fix what you haven't actually seen, and when something is hard to reproduce, make the app tell you what's wrong.

Getting found

To help people find the group, I added a sitemap, set the site up in Google Search Console, and made a social preview image so links look good when shared in chats. New people often reach us through Messenger, so a link that looks good there is worth having.

What I learned

A few lessons I'll carry into future projects:

  1. Iterate with a spec, and let the latest decision win. Writing things down and versioning them kept the project focused, and it made working with my AI assistant much smoother.
  2. Keep it simple. Plain HTML, CSS and JavaScript with no build step has been easy to understand, deploy and fix.
  3. Privacy first. Deciding early what's public and what isn't saved a lot of rethinking later.
  4. Move to a framework only if the needs grow. For now, the simple stack does everything we need. If that changes, I'll revisit it, but not before.
  5. Test the whole flow before the real event. A full run-through with made-up players found 13 problems I'd never have spotted otherwise.

Working with an AI assistant didn't mean handing the project over. I still made the decisions about what NBS needed and what it didn't. What the assistant gave me was a patient thinking partner and an extra pair of hands for the spec, the code and the tests.

What's next

The internal tournament is in late November. About a week before, I'll run a dress rehearsal of the whole day, and after that I'll freeze features: only fixes, nothing new, so nothing surprises us on the day.

From December, the admin tools have grown enough that I'm planning to move those pages to a framework, most likely Svelte. The public pages will stay plain HTML, CSS and JavaScript. And if we ever need a proper server, for example to track payments, I'll add a small Python backend then, and not before. That's lesson 4 in action: the needs grew, so now it's time.

Come and play

If you play badminton in Melbourne, or you're keen to start, have a look at novabadmintonspace.com. You'll find our session times, how flexible play works, and details on the internal tournament coming up in late November. We'd love to see you on court.

Built by Nhan Trong Nguyen.