Short-form audio,
visual-first feed

Creators record original 15-second clips. Listeners browse a scrollable card feed — each card shows a waveform preview they tap to play. React, follow, tip, share. Think SoundCloud meets TikTok, built around a specific, hilarious content niche.

App name: RipFeed. Domain: ripfeed.io. "Rip" covers ripping audio, ripping farts, and ripping through a feed — passes App Store review and AdMob brand safety without friction.

React Native (Expo) Supabase Cloudflare R2 FFmpeg Cloudflare Pages AdMob RevenueCat Solo dev · minimal cost

Tech stack

Layer Choice Reason
Mobile React Native (Expo) free Single codebase for iOS + Android
Backend / DB Supabase free tier Auth, Postgres, Realtime, Edge Functions — generous free tier
File storage Cloudflare R2 ~$0/egress Zero egress fees — the critical cost saver for audio streaming
Audio processing FFmpeg on Fly.io Transcode to AAC + generate waveform PNG on upload
Web player Cloudflare Pages free Public /clip/[id] player — no app needed. Powers the viral sharing loop.
Ads Google AdMob free SDK Native feed ads, interstitials, rewarded video — React Native SDK
Subscriptions RevenueCat free tier Creator Pro + coin packs, handles App Store / Play Store billing
Push Expo Push free Reactions, follows, new clips — switch to OneSignal at scale

Web player + shareable links

How it works

Every clip gets a permanent public URL: [domain].com/clip/{clipId}. This is the engine of organic growth — a fart clip is one of the most naturally shareable things on the internet, but only if the recipient can hear it without installing the app first.

1
User taps share in app
App constructs [domain].com/clip/{clipId} and calls Share.share(). One line of code.
2
Link unfurls in iMessage / Twitter / WhatsApp
Open Graph meta tags render the waveform PNG as the preview image. Immediately recognisable as audio content.
3a
Recipient has the app → Universal Link
iOS Universal Links / Android App Links intercept the tap and open directly to clip/[id].tsx in the app. No browser, no friction.
3b
Recipient doesn't have the app → web player
Cloudflare Pages serves the clip page. They hear the clip. A "Get the app" banner at the bottom drives installs. This is the acquisition moment.

Web player stack

Minimal Next.js site deployed to Cloudflare Pages. Reads clip metadata from Supabase's public API (anon key, no auth). Streams audio directly from R2 CDN. No new backend infrastructure.

What it renders
Waveform PNG, play/pause, username, title, tags, play count. Plus "Get the app" CTA.
Audio playback
Plain HTML5 <audio> element pointing at the R2 CDN URL. No library needed.
Cost
Cloudflare Pages is free. Audio egress from R2 is zero. Total addition: $0.

Open Graph tags (per clip)

<!-- These power link previews in every messaging app -->
<meta property="og:title" content="@username on RipFeed" />
<meta property="og:description" content="Clip title + tags" />
<meta property="og:image" content="https://cdn.[domain].com/waveforms/{clipId}.png" />
<meta property="og:audio" content="https://cdn.[domain].com/clips/{clipId}.aac" />
<meta property="twitter:card" content="player" />

The waveform PNG generated by FFmpeg doubles as the OG image — visually distinctive, immediately communicates "audio clip," no extra work.

App Store strategy

Locked
App name: RipFeed
"Rip" covers ripping audio, ripping farts, and ripping through a feed. Passes App Store review and AdMob brand safety without friction. Domain: ripfeed.io.
ripfeed.ioReady for submission
Locked
Age rating: 12+
Self-declared during App Store submission. "Infrequent/Mild" crude humor. Don't attempt 4+ — reviewers will reject. 12+ does not meaningfully reduce the addressable audience for this content type.
Crude humor12+
Ships in Phase 1
UGC compliance
Apple guideline 1.2 requires: content filtering/reporting mechanism, ability to flag content, ability to block users, and contact info for reporting. These must be present at first submission. A "report" button + block flag in the DB is enough for v1.
Report clipBlock userContent policy
Risk
AdMob CPMs
Some premium advertisers block humor/audio apps for brand safety reasons. Fill rates and CPMs may be below benchmarks. Have a backup network in mind: Meta Audience Network or ironSource. Monitor after launch and switch if needed.
Brand safety riskMonitor post-launch
Advantage
Android first
Google Play is more permissive on this content category. Consider a soft Android launch to build audience and iron out bugs while navigating iOS App Store review in parallel.
Lower review riskParallel track
Note
Screenshots + description
Lead with the product concept ("short-form audio social") not the content in your App Store description. Show clean UI in screenshots — waveform feed, recording screen, profile. Don't wave a flag at reviewers.
Lead with productClean screenshots

Copyright + DMCA

Users will attempt to upload fart sounds from movies, TV, and YouTube. A DMCA takedown process is legally required before launch. More importantly, "original recordings only" as a community value keeps the social dynamics interesting — you want performers, not a clip repository. Enforce this in community guidelines from day one.

Upload flow + feed design

Upload flow

1
Record on device
Max 15s enforced client-side via expo-av. User can also pick from library.
2
Request presigned R2 URL
Client calls a Supabase Edge Function which returns a time-limited upload URL. Backend never touches audio bytes.
3
Upload direct to R2
Client PUT to the presigned URL. Audio goes straight to Cloudflare — no server relay.
4
Trigger FFmpeg worker
Edge Function calls the Fly.io worker with the R2 object key.
5
FFmpeg single pass → two outputs
Transcode to AAC 128k (hard-capped at 15s) + render waveform PNG (300×64px, transparent bg, purple bars).
6
Store URLs in Supabase
Worker uploads both files to R2 and writes audio_url + waveform_url to the clips table.

FFmpeg command

# Single pass — two outputs — transparent waveform background
ffmpeg -i input \
  -c:a aac -b:a 128k -t 15 output.aac \
  -filter_complex "aformat=channel_layouts=mono,compand,showwavespic=s=300x64:colors=#7F77DD|0x00000000" \
  -frames:v 1 waveform.png

# Filter chain:
#   aformat=channel_layouts=mono  — collapse stereo → unified waveform shape
#   compand                       — dynamic range compression, every clip looks interesting
#   showwavespic=s=300x64         — render static waveform image
#   colors=#7F77DD|0x00000000     — purple bars, transparent background

Feed design

Implemented as a FlatList. Each item is a clip card with: avatar, username, timestamp, clip title/tags, waveform PNG, play button, play/reaction counts. Ad cards are injected every 8–10 organic cards, styled to match but labeled "Sponsored."

Native feed ads
Every 8–10 cards. Looks like a clip card. Labeled "Sponsored." Highest fill rate, least disruptive.
Interstitial
Full-screen on app open or after 5 clips played. Highest CPM. Capped at 1 per session.
Rewarded
Opt-in 15s video. Earns coins. Highest eCPM of all formats. Zero user resentment.

monetization

Phase 1 — launch
Native feed ads
AdMob native ads every 8–10 cards. Styled to match clip cards. Works from day one, no audience minimum needed.
CPM passiveLow disruption
Phase 2 — growth
Interstitial banners
Full-screen AdMob interstitials on app open or after 5 clips. High CPM. Capped at 1 per session — no exceptions.
High CPMUse sparingly
Phase 2 — growth
Rewarded ads
15s opt-in video via AdMob. Earns coins. Highest eCPM of all formats. Fully voluntary — zero resentment.
Highest eCPMOpt-in only
Phase 2 — growth
Listener tipping
Coin packs via RevenueCat IAP. Users tip creators. Platform takes 15–20%. Coins also earnable via rewarded ads.
Network effectSocial feel
Phase 3 — scale
Branded challenges
Brands sponsor audio challenges. Direct sales, high margin. Native feed slot + challenge banner. Sell direct to small brands first.
High marginCommunity

User tiers

Tier
Free
15s clip uploads
Sees native feed ads
Sees interstitial ads (1/session)
Can earn coins via rewarded ads
Can tip creators
Standard feed ranking
Tier
Creator Pro — $2.99–4.99/mo
No ads. Ever.
Analytics dashboard
Custom profile theme
Priority in recommendations
Pro badge on profile
30s uploads — deferred. 15s is sufficient for this content type. Revisit in Phase 3 alongside duet/remix features.

Enforced via usePro() hook — every ad component checks isPro before rendering. If true, skip entirely — no placeholder rendered.

Key rules — never break these

Pro users never see ads. Check usePro() before rendering any ad component. If isPro === true, skip entirely — don't render a placeholder.
15s cap is server-side. FFmpeg -t 15 is non-negotiable. Never trust the client duration. Pro users get 30s via a separate FFmpeg flag.
Audio never passes through the backend. Always use presigned R2 URLs for upload and Cloudflare CDN URLs for playback.
Waveform PNGs have transparent backgrounds. The 0x00000000 second color in the FFmpeg filter is mandatory. Required for correct rendering on light and dark card backgrounds.
Interstitials capped at 1 per session. Tracked in a module-level variable in admob.ts. Never show an interstitial if one has already been shown this session.
Coin balance is authoritative in the DB. Never trust client-side coin counts for tip transactions. Always read from profiles.coin_balance and decrement server-side in a Supabase Edge Function.
Report + block must ship at launch. Apple requires UGC moderation mechanisms at App Store submission. Not optional. A report button that logs to Supabase and emails you is sufficient for v1.
Original recordings only. Don't build features that facilitate uploading third-party audio. DMCA takedown process required before launch. "Original recordings" is both a legal requirement and a community value.
Web player reads public data only. The Cloudflare Pages site uses Supabase's anon key against public clip data. No authenticated routes, no sensitive data exposed on the web player.

MVP roadmap (revised)

Phase 1 — Core + compliance + viral loop (weeks 1–6)
Ship first
Weeks 1–3 — Infrastructure
Supabase setup + migrations Expo scaffold + Expo Router Auth screens R2 bucket + CDN config FFmpeg worker on Fly.io
Week 4 — Upload + feed
Audio recording (15s cap) Upload flow → R2 → FFmpeg Scrollable feed (chronological) Tap to play (CDN streaming)
Week 5 — Web player + sharing
Cloudflare Pages /clip/[id] OG meta tags HTML5 audio player "Get the app" banner Share button in app Universal Links (iOS) App Links (Android)
Week 6 — Compliance + polish
Report clip Block user Written content policy Age rating: 12+ Basic profile page App Store assets TestFlight beta
Phase 2 — Social + first revenue (weeks 7–10)
Monetize early
Follow / unfollow Emoji reactions Push notifications AdMob native feed ads Creator Pro subscription Pro users = no ads Pro badge on profile
Phase 3 — Discovery + tipping (weeks 11–14)
Engagement
Trending / ranked feed Hashtag / category tags AdMob interstitials (capped) Rewarded ads Coin packs + tipping Creator analytics Revisit Pro differentiators
Phase 4 — Scale (weeks 15+)
Growth
Branded challenges Comments Search by user / tag Multi-clip stories / duet Advanced moderation tools

Estimated costs

1,000 active users / month
Supabase (free tier)$0
Cloudflare R2 — audio + waveform PNGs (~300MB)~$0.01
Fly.io FFmpeg (transcode + waveform render)~$3–5
Cloudflare Pages (web player)$0
AdMob / RevenueCat (free tiers)$0
Expo Push notifications$0
Total≈ $3–6 / mo
100,000 active users / month
Supabase Pro$25
Cloudflare R2 (30GB+ audio + waveforms)~$5–10
Fly.io workers (more concurrency)~$20–40
RevenueCat (scales with revenue)~$0–99
Total~$50–175 / mo

R2's zero egress fees are the primary reason costs stay this low at audio streaming scale. AWS S3 would add ~$0.09/GB in egress alone.

Environment variables

Supabase
EXPO_PUBLIC_SUPABASE_URLyour project URL
EXPO_PUBLIC_SUPABASE_ANON_KEYpublic anon key
SUPABASE_SERVICE_ROLE_KEYserver / worker only — never in client bundle
Cloudflare R2
R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET_NAME
EXPO_PUBLIC_R2_PUBLIC_URLCDN base URL for playback
AdMob — use test IDs during development
EXPO_PUBLIC_ADMOB_APP_ID
EXPO_PUBLIC_ADMOB_NATIVE_AD_UNIT_ID
EXPO_PUBLIC_ADMOB_INTERSTITIAL_AD_UNIT_ID
EXPO_PUBLIC_ADMOB_REWARDED_AD_UNIT_ID
RevenueCat
EXPO_PUBLIC_REVENUECAT_API_KEY_IOS
EXPO_PUBLIC_REVENUECAT_API_KEY_ANDROID
Fly.io FFmpeg worker
FFMPEG_WORKER_URL
FFMPEG_WORKER_SECRETshared secret for webhook auth