Inspiration

Every friend group has the same 40-message thread: "dinner?" "idk where" "I'm vegetarian" "not too far" "who's calling?" Planning apps ask everyone to download something, and nobody does. We wanted planning to happen where the group already lives, in iMessage, with an agent for each person that already knows their limits, so nobody has to repeat them or say them out loud.

What it does

  • Your bubble. Sign up with your phone and your bubble (a private iMessage agent) asks a few quick questions: diet and allergies, budget, neighborhood, favorite spots, how you like to get paid back. With permission it can also see your calendar free/busy, your Focus status, and your location.
  • Your crew. Text "new crew" and drop the join link in your group chat.
  • "dinner tonight?" The bubbles deliberate privately:
    • They pull live restaurants from Google Places near the crew.
    • They drop any place that fails anyone's hard limits: allergies, religious diets, accessibility, budget, distance, opening hours.
    • They score what's left and run fairness-weighted elimination rounds until one place works for everyone, keeping a backup.
    • If nothing fits, a deadlock breaker finds the smallest concession and privately asks just one person (for example, a slightly higher budget). Allergies, religious diets, and accessibility are never relaxed, and nobody ever sees anyone else's limits.
  • The call. A voice agent phones the restaurant to book the table and tries the backup if nobody answers.
  • Booked. Everyone gets an iMessage ticket card that opens a ticket page with Maps and an add-to-calendar link.
  • Helper agents:
    • The Inviter sends each person a calendar invite and the address.
    • The Splitter steps in after dinner: the person who paid tells their bubble the real total, and everyone else gets their share with a Venmo, Cash App, or PayPal link. A ledger keeps track of who pays next time.
    • The Watcher wakes up 45 minutes before the reservation. If someone is 15+ minutes late, it has the caller ask the restaurant to hold the table.
  • The live page. A Windows Vista style desktop where everyone's bubbles drift toward the places that fit them, merge into one crew bubble, split into the helper agents, and pop as each job finishes.

How we built it

  • Stack: a TypeScript monorepo.
    • Fastify + Socket.io server on Railway: plan state machine, solver, durable timers, webhooks.
    • SQLite through Drizzle.
    • Next.js live page on Vercel.
    • A shared zod package holding the event contract and port interfaces, so four people could build in parallel against fakes.
  • iMessage: Photon Spectrum (spectrum-ts) for group and private texts, polls, typing indicators, location pins, and Mini App ticket cards. Photon's Advanced iMessage API for Find My and Focus.
  • Language, never decisions: Claude Haiku 4.5 reads onboarding answers into facts, gives a second opinion on vague plan messages and RSVPs, classifies post-dinner reactions, and words private asks warmly. Every output is zod-validated JSON with a rule-based fallback. Deterministic code makes every decision.
  • Phone calls: an ElevenLabs Conversational AI agent (Claude Haiku as the call brain, Flash v2 voice) dialing out through Twilio. Results come back as structured data collection fields over an HMAC-verified post-call webhook, with polling as a fallback.
  • Data: Google Places API (New) for live venues, OpenStreetMap for seed data, Google Calendar free/busy over OAuth.
  • Live page: a d3-force simulation with custom collision and steering physics, drawn as SVG soap bubbles. A paced event queue waits for each animation to finish before the next step, so viewers see every move.
  • Team: four of us, each running a Claude Code agent with its own role. Teammates relayed structured team messages between the agents. 470+ automated tests.

Challenges we ran into

  • Being in a group chat without being annoying. Our first real three-phone run sent 9 messages per person per plan, and we trimmed hard from there. Only one process may hold the iMessage line at a time, so we used a LINE LOCK protocol between teammates.
  • Privacy. One real-phone run booked a time that had already passed, and the Watcher announced "Mahil is about 123 min behind" to the whole group. We fixed the time fallback, guarded the Watcher, and the crew now only ever sees "on the way / here / running late".
  • Phone calls. English ElevenLabs agents require the Flash/Turbo v2 voices. The first message failed until every dynamic variable was sent. Twilio's trial only calls verified
  • Location. Find My only works once a person shares with their bubble, so we added location pins as an easy, temporary fallback.
  • Making AI deliberation watchable. The engine finishes a whole deliberation in milliseconds, so the live page queues events and waits for each animation to settle.

Accomplishments that we're proud of

  • On the real iMessage line with three phones (demo mode), a plan went from "dinner tonight?" to Booked in about a minute. The poll reached all three phones in 3 seconds.
  • Privacy is built in: models only label and word, deterministic code decides, and invariants like "never relax an allergy" are covered by tests.
  • Four people and four AI agents shipped one coherent product in 24 hours, with 470+ tests passing.

What we learned

Agents in a group chat should be heard as little as possible: every extra message costs attention. A deterministic core with LLMs only at the edges made the system predictable and easy to debug. And fairness matters: tracking who compromised and who paid last is what makes friends trust the result.

What's next for Bubbleworks

Taking calls back from restaurants, more meal types than dinner, more cities, and group chats beyond iMessage.

Built With

Share this project:

Updates

Submission history