Skip to content

Case Study

Dice Cat: Tabletop Gaming Matchmaking App

Role: UX Designer & Full-Stack DeveloperDuration: 2024-2026Status: Live on Google Play
ReactIonicTypeScriptSupabasePostgreSQLPostGISFirebase Cloud Messaging

Scale

1,413

Commits

~72,300

Lines of TypeScript

87

React components

10,000+

Games in the database

86

SQL schema files

<500 ms

Match query time (from 3-5 s)

The Problem

Tabletop gamers accumulate large collections and strong preferences, but finding local players who own the same games, have overlapping schedules, and match on play style is genuinely hard. Existing options - Facebook groups, Discord servers, Meetup - require manual coordination and have no game library matching.

The core ask: build something like Tinder for board games that actually knows your library.

Overview

The product started as "Meeplr" - a UX concept with substantial research before a line of code was written.

  • ·Competitive analysis of existing community and matchmaking tools
  • ·User persona development - a relocated gamer with no local gaming group
  • ·Three rounds of think-aloud usability testing with 15 participants
  • ·Kano analysis: all planned MVP features tested as performative or attractive
  • ·Renamed to Dice Cat after trademark research blocked the original name - improved recall in testing

Key Features

  • ·Multi-dimensional matching: game libraries, geographic proximity, schedule overlap, and play style preferences
  • ·10,000+ game database with local fuzzy search and manual addition capability
  • ·Visual weekly schedule builder with overlap detection across potential matches
  • ·Connection request system with mutual matching before messaging
  • ·Encrypted in-app messaging for session planning
  • ·Push notifications via Firebase Cloud Messaging
  • ·Custom SVG avatar builder with layered component system
  • ·Nearby players map view via PostGIS geospatial queries

Screenshots

Technical Architecture

FrontendReact with Ionic framework for native-feeling mobile UI
LanguageTypeScript throughout
BackendSupabase (PostgreSQL, Auth, Realtime) with Row Level Security
GeospatialPostGIS for proximity queries - optimized from 3-5s to under 500ms via strategic indexing
Push notificationsFirebase Cloud Messaging, migrated to Supabase Edge Functions for reliable permission handling
MessagingEncrypted in-app messaging with Supabase Realtime subscriptions
Game databaseOriginally BoardGameGeek API; rebuilt as local database after API access was discontinued
BuildAndroid Studio for Google Play builds

Technical Highlights

Database performance

Multi-table JOIN queries for match scoring originally ran at 3-5 seconds. Strategic PostGIS indexing and query restructuring brought them under 500ms.

External API dependency failure

BoardGameGeek discontinued API access mid-development. Pivoted to building a local 10,000+ game database with community-sourced additions - better offline behavior and no future dependency risk.

Push notification permission architecture

A trigger-based approach encountered permission errors in production. Migrated the logic to Supabase Edge Functions, which resolved the permission context problem cleanly.

What I Learned

A matchmaking app is judged on whether people message each other, which turns out to be a different target from whether the matching is correct.

A compatibility score is a product decision, not a calculation

Users reported that scores sat consistently under 20%, and the effect was that nobody felt encouraged to reach out. The maths was defensible; the product was failing anyway. Rebalancing the bases and weights, so a good match reads 76% rather than 65% while weak matches still read clearly weaker, changed behaviour without changing the ranking. A score shown to a person is an invitation, not a measurement, and it has to be calibrated for the decision it is asking them to make.

Fetch-everything works until somebody is a heavy user

Conversations with over a thousand messages took about ten seconds to open. Loading the hundred most recent in descending order and reversing them for display brought that under a second, and changed nothing for short conversations. The query was fine on every account that existed when it was written, which is exactly why this class of problem ships.

A required field that is not universally required

Profile validation demanded a location from everyone, including players who only play online and have no use for one, so a whole category of user could not finish signing up. Making location conditional on the play preferences, and saying which case applies in the label rather than in a note, fixed both the block and the confusion behind it.

Links