Call roll and everyone shoots the same moment. You see it all the next day.
SocialTypeScript · HTMLGitHub @deshpandead29OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A shared camera for friend groups: call roll and everyone shoots the same moment, with short challenges, and it all develops the next day.
CLAUDE.md: the build brief. Start here.prototype/index.html: the working prototype (design, rules, photo look, sample event). Serve the folder over HTTP to try it (npx serve prototype); the sample photos won't process from a file:// URL. Outside claude.ai the prototype has no database, so creating groups is disabled; the sample event and "Try the look" still work.docs/challenges.md: every challenge.docs/recreate-challenges.xlsx: recreations with copyright notes.docs/research.md: the research behind the decisions.docs/credits.md: photo and font credits.Next.js (App Router) and Supabase. Node 20 or later.
npm install
cp .env.example .env.local # then fill in the two values
npm run dev
Needs Docker. npx supabase start applies supabase/migrations, uses the auth settings in supabase/config.toml and prints the API URL and publishable key for .env.local. Sign-in emails land in Inbucket at http://127.0.0.1:54324.
npx supabase link --project-ref <ref>, then npx supabase db push. Or paste each file in supabase/migrations into the SQL Editor, oldest first.https://<your-domain>/** (plus Vercel preview URLs if you use them) to the redirect URLs.smtp.resend.com, port 465, user resend, password a Resend API key), raise the email rate limit, then paste supabase/templates/sign-in.html into both "Confirm signup" and "Magic link", and supabase/templates/email-change.html into "Change email address". The default templates work, but only send a link. Ours add a code, because a home-screen app on iPhone doesn't share cookies with Safari, so the link alone signs people in on the wrong side.npx web-push generate-vapid-keys): NEXT_PUBLIC_VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY (secret).supabase/migrations/20261021000000_foundations.sql has every table from the brief, plus a timezone on groups and host_member_id on events. Row-level security is on everywhere: people see only their groups, and other people's photos (rows and Storage objects) stay hidden until the reveal. Groups are made with create_group() and joined with join_group() from an invite link; group_preview() lets the invite page show the group before you sign in. Events are made with create_event(), which saves the event and its guest list together. event_shot_counts() gives the group each person's shot count without unsealing anything. Roll Calls go through call_roll() and answer_roll(), which apply the budget, reserved call, one-per-person rule, cooldown, backed extras and answer windows in one transaction. roll_state() returns the same check for the button, so the app and the server always agree. ask_extra() and back_extra() handle asking for one more. Roll calls, asks, backs and statuses are published to Realtime. Push: devices register with save_push_subscription(); after a call, /api/push/roll-call gets the recipients once through roll_call_push_targets() (caller only, under a minute old, snoozed and home left out) and sends with web-push.
Profiles (20261027000000_profile.sql): one name and one away card per person. set_my_name() and set_my_away_card() update every group you're in; making or joining a group copies them in. Profile cards are stored at away-cards/u/{user_id}/.
Start-day alerts (20261031000000_start_push.sql): "Sam's 30th is today", once, from 09:00 local on the first day, sent the same three ways as reveal alerts.
Reveal alerts (20261030000000_reveal_push.sql): "Lisbon is out" goes to everyone on an event once it reveals. Whichever comes first sends it, once: the host pressing Reveal now, anyone in the group opening the app after the reveal time, or the daily Vercel cron in vercel.json (needs CRON_SECRET in Vercel and the same value in private.secrets).
Letter-day word (20261029000000_hunt_word.sql): events.hunt_word, set by the host when creating a trip; event_details is rebuilt to include it.
Negatives (20261028000000_negatives.sql): at capture the phone also uploads a 45×60 inverted, tinted negative to the negatives bucket. The group can read those before the reveal, and event_negatives() lists which shots exist without saying what they are.
Moderation and cleanup (20261026000000_moderation.sql): report_photo() reports a revealed photo; the event's host and the group's maker see reports (reports table) and can remove any revealed photo, and owners can always delete their own. leave_group() deletes your photos and event places, keeps Roll Calls you started, hands events you host (and the group, if you made it) to whoever joined first, and leaves your name unclaimed so you can rejoin as yourself; the last person out deletes the group. Anyone in the group can join an event until its reveal; late joiners sort last so nobody else's challenge moves. Photo files are cleaned up by their uploader: Storage only deletes what you can read, so my_orphaned_files() lists your files that no row points at any more and the app removes them on load. Until then nobody else can read them, because reading a photo or away card now needs a live row.