Inspiration

I call the same person every night. I built Same Moon for the two of us, and we have used it for 62 nights in a row. Streak apps and couples apps already exist, but none of them felt like the thing we actually share: the same sky, at the same time, from two different places. So the whole app is that sky.

What it does

  • A pixel-art night sky with the real phase of tonight's moon. Two houses sit on the horizon, one for each of you. A window lights up while that person has the app open, and a string of lights hangs between the houses when you are both there.
  • Mark a night after you call and the streak grows. A rare "clouded" night can bridge one bad evening, so a single miss does not erase two months.
  • Video calls are peer to peer over WebRTC. The server only carries the handshake, so audio and video go straight from one phone to the other.
  • A storybook you build together: notes, photos and drawings from a shared canvas. Arrange a page like a scrapbook, then seal it with a kiss. A sealed page can never be changed again, by anyone.
  • Games for during the call (tic-tac-toe, four in a row, pairs) are refereed by the server, so nobody can cheat by editing their phone. ## How we built it
  • Next.js 15 and React 19 in TypeScript, with no UI framework. Every sprite is ASCII art drawn on an integer grid.
  • SpacetimeDB is the whole backend. A Rust module holds every rule (who can answer a ring, when a round is scored, that a sealed page stays sealed), and both phones keep a live subscription, so a change on one phone shows up on the other in about half a second.
  • The iOS app is a Capacitor 8 shell with the native RevenueCat SDK.
  • Tests: 64 unit tests, plus a 168-assertion suite that drives every database rule against a real local SpacetimeDB. ## Challenges we ran into
  • Turning an app built for exactly two people into one any couple can use. Inside the database the two people are now "Moon" and "Star", and their real names live in one row, so a rename never touches a night, a message or a page.
  • Keeping a call from double-offering when both phones ring each other at the same moment. Whoever rang first is always the offerer.
  • SpacetimeDB treats adding a column to a live table as a breaking change, so every new feature lives in its own table to keep our real data safe through every update.

Accomplishments that we're proud of

  • Got our friend a girlfriend
  • Held my first relationship for longer than 30 days

What we learned

  • SpacetimeDB treats adding a column to a live table as a breaking change, even when the table is empty. So every new feature gets its own table, which kept our real data safe through every update.
  • Realtime apps get glitchy when one kind of event re-renders everything. Typing indicators were rebuilding the whole message thread. Splitting the connection, the messages and the games into separate subscriptions fixed it.
  • Building for strangers is a different problem from building for yourself. Names had to come out of the code, and secrets had to come out of the app's bundle. The door word is now checked on the server. I also learned to write down plainly what is and is not private.
  • Price the thing that actually costs money. Photos are the only feature with a real storage cost, so they are what Plus pays for. A subscription for two also needs shared state, and in production that state should be set by a RevenueCat webhook on the server, not by the phone.
  • iOS zooms into any text field with type smaller than 16px, which pushed our lock screen off the side of the phone. I only found it by running the real app in the simulator, not the website.

What's next for Same Moon

Real accounts with each couple scoped to their own rows, the RevenueCat webhook for Plus, a home screen widget with tonight's moon and the streak, and Android.

instructions

HOW TO TRY SAME MOON

Live demo: https://same-moon-demo.vercel.app Door word: moonlit-harbor Works in any browser. It looks best at phone size or on a phone.

GETTING IN

  1. Type the door word and tap Come in.
  2. Pick who you are: Sriyan or Daksha. The couple is already named for the demo.
  3. You will see tonight's real moon phase, the streak, and two houses on the horizon.

SEE BOTH PHONES AT ONCE Open the same link in a second browser or a private window, type the same door word, and pick the other person. The second person's window lights up on the first screen, and a string of lights appears between the houses while both are open. Games and the storybook update live in both windows.

THINGS TO TRY

  • Mark tonight to see the streak celebration (if tonight is not marked yet).
  • Book: add a note page, arrange it like a scrapbook, then tap Seal with a kiss and press and hold the lips. A sealed page can never be edited or removed. The server refuses every change.
  • Games: play tic-tac-toe, four in a row or pairs between the two windows.
  • Call: with both windows open, tap Call. Allow camera and microphone. Video goes directly between the two browsers.

SAME MOON PLUS Storybook photos are part of Same Moon Plus, which runs through the RevenueCat SDK in the iPhone app. In a browser, the Plus sheet says "Plus is in the iPhone app." The demo build has no RevenueCat key configured, so a purchase cannot be completed in the demo.

RUN THE IPHONE APP git clone https://github.com/teameuphoriainv-dot/same-moon cd same-moon && npm install CAP_SERVER_URL=https://same-moon-demo.vercel.app npx cap sync ios Open ios/App/App.xcodeproj in Xcode, choose a signing team, and run it on an iPhone or the simulator. The simulator has no camera, so calls need a real phone.

RUN EVERYTHING LOCALLY The README covers it: a local SpacetimeDB server, the settings in .env.example, and npm run dev.

KNOWN ISSUES

  • On narrow phone screens, the Messages panel is shifted left and partly cut off.
  • The presence line under the moon shows "Star" or "Moon" instead of the person's name.

Built With

Share this project:

Updates

Submission history