Skip to content
← Projects
The Wafflr waffle, peeking over the edge

It was called Wednesday Waffle

A two-minute video, once a week, for the friends you'd otherwise drift from. The rule it was named after didn't make it to summer. The waffle did.

Wafflr is an app for sending a two-minute video to a few close friends, once a week. it started as a rule: post every wednesday inside a 36-hour window, or take a strike. this page is about what we put in the rule's place, and about the harder question for a once-a-week app: what it does the other six days.

App Store screenshot: four friends' faces and the line Share life's moments weekly
App Store screenshot: the Stacks list under the headline 2-minute updates once a week
App Store screenshot: a Stack's member list under the headline Keep in the loop with your inner circle
the launch-month App Store listing

a few of us went to high school together in Irvine. then everyone left for different colleges, and the group chat turned into a meme feed. we knew exactly what everyone found funny and almost nothing about their lives. so we tried something: once a week, everyone recorded a short video for the group. nothing polished. it turned into the best part of the week.

Wafflr is that habit as an app. you join a small private group called a Stack. on your Stack's day you record a waffle: up to two minutes of whatever happened, plus up to three photos, which we call toppings. friends watch, react and comment when they get to it. there's no public feed and no follower count, and a Stack only shows the current round.

i built it with three of the same friends: Arush, Leyland and Advait. we later became The Friendship Company, and this was the first thing we made together.

A plan made of penalties

the first commit is from january 2, 2025. the next morning i wrote a README called "🧇 Wednesday Waffle" and a planning doc that runs 1,179 lines.

The first README, with every Wednesday, the 36-hour window, three strikes and late tags highlighted
the rule was in the name

every wednesday the window opened at midnight. you had until thursday noon to post. post late and your waffle got a "late" tag. miss three wednesdays and you were out of the Stack. the planning doc first floats 24 hours, "like BeReal's 2-min window, but longer".

weekly was the point. BeReal asks for a photo every day. our bet was that a real update takes a week to happen and two minutes to tell.

the planning doc also has a lot of breakfast in it. a four-week streak makes you a Waffle Iron, eight a Waffle Master, twelve a Waffle Legend. if you're about to strike out, your friends can vote to keep you with a Waffle Warranty. there's a group chat called the Waffle House.

every feature had to be a breakfast pun. this was not up for discussion
the plan, jan 3what shipped
a 36-hour wednesday windowa day each Stack picks, with late posting all week
"late" tag on late postsshipped in february, removed in june
three strikes and you're outa bump: one tap to nudge a friend
Iron, Master and Legend badgesstreaks, per Stack and overall
Hot Takes / Life Advice / Check-ins commentsreplies, emoji reactions, voice comments
"Waffie", a waffle mascotthe peeking waffle, five months later

most of what got cut was a penalty.

Five weeks to the App Store

we built it fast. it was renamed Waffl on january 11 and Wafflr two days later, on a branch called "Advait jan 12". we put out a release every week with notes: an MVP on january 14, Alphas on the 21st and 28th, a "Gamma" on february 5. that day Wafflr went live on the App Store, five weeks after the first commit.

the git log shows how the work split. Arush built the first recorder, resumable uploads, the first feed, groups and onboarding, and the code that let a group pick its own day. Leyland built the video player, comments, invites, the late tag, time-zone fixes and the bump. Advait did the rename, the sign-up redesigns, the first-run tutorial and the ring that fills as friends post. i set up the project, sign-in and the database, and did streaks, reactions, account deletion and the App Store submission.

after launch it changed hands. Leyland came back in june for the color overhaul and the posting-day change, Arush worked on signed media URLs in early 2026, and most commits since are mine.

the february version already had the loop: a countdown ("2h 4m left to post"), a ring that fills as people post, a recorder that counts to 2:00, threaded comments, and a push when a friend posts. the Gamma notes add blocking and reporting, which a social app needs before strangers can use it.

App Store screenshot of the recorder counting toward two minutes
App Store screenshot of lock-screen notifications from Wafflr
launch month: the purple-to-pink era

Killing the Wednesday

the wednesday started loosening right away. on january 24, three weeks in, Arush made the posting day configurable. in february Leyland shipped the late tag. then we used it for a few months, and one short window on one fixed day was too constraining.

so we took it apart over the summer.

  • june 17, 2025: the window stopped being a wall. miss your day and the button says "Post late!", any time until the next window. the commit is called "getting rid fo the posting window".
  • june 23: "Posted late!" went too. a late post now just says "Posted!" in green. the commit message gives the reason: "celebrates participation over timing".
  • july 24: we built daily, every-two-weeks and monthly Stacks, next to weekly.
  • january 2026: the 0.4 builds dropped the frequency picker. every Stack you can make today runs weekly.
Bars on a 28-day axis: the README's 36-hour Wednesday windows with tagged late time, today's weekly 24-hour windows with seven days of late time, and faded rows for the retired two-week and monthly cadences
four weeks, to scale. faded rows were retired

every cadence was two numbers: how long the window stays open, and how long a late post still counts. every two weeks was 72 hours and 14 days, monthly 120 hours and 30 days. weekly is 24 hours and 7 days, and it's the one that stayed.

a weekly window opens at midnight on the Stack's day, on each phone's own clock, and stays open for 24 real hours. take a Sunday Stack with one friend in Los Angeles and one in New York. the New York phone opens the window at 9pm Saturday, Pacific time, and closes it three hours before Los Angeles does.

Two bars in Pacific time: the Los Angeles phone's window runs Sunday 12am to Monday 12am, the New York phone's runs Saturday 9pm to Sunday 9pm
New York opens three hours before Los Angeles

the 24 real hours matter twice a year. on november 1, 2026 the clocks fall back, so that Sunday is 25 hours long in Los Angeles, and the window runs from 12am to 11pm. a late post counts for seven 24-hour days from the window's start, so it ends at 11pm on saturday, november 7, an hour before the next window opens at midnight. in spring it runs the other way: the window that opened on sunday, march 8, 2026 stayed open until 1am monday, and its late period ran until 1am on march 15, an hour into the next waffle day.

when you make a Stack you pick the day, and the app says it back in plain words.

Stack settings: a row of weekday circles with W selected and the line Everyone posts on Wednesdays
the wednesday window: 36 hours
Late Waffle 🧇
strike 2 of 3
0 of 4 posted

the Stacks list followed. lateness used to be the loudest thing on the card, "Waffle late!" in red. we flipped it: the countdown gets the accent color, and a late Stack is quiet text with no exclamation mark.

Watching takes one sitting

a weekly habit only lasts if catching up is quick, so watching is meant to take one sitting. the top of the Stacks list is a row called "Hot off the Iron": everyone who's posted, across all your Stacks. the ones you've watched move to the end, and "Play all" plays the rest back to back.

inside a Stack, a waffle doesn't loop. it moves on to the next friend, and the last one holds its frame. it won't advance while comments are open or while you're recording a voice comment. press and hold for 2x. the trade is that nothing replays on its own: a waffle plays once and hands off to the next friend.

there used to be a playback speed menu with six options. we deleted it. that's a control for long videos, and these are two-minute clips from a handful of friends.

App Store screenshot of a friend's waffle playing full screen
App Store screenshot of threaded comments and reactions under a waffle

comments open in a sheet over the video. you can reply, react with any emoji, or leave a voice comment up to a minute long. voice comments record as mono AAC at 64 kbps; it's a voice, so stereo would just be a bigger file.

feedback has a house rule. if you can already see the result of what you did, there's no toast. delete a comment and it leaves the list, and that's the confirmation. toasts are for real failures and for limits you can't see, like "you already posted this week".

A face instead of a gradient

the icon is the part of a weekly app you see on the other six days, and we went through four of them in five months: a W cut into a golden waffle, a cream W on black, and, the day before launch, a purple-to-pink gradient wordmark. the changelog announced it in caps: "NEW COLOR SCHEME, NEW LOGO".

Logo one: a brown W cut into a golden waffle tile
january 9
Logo two: a cream W on black
january 21
Logo three: the word Wafflr on a purple-to-pink gradient
february 4
Logo four: a smiling waffle peeking over the bottom edge, holding on with both hands
june 4

the gradient looked like every other social app from 2025, and four icons in five months meant nobody had time to learn any of them, which is an icon's whole job. in june, Leyland's color overhaul swapped it for cream and dark brown and a waffle peeking over the bottom edge of the icon. the planning doc had asked for a mascot on day two, "Waffie", and said account setup could show the waffle "putting on different hats/accessories". two weeks after the new logo, it started wearing things, each outfit an app icon you can pick. in february 2026 it got a bigger face and eight new ones.1

Eight alternate app icons in two rows: the waffle in 3D glasses, a flower crown, a gold chain, heart eyes, a monocle, nerd glasses, shades and ski goggles
the 2026 outfits

A waffle is a week, not a sitting

for over a year a waffle was one clip. you sat down on waffle day and talked at your phone. the trouble is that most of a week happens on other days.

in april 2026 we added clips. record a few seconds on tuesday, a few more on friday, and the app stitches them into one waffle. it started as a separate vlog mode with its own screen. five days later we merged the two recorders, so there's no mode to pick: a waffle is one or more clips, two minutes total.

the recorder shows controls only as you need them. with no clips it's just the camera. the first clip brings in a filmstrip, a bar for how much of your two minutes is left, and a Review button. the thumbnail beside Record opens "This week's videos", since that's what a waffle is about.

three clips from this week, 0:15 of 2:00

tap a clip to preview it in place, where you can trim it or delete it. long-press to reorder. close the recorder halfway and it asks whether to keep the clips: "Clips stay saved for 14 days." you can jot down talking points too, up to 200 characters, and they float over the camera while you record. the recorder's chrome is plain white, the one surface the theme doesn't touch, so it looks like the iOS camera people already know.

Never post a black waffle

stitching is where a weekly app can lose a week. if the file comes out wrong, your friends open your waffle and get two minutes of nothing, and you don't find out until next week. the guard in this section started with a stitched waffle that came out black. instead of patching the one path that made it, we made the stitcher prove every file before it hands one over.

the stitcher is a native module: AVFoundation on iOS, Media3 on Android. on iOS it picks one of two lanes.

Two lanes. Fast lane: three identical upright clips are remuxed without decoding. Re-encode lane: mixed clips are drawn into a 720p H.264 file in BT.709 at the slowest clip's frame rate

if every clip has the same size, rotation and codec, it remuxes: no frame gets decoded, so it's close to instant. that's the usual case, every clip from the back camera, held upright. if anything differs, it draws every frame into a new 720p H.264 file. a sideways clip gets letterboxed. the frame rate follows the slowest clip, kept between 24 and 60, so a 30 fps clip next to a 24 fps clip renders at 24 instead of padding everything to 30.

camera-roll videos are the hard part. on a recent iPhone they're often 10-bit HDR HEVC. so imports get compressed to the camera's own target, 720p at 2.5 Mbps, when you pick them, and the re-encode lane pins its color to BT.709, so anything HDR gets tone-mapped to SDR on the way through instead of rendering black.

none of that makes you wait. tap Review and the posting sheet comes up at once, with the Stack you started from already checked. the stitch runs underneath ("Getting your Waffle ready…") while you choose, so by the time you tap Share the file is usually done.

then the stitcher checks its own work. an exporter that says "done" has only told you it wrote a file. so after every export it reopens the file and pulls three frames: 0.1 seconds in, the middle, and half a second before the end. each frame is shrunk to 64 pixels, then drawn into a 16 × 16 grid, and every pixel becomes one brightness number from 0 to 255 (0.299 red + 0.587 green + 0.114 blue). the whole probe takes well under 100ms.

here's the same math, run by hand on the three clips from the loop above, fifteen seconds end to end.

Three frames from the stitched waffle, each shrunk to 16 by 16 pixels, scoring 182.8, 93.3 and 185.9 for a mean of 154.0; a number line shows a black file at 0.0 failing the 3.0 threshold and the coffee frame at 5% brightness passing at 4.7
Rec. 601 luma, 0 to 255

the mean is 154.0, so it posts. the cutoff is 3.0, about 1% of full scale, so only a file with almost nothing in it fails. the coffee frame dimmed to 5% still scores 4.7 and passes; dimmed to 3% it scores 2.7 and would be refused. if the fast lane's output comes back under 3, the stitcher retries once on the re-encode lane. if that's dark too, the source clips are dark, and it stops and says so instead of handing back a file. the recorder runs the same check on each clip as you save it, while you can still re-record: "That clip looks black".

the commit that added it is called "never post a black waffle".

after Share, the upload is a job written to disk, so closing the app halfway doesn't lose it; it picks up when you come back. each waffle carries an idempotency key, so an upload that retries after a dropped connection can't post twice.

Six days of not opening the app

you open a weekly app once a week. the other six days, it has to come find you. most of the later work went into the rest of the phone.

there's a home screen widget for your Stack. on waffle day it shows how many friends have posted and what's left for you; the rest of the week it counts down to waffle day.

Home screen widget for Climbing Crew: LIVE, 0/4 friends posted, Record yours
before you post
The same widget after posting: LIVE, 1/4 friends posted, You posted!
after

open your Stack during the window, before you've posted, and it also starts a Live Activity. the lock screen carries the count and a countdown to the end of the window, capped at 8 hours, the most iOS lets a Live Activity run.

Bottom of the lock screen with a Wafflr Live Activity: Climbing Crew, 0/4 waffled, 6h 25m left
the lock screen, sunday at 5:35 pm

none of these surfaces run inside the React app. the widget and the Live Activity are written in TypeScript with expo-widgets, but only the body of the layout function gets shipped to the extension, so the palette and every helper have to live inside that one function. the app also keeps a small copy of your Stacks and shareable waffles in a shared App Group, so the share sheet and the iMessage app can draw without waiting on the network.

the iMessage app drops your waffle into a chat as a card. only your own waffles show up there, because only the person who posted a waffle can create its share link. whoever taps the card without Wafflr gets an App Clip that plays it, with the App Store banner under it.

A Messages conversation with a sent card: Alex Rivera's Waffle, Tap to watch on Wafflr

notifications get batched. when a friend posts, every other member gets a row in a queue. every 30 minutes a job claims those rows under a 10-minute lease, so an overlapping run can't send them twice, and groups them by Stack.

A timeline from 9:00 to 9:30 with four posts by Leo, Sam, Jules and Maya and one job run at 9:30 sending a single push: Leo, Sam & 2 others posted in Climbing Crew
one push instead of four

every sender also checks quiet hours against the time zone your phone reports. if you never set any, posts, comments, reactions and bumps hold off from 10pm to 8am. the copy kept some of the breakfast: a single post is "Order up 🧇" or "Fresh off the griddle", and a bump says "They're waiting for your waffle".

the planning doc's version was "Your friend's waffle is getting cold!"

Moving sign-in without moving anyone

a weekly habit breaks the first week someone gets logged out of their Stack. so when we moved sign-in from Clerk to Firebase phone auth in july 2026, nobody could lose their Stacks, their streaks or their waffles. every row in the database pointed at a Clerk user id, and we didn't rewrite any of them.

Two Firebase tokens: an older account carrying a legacy_user_id claim and a newer account with only its Firebase uid, both resolved by requesting_user_id, which returns the legacy id when present and the Firebase uid otherwise

a small edge function checks the Firebase token against Google's public keys, finds your existing profile by phone number, and writes two custom claims onto your token: a Postgres role and your old id. one SQL function, requesting_user_id(), returns the old id if there is one and the Firebase id if there isn't.

that function is the only door. access goes through Postgres row-level security: a waffle, its photos, its comments and the member list are read through policies scoped to that Stack, and every policy asks requesting_user_id() who you are. so the move changed one function and no policy. integration suites run the write paths with real tokens for isolated test users, so it got tested against the actual policies.

the same rules cover the rest of the trust model. a co-admin can edit settings and remove members, but can't remove the admin or delete the Stack. deleting your account is self-serve, with 14 days to change your mind, and the backups honor it: deleted files leave the Cloudflare R2 copy a month later. the app is Expo and React Native on Supabase, and since september 2026 it also ships straight to Android as a signed APK.

What's left of Wednesday

the rule we named it after was the first thing we wrote and the first thing we took apart. the habit never needed it. the Android package is still called com.arnenoori.wednesdaywaffle.

  1. party and strawberry went. 3D glasses, a flower crown, a gold chain, heart eyes, a monocle, nerd glasses, shades and ski goggles came in. ↩