Inspiration
I live in Florida. My D&D group lives in the Netherlands - except for one player, who's in Finland.
For a while we made it work. Then adult life happened: jobs, partners, kids, a significant time difference, and a Discord channel where "can everyone do this Sunday?" would sink under forty messages of memes before three people had answered.
The night that broke it for me, I logged on for a session and sat there alone. Half the table never showed. Not because they didn't want to play, but because they'd never read the message, and nobody noticed that no response isn't the same as yes.
That's the entire problem, and it's not a people problem. Group chats are structurally terrible at scheduling because silence and agreement look identical. There's no state. Nothing anywhere tracks who is actually in, so the organizer becomes a human polling loop, and eventually the organizer gets tired and the campaign quietly dies.
So I build the thing I wanted: a place where proposing a game night creates a small piece of shared state that everyone can see, that chases people on its own, and that resolves itself without anyone having to play secretary.
What it does
Next Session runs one loop: Propose → Vote → Confirm.
Someone proposes a session from a list of dates where enough group members are available, the group votes, and the session automatically locks itself in once enough people accept. Conversely, when enough people decline, it denies itself instead of sitting in limbo forever.
Each group sets how many players can be missing and still play, so a five-person table with a threshold of one locks in at four accepts. Everyone also keeps a personal availability calendar - recurring ("never Mondays), plus one-off overrides - which aggregates into a group view so a proposer can see which days are actually open before proposing.
And then the part I'm proudest of: the auto-scheduler. Turn it on, pick a time of day, and the moment one session wraps, Next Session searches forward for the first date the table is free and proposes the next one by itself. No rule to write, no cadence to configure. You stop scheduling entirely; sessions just keep appearing.
That feature is free. More on why below.
How we built it
Flutter and Material 3 on the front end, shipping to Android and the web from one codebase, with a genuinely separate desktop layout rather than a stretched phone UI. go_router for navigation, Riverpod for state, as recommended by the wonderful CodeWithAndrea. Firebase for everything behind it: Auth, Firestore, Cloud Functions, Storage, Cloud Messaging, App Check. Monetization though Revenuecat - purchases_flutter on mobile, and Revenuecat's web integration with Stripe for browser purchases. Because Next Session spans web and Android, tracking subscription state across platforms could have been painful. Revenuecat eliminated that headache by flawlessly syncing entitlements between Stripe and the app. AdMob on mobile for the free tier.
The architectural decision I'd defend the hardest is that schedule is event-driven, not a cron sweep.
The obvious way to build recurring session is a periodic job that wakes up and asks "does anything need generating?" I did it the other way: exactly one upcoming session is ever in flight per group, and the next one is generated by a Firestore trigger the instant the current one resolves - completes, gets canceled, or fails to vote. There's no polling and no drift. Firebase Cloud Messaging ties directly into these event-driven triggers, making the localized push alerts fire instantly without needing a polling loop.
That one choice removed a whole category of UX I never had to design. Cancelling an auto-scheduled session is just... cancelling a session. There's no "this occurrence / this and following / the entire series" modal, because there is no series sitting in the future - only the next one, which generates from the current state whenever the current one ends. The feature got simpler to use and cheaper to run at the same time, which almost never happens.
The transatlantic table shaped a lot of the rest. Every notification is rendered in the recipient's own locale and local time, because "Sunday 19:00" means two different moments to me and my group. The app ships localized, and the server-side notification copy is localized too - a Dutch player gets Dutch push notifications about a session an American proposed.
Challenges we ran into
The paperwork was the long pole, not the code. I formed Stayzeef Industries LLC to ship this properly, and discovered the dependency chain is completely sequential: state filing → EIN → business bank account → paying for corporate accounts → migrating the app. Every link waits on the one before it. The D-U-N-S number alone quoted up to 30 business days, and I needed it before I could even create a Play Console account.
Who owes the sales tax? Wiring up web payments, I hit a fork in Revenuecat's web setup that looked technical and wasn't. The two paths differ in who is the merchant of record - and therefore who is liable for calculating, collecting, and remitting sales tax in every jurisdiction a customer might live in. For a one-person LLC, taking that on myself was not a realistic option. I picked the path where Stripe is the merchant of record and handles tax, which meant building the product catalog, tax codes, and pricing tiers in Stripe and letting Revenuecat sync against it.
AdSense rejected me, and it made the product better. I'd planned ads on the web build. AdSense denied the application - fairly, in hindsight, since it's build for content sites and my web app is a canvas. Rather than hunt for a replacement network, I made the web build permanently ad-free. It feels dramatically more premium, and it turned out to be a decision I'd have wanted to make anyway.
Mobile ads are a chicken-and-egg problem. AdMob wouldn't let me finish consent configuration until the app existed in the Play Store - which needed the D-U-N-S number, which needed the LLC, another link in the same chain.
Signed correctly, rejected anyway. Google sign-in failed with a useless "unknown error" during testing. The cause: Play's Internal App Sharing resigns your build with a different certificate than you app-signing key, so the SHA-1 registered in Firebase didn't match what was actually running. Hours of debugging for a fingerprint mismatch...
What we learned
Deciding what's free is a product decision, not a pricing one. I originally gated the auto-scheduler behind the paywall - it's the flagship feature, so it seemed obvious. Then I reversed it, and I think that reversal is the best call I made. The auto-scheduler is what makes the app sticky: a group that turns it on never has to think about scheduling again, so they keep coming back, and free users monetize through ads anyway. Paywalling my own retention mechanic would have been charging for the reason to stay. Next Session+ now sells the refinements around it - recurring rules with full cadence control, calendar sync, vote reminders, custom group images, and no ads.
Building is the fun part; marketing is the work. My landing page sat live for two weeks with essentially no organic traffic. Nothing about writing good code causes anyone to find you!
Compliance isn't a checkbox when your users are the ones it protects. My actual players are in the EU. Analytics ships off by default, region-scoped: opt-in for the EEA/UK/Switzerland, opt-out elsewhere, with consent revocable from Settings at any time. It wasn't theoretical - I was building it for my friends.
What's next for Next Session: Game Night
Public launch on Google Play, then actually marketing it: the TTRPG communities first, since that's where the pain is sharpest, then the broader "any group that can't pick a date" audience - board game nights, book clubs, climbing groups, etc. iOs is deferred but the code base is structured for it. I couldn't justify paying Apple the $99 per year without a user base on Android and web first.
Log in or sign up for Devpost to join the conversation.