Skip to content

Open Algorithms & Code

Every algorithm that shapes your experience is documented here — in plain language, with the actual formulas, and direct links to the source code.

When an algorithm decides who sees your profile, how your reliability is scored, or which sessions appear in your feed — those decisions affect real-world interactions. A late-cancel penalty isn't just a number; it's the difference between a full table and a canceled game night.

We believe you deserve to understand how every scoring and matching system works. Not in vague marketing terms — in actual math, with the real weights, thresholds, and design tradeoffs explained.

Each section below documents one of our algorithm systems. You'll find the formula, the reasoning behind key decisions, and a direct link to the source code on GitHub. If something seems wrong, you can open an issue.

Player Reliability Score

The Player Reliability Score is an attendance-based metric that reflects your actual track record at game tables — not your popularity, not your social connections, just whether you show up when you say you will.

How It Works

Score = (weighted_sum / game_count) × 100

Clamped to range [0%, 100%]

Attendance status weights
Status Player Weight Host Weight
Attended +1.0 +1.0
Late Cancel (<24h) −0.3 −1.2
No-show −1.0 −1.5
Excused 0.0 0.0
Cancelled Early (>24h) 0.0 0.0

Tier Classification

Reliable

≥ 95% and ≥ 5 games

Active

≥ 5 games (any score)

Newcomer

< 5 games

Design Decisions

  • Hosts face steeper penalties for late cancellations and no-shows because they affect every participant signed up for that game, not just themselves.
  • Grief resistance: peer-reported attendance events carry a weight multiplier to reduce the impact of any single reporter.
  • Scores are fully recomputed on every attendance change (not delta-based) to guarantee correctness and prevent drift.

GM Ratings & Reviews

GM Ratings aggregate community feedback for game masters into an average score and review count. Only published reviews contribute to the aggregate — reported or pending reviews are excluded entirely.

How It Works

Average Rating = COALESCE(AVG(rating), 0)

Review Count = COUNT(*) WHERE status = 'published'

Proficiency Badges = TOP 3 tags by frequency across published reviews

Only reviews with status "published" are included. Reviews that are pending, reported, or removed are excluded from all calculations.

Design Decisions

  • Only published reviews count toward the average. Reported or pending reviews are excluded to prevent manipulation and ensure fairness.
  • Aggregates are recomputed on every review change (create, publish, unpublish, delete) to keep the displayed score always in sync.

People Discovery

People Discovery suggests nearby players based on taste compatibility and social overlap. It uses your game system preferences, vibe preferences, team memberships, and social connections to find people you'd enjoy playing with.

Pipeline

1
Phase 1: Geohash Tile Expansion

Starts at 4-character geohash (~20km). If fewer than 10 candidates found, expands to 3-char (~100km), then 2-char (~500km). Blocked users and existing follows are excluded.

2
Phase 2: Bulk Preference Loading

Loads game system favorites, vibe preferences, team memberships, and follow relationships for all candidates in a single batch.

3
Phase 3: Scoring

Computes taste similarity (Jaccard on game systems + vibes) and social overlap (team overlap + mutual follows). Results are privacy-aware: hidden fields reduce available signals.

4
Phase 4: Paginated Results

Results cached per geohash tile for 5 minutes. Paginated at 12 per page.

How It Works

Taste: J(A, B) = |A ∩ B| / |A ∪ B|

  Computed on game systems + vibes, averaged

Social: (team overlap + mutual follow) / components

Composite:

  if taste & social: score = taste × 0.7 + social × 0.3

  if taste only: score = taste

  if social only: score = social

Scoring component weights
Component Weight (both available)
Taste similarity 0.7
Social overlap 0.3

When only one signal type is available, it receives 100% weight.

Design Decisions

  • Privacy-aware reweighting: when a candidate has hidden fields (game systems, vibes, teams), those signals are excluded from scoring and remaining signals get proportionally more weight.
  • Blocked users are excluded from the candidate pool entirely — they never appear as discovery results.
  • When all signals are hidden (only location visible), the candidate appears with "Nearby" as the sole match reason, so the UI still shows something meaningful.

Session Recommendations

Session Recommendations suggest games and campaigns matched to your preferences. It uses a two-tier approach to surface the best matches first without penalizing users who haven't set vibe preferences.

Two-Query Approach

1
Boosted Query (Primary)

Matches sessions that share your favorite game systems AND favorite vibes. These are the strongest matches.

2
Fallback Query

Matches sessions by favorite game systems regardless of vibes. Surfaces relevant sessions even without vibe overlap.

Results are deduplicated by type+id, with boosted results shown first. Capped at 12 total recommendations.

Preference Resolution

allowed = (favorites + implied_favorites) − avoided

Boosted: allowed AND favorite_vibes

Fallback: allowed (any vibe)

Preference resolution rules
Rule Behavior
Base game favorited Expansions become implied_favorites
Explicit avoid Always wins over favorite or implied
Vibe exclusivity Favoriting one auto-avoids its partner

Design Decisions

  • Favorited base games automatically include their expansions as "implied favorites" — you don't have to favorite every expansion separately.
  • Explicit avoid preferences always win over favorite or implied — if you avoid a system, it stays excluded even if it's an expansion of a favorite.
  • Vibe mutual exclusivity: favoriting one vibe auto-avoids its partner (e.g., "Competitive" auto-avoids "Cooperative").

Proximity Engine

The Proximity Engine powers geographic queries for finding nearby sessions, players, and venue hubs. It uses a two-phase approach: a fast bounding-box filter followed by precise Haversine distance calculation.

Two-Phase Approach

1
Phase 1: Bounding Box Pre-filter

Uses a composite (latitude, longitude) B-tree index for fast row elimination. The bounding box is intentionally slightly larger than the exact search circle to ensure no candidates are missed.

2
Phase 2: Haversine Distance

Applies the Haversine formula for precise distance calculation, filtering results to the exact radius.

How It Works

d = 2R × arcsin(√(

  sin²(Δlat / 2) +

  cos(lat₁) × cos(lat₂) × sin²(Δlng / 2)

))

where R = 6371 km (Earth's radius)

Geohash Tile Sizes

Geohash precision levels and approximate tile sizes
Precision Approximate Size Use
4 chars ~20km × 20km City-level caching
5 chars ~2.4km × 4.9km Neighborhood-level
6 chars ~0.6km × 1.2km Venue-level

Hub results are cached per geohash tile (5-char ≈ 2.4km × 4.9km) with a 15-minute TTL.

Design Decisions

  • The bounding box is intentionally larger than the exact circle. The Haversine formula then filters to the precise radius — this two-phase approach uses the B-tree index for speed.

Platform Score

The Platform Score ranks game systems by community engagement using a weighted formula that accounts for favorites, total games, campaigns, and active (scheduled) sessions.

How It Works

score = (favorites × w₁) + (games × w₂)

      + (campaigns × w₃) + (active_games × w₄)

Type-differentiated scoring weights
Signal Board Games TTRPGs
Favorites 10 10
Total Games 3 3
Campaigns 5 15
Active Games (scheduled) 20 10

Design Decisions

  • Board games weight active sessions highest (20 pts) because a currently-scheduled game is the strongest signal of community engagement.
  • TTRPGs weight campaigns highest (15 pts) because ongoing campaign play is the primary activity metric for tabletop roleplaying groups.

Read the Code Yourself

Every algorithm documented here runs from our open-source repository. Clone it, audit it, open an issue — it's all yours.

You're offline — some features may be unavailable
Back online