Skip to content

<- all work

Connecton

A 3D isometric city where every person you want to keep in touch with lives in a house. Check in to wake it up.

RoleDesign, full-stack build, deploy
Year2026
StackReact 19, three.js, Supabase, Tailwind, PWA, Gemini
Links Live site -> (opens in a new tab)
FIG. 01 Looping clip of the live city. Names are demo data.

The problem

Staying in touch with people is easy to intend and easy to forget. Contact lists and reminder apps feel like chores, so nobody opens them.

The idea

Every connection lives in a house in a small 3D city. A house goes dark and falls asleep when you have not been in touch for as long as you chose for that person. Logging a note wakes it, earns XP, and grows the city. An AI helper drafts your next message from your recent notes, so the hardest part (starting) is done for you.

Isometric 3D city. Five narrow brick buildings stand on city blocks with streets, cars and trees. Three buildings have dark windows and a sleeping Z symbol above them.
  1. 1 Awake. A recent note keeps the windows lit.
  2. 2 3 Asleep. The time you chose for that person has passed, so the windows go dark and a sleeping Z appears.

The map is a 7x7 grid of lots. A house only appears when you add a connection, and more lots open as your city level rises.

FIG. 02 The city, annotated. Names in the picture are demo data.

What I built

  • A 3D isometric city with react-three-fiber: streets, traffic, parks, hills and a harbor, all generated from the account id so no extra data is stored.
  • A server-side rules engine. XP is decided by a Postgres function (log_interaction()), not the browser. Row-level security gives every row an owner, and the 20-connection cap is enforced by the database.
  • AI message drafts through Gemini, with the tone chosen from how you know the person and a rolling daily limit enforced on the server.
  • Push reminders from a scheduled Supabase edge function that runs every 15 minutes, delivered to an installable PWA.
  • A separate landing page in Astro: about 13 KB compressed, no analytics unless configured, and every button opens the app on sign-up.
Connecton system overview The Connecton app runs in the browser as an installable web app built with React, three.js and Tailwind. It sends queries to Supabase, which holds login, a Postgres database with row-level security and the log_interaction function, and edge functions. One edge function drafts messages by calling the Gemini AI. Another runs every 15 minutes and sends web push reminders back to the browser when a house falls asleep. Supabase queries prompt send push: a house fell asleep BROWSER / PWAReact 19three.js (R3F)Tailwindinstallable + push AUTHemail + 6-digit code POSTGRESrow-level securitylog_interaction()20-house cap EDGE FUNCTIONSdraft-messageprocess-reminderscron, every 15 min GEMINIAI drafts WEB PUSHreminders
FIG. 03 System overview: the browser talks to Supabase; edge functions call Gemini and send push reminders.

Engineering notes

  • 18 ordered SQL migrations, each next to its test, run with Node’s built-in test runner.
  • The model files are compressed and checked by scripts (models:compress, models:check) so the 3D scene stays light on phones.
  • Anything that affects fairness or cost (XP, draft limits, the connection cap) lives in the database, not the client.
Where Connecton's rules are enforced Logging a note from the app calls the log_interaction database function, which decides the XP. The XP goes into a ledger per connection and raises the city level, which unlocks more lots. Two layers sit under all of it in the database: row-level security, so every row has an owner, and constraints such as a maximum of 20 houses, one house per lot and house styles limited by level. Row-level security: every row has an owner Constraints: max 20 houses, one per lot, styles by level LOG A NOTEfrom the app log_interaction()decides the XP XP LEDGERper connection CITY LEVELunlocks lots
FIG. 04 Where the rules live: the database decides XP, ownership and limits.

Status

Live and in active development.