Timeline March – April 2026
Location Valkyrie Ranch, Paige TX

Peak State Festival App

A progressive web app, custom CMS, live-sync Worker, and offline-first map. Built solo in 28 days for a 4-day desert festival and feature film shoot.

React + TypeScript Cloudflare Workers PWA / Offline-First Map Engineering Brand Adaptation Live Festival Hotfix Ops
Aerial footage by Francesco Artes // Peak State 2026

A festival app for a festival that's also a movie set.

Peak State was a music and arts festival held April 16-19, 2026, at Valkyrie Ranch outside Austin. It also served as the live set of Peaking, a feature film by director Brent Pella and producer Nikki Howard. Every attendee was potentially on camera. Six stages, 27 announced artists, two giant art cars (Abraxas the 60-ft fire dragon, El Pulpo Mecanico the 28-ft mechanical octopus), and a 73-scene shooting schedule.

Jesse Brede, the festival's Technical Production Manager and CEO of Gravitas Recordings, asked on March 19: "What do you think it would take to make a SUPER simple iPhone/Android app for Peak State festival?"

28 days later, the app went live at app.didyoupeakyet.com: a custom CMS, a Cloudflare Worker syncing five data categories from Google Sheets, an offline-first map of all 24 venue pins, and a film tab that explained to attendees how to be in the movie.

I built and shipped all of it solo, running a custom agent swarm of Claude, Gemini, ChatGPT, and Grok. Peak State was the first production-pressure test of the early setup that has since become Murmuration.

Peaking / Peak State official poster: full lineup including Sabo, Goldcap, Equanimous, Skysia, Ruby Chase, Savej, dates April 15-19 2026, Valkyrie Ranch, feature film shoot with Brent Pella and Nikki Howard
Official poster by Danielle Capra

The instinct says native. The math says PWA.

When somebody says "iPhone and Android," the first thought is two native apps: Swift for iOS, Kotlin for Android. Two codebases. Two App Store submissions. 3-7 day reviews per change. For a one-time festival on a tight timeline, that's the wrong tool.

The commercial festival app vendors (Aloompa powers Bonnaroo and CMA Fest; Greencopper powers SXSW and Lollapalooza) solve this problem, but they charge enterprise rates and lock you into their platform. A Progressive Web App skips both problems: scan a QR code, tap "Add to Home Screen," and it shows up as a full-screen app on your phone. Caches everything locally, works offline, updates in seconds.

Approach Build time Codebases Offline App Store Update speed
Native (iOS + Android) 4-8 weeks 2 Yes, complex Required 3-7 days
Hybrid (Capacitor wrap) 2-3 weeks 1 Yes Required 3-7 days
Commercial vendor Weeks + onboarding Their platform Varies Their call Their timeline
PWA 2 weeks
(core shell dev only)
1 Built in None Instant
Mobile website 1 week 1 No None Instant

The offline thing is why this matters most

Cell service at festivals is notoriously unreliable. Valkyrie Ranch is 500 acres, about an hour east of Austin, with thousands of phones hammering the same tower. Any festival app that requires a constant internet connection is useless during the event itself.

A PWA's service worker caches the entire schedule, map, and emergency info on first load. The app runs in airplane mode after that. When connectivity comes back, it syncs in the background. This is the single biggest technical decision and it's why every other approach loses on this constraint alone.

From "SUPER simple" to a full PWA in 4 days.

Day 1, I quoted $4,500 to build it. The festival didn't have budget available for that. I decided to build it anyway. We shook hands over SMS. No contract, no work-for-hire. This was an opportunity to pressure-test an early version of my agent swarm. Building under pressure is fun for me.

Day 4 was a working v1 prototype with a Peaking movie poster I generated for the splash screen. Day 5, after the production team sent over the official "Peaking" PSD, I rebuilt v2 against the festival's actual brand: poster-derived palette, extracted logo, 27 artists, tiered lineup, secret headliner card, pre-fest Day 0, the official Peaking poster as the splash, and an Experiences section.

v1 Peaking movie poster: Gemini-generated illustration with cassette tape portal, festival-goers, school bus, DJ booth, Orgy Dome, fire pit, with PEAKING title and 'The trip of a lifetime' tagline
v1 splash: a Peaking movie poster I generated for the launch (later swapped for the official Peaking PSD)
28
days from ask to live
125
events synced
24
map pins
44
artist entries

A splash screen and 5 tabs. Built for a phone in your hand at 2am in a field.

Designed mobile-first, offline-first, festival-first. Every tab survives losing signal. The schedule keeps working. The map keeps working. The favorites you set on Wednesday still buzz your wrist 15 minutes before the set on Saturday.

App splash: Peaking poster with full lineup, "tap to enter" prompt Splash screen
Schedule tab: now playing Truth's Playhouse, up next gEm wrangler, six-stage overview Schedule
Map tab: satellite imagery of Valkyrie Ranch, 24 GPS pins for stages and venues Map
Film tab: Peaking film context, 73 scenes, 40 cast, 20 locations, daily film schedule Film
Info tab: tiered artist lineup, "the trip of a lifetime" tagline, Experiences tags, stage gallery Info
Alerts tab: real safety alerts, schedule changes, end-of-event messaging Alerts
Schedule
125 events across 6 stages with day filters. NowPlaying card. "Now at each stage" overview so you can see all six stages at a glance. Favorite a set and you get a vibration plus toast 15 minutes before doors. Schedule conflict detection on overlapping favorites. Festival-aware sort: 2am reads as continuation of evening, not next morning.
Map
24 GPS-pinned locations on satellite imagery. Georeference recalibrated mid-festival via 10-point least-squares against production GPS coords (max residual reduced from ~155m to ~48m). Layer toggle for satellite or illustrated map. Walking-time between stages via Haversine at festival pace (~60m/min). Google Maps nav handoff per pin. Tile fallback for when you wander out of signal. Reused the map foundation I'd already built for the Nameless Cemetery project, which made this a half-day swap-in instead of a from-scratch build.
Film
"You're not just attending a festival, you're in the movie." Day-by-day film schedule, 73-scene reference, 20+ filming locations, attendee guidance. Brent's voice for context, my CMS for delivery.
Info
Tiered lineup (headliner / support / full alphabetical), Experiences tags, stage render gallery, art car photos (Abraxas, El Pulpo), venue directory with collapsible categories, quick links to Kickstarter / Instagram / Venue. Lock-screen lineup export: rendered PNG, Web Share API.
Alerts
Real-time announcements pushed via the sheet. Unread badge on the bottom nav. Used in production for safety ("If you get stuck in the mud, don't spin your tires"), schedule changes ("CØNTRA Mainstage NOW"), and end-of-event messaging.

Sheet → Worker → KV → app. Edit a cell, see it in 60 seconds.

The whole thing runs on a Google Sheet. Six tabs, six endpoints, one Cloudflare Worker that polls them on a cron and caches the JSON in KV. The app reads from the Worker, not the Sheet. The Sheet is the CMS. Non-technical operators (Jesse, Eric, Areen) edited live during the festival without a deploy.

Frontend
  • React 19.2 + Vite 7.3 + TypeScript 5.9
  • Tailwind 4.1 for styling, Zustand 5 for state, Dexie 4 (IndexedDB) for offline persistence
  • Leaflet for the map, vite-plugin-pwa for the service worker + manifest
  • Unbounded (display) + DM Sans (body), pulled from the official Peaking PSD
Backend / sync layer
  • Cloudflare Worker with KV cache, polling Google Sheets API on a 1-minute cron during festival (5-min pre-fest)
  • Six tabs in the CMS sheet (Alerts, Artists, Set Times, Film, Scenes, Info) feeding the five app tabs
  • Custom wide-grid parser for the 8-column workshop schedule (2 header rows + 4 day-pairs)
  • fetchSheetCSVByName resolver: fetch by tab name, not gid
Hosting / deploy
  • Cloudflare Pages at app.didyoupeakyet.com via CNAME from the festival's Wix DNS
  • Netlify mirror at travisfixes.com/peak-state-aa57b5cf/ as a fallback
  • PWA installable on iOS and Android, custom icons extracted from the official logo, splash screen rendered from the Peaking poster
Operator experience
  • Dropdown menus on every input: stage, day, type, alert type, active, featured, secret
  • Color-coded tabs, frozen headers, header row protection
  • README tab in plain English so a non-technical operator can edit on their first day
  • Cell notes on every column header explaining intent

What this actually cost to build.

28-day calendar window from ask to live; 10 of those days were active build days; ~30 hours of focused work across them. Claude is a hard number from my own attribution tracker (claude-fuel). Gemini, ChatGPT, and Grok ran lighter as research and sanity-check sub-agents; their API spend is estimated from observed volumes at published per-token rates.

~30
Travis hours
~$825
Total API spend
383M
Tokens processed
$5
Cloudflare (Workers)

// API spend breakdown: Claude $760 (hard) · Gemini ~$30 · ChatGPT ~$20 · Grok ~$15

Build timeline by lane

Mar 19 to Apr 19. Each lane is one agent (or me). Block opacity tracks intensity that day: brighter blocks are heavier work. Hover for detail.

// LANE
Mar 19 Mar 26 Apr 2 Apr 9 Apr 16
total
Travis
orchestrator / human
356
prompts
Claude
primary builder
$760
API spend
Gemini
research / sub-agent
~$30
est. API
ChatGPT
research / sub-agent
~$20
est. API
Grok
sanity check / sub-agent
~$15
est. API
Cloudflare
deploys / Worker / KV
$5
Workers
// 31 days · Mar 19 → Apr 19 · block opacity = activity intensity that day

Reactions from the people running it.

fucking rad. super dope. stellar. a wonderful gift.
Brent Pella // Director, Door 11 Studios · voice memo via Jesse, Mar 24
Dude. Legend. Unreal.
Jesse Brede // CEO, Gravitas Recordings · Apr 3, after the on-site testing push
Looking pretty awesome.
Eric Sumner // festival tech · Apr 3

Declined the contract. Kept building. Kept the IP.

Three weeks in, a Volunteer Deal Memorandum and Personal Release arrived from Synchronica LLC, the production company behind Peaking. Section 5.0 Work-for-Hire language would have transferred the app, the Worker, the CMS, and every piece of infrastructure I'd built, in perpetuity, all media, with no obligation to credit me.

My counsel flagged 15 amendments across roughly 7 sections. I offered redlines to their lawyer. The response: "She won't accept any redlines. Maybe just come as my guest." I said yes. Medical complications kept me from attending in the end.

The app stayed on my infrastructure. The IP stayed mine. The portfolio stays mine. The relationship stays intact.

Full timeline. Every milestone, every hotfix.

Reconstructed from the production thread, the Claude attribution tracker, and iMessage with the operations team. Build days color-coded by day-of-week. Times in CT.

Thu Mar 19 Day 1 // the ask
~9:47 AM
Jesse: "What do you think it would take to make a SUPER simple iPhone/Android app for Peak State festival?"
morning
PWA research and analysis kicked off. Multiple agent rounds of comparative analysis: Native (iOS + Android) vs Hybrid (Capacitor) vs Commercial vendors (Aloompa, Greencopper) vs PWA vs mobile site. Cell-tower load research at festival venues. Offline-first patterns. Comparison matrix built. Tech stack picked. Phased timeline drafted.
midday
Briefing document written: 10-section research summary distilled across multiple agents. Why PWA wins on this timeline + this venue (offline-first, no App Store, instant updates, ~$0 hosting). MVP feature spec. Phase 2 backlog. What the organizers would need to provide (schedule, map, emergency contacts, branding).
~1:30 PM
NotebookLM presentation generated from the briefing: 10 slides + narrative video. Engineering Peak State: The Offline-First Festival App -- a peer-to-peer technical conversation, not a sales pitch. Render delivered as MP4.
~2:42 PM
Proposal package delivered at travisbreaks.org/share/peak-state/: the briefing, the NotebookLM presentation, the explainer video, scope, deliverables, $4,500 quote, 3-week timeline.
~2:53 PM
Quote back: "Wow. You are a gangster. How many Doll Hairs?" Festival has no budget available. Decision: build it anyway as a Murmuration pressure test.
evening
v0 scaffold begins. React 19.2 + Vite 7.3 + TypeScript 5.9 + Tailwind 4.1 + Dexie 4 + Zustand 5 + Leaflet + vite-plugin-pwa. Service worker scaffolded.
late night
v1 prototype shipped to share page.
day total
53 prompts · ~100M tokens · $217 in Claude across research, briefing, NLM presentation, scaffold, and v1. Biggest spend day of the build -- the analysis took real compute, not just the code.
Sat Mar 21 v1 polish
~4:09 PM
v1 share refresh with the polished prototype. Quote: "Dude. Absolute legend."
~6:41 PM
Film tab wired to the shooting schedule (PDF parse). Per-day filming guidance for attendees.
Sun Mar 22 brand work
all day
Pulled the official Peaking PSD (197MB, Danielle Capra). Palette extraction from poster pixels. Logo extracted from PSD layers. Typography pulled (Unbounded display, DM Sans body). 17 prompts, $45 in Claude.
Mon Mar 23 v2 ships
~2:03 PM
v2 deployed with full official branding: poster-derived palette (Void #0a101c, sunset, neon, teal, gold, fire), extracted logo, 27 announced artists, tiered lineup (headliner / support / full), secret headliner card, pre-fest Day 0, the official Peaking poster as splash, Experiences section. 28 prompts, $61 in Claude.
Tue Mar 24 Brent feedback
~7:49 AM
Brent voice memo arrives via Jesse: "fucking rad / super dope / stellar / a wonderful gift." Ask: bigger fonts, warmer mono, brand-voice copy editing via CMS.
~12:42 PM
Typography update. Base font 14→16px. Body type 10→12px / 11→13px across all components. JetBrains Mono pulled from prose (kept for data/timestamps only). DM Sans everywhere.
Wed Mar 25 CMS architecture // peak day
all day
Biggest build day of the project. 73 prompts, $260 in Claude. CMS sheet structure designed: 6 tabs (Alerts, Artists, Set Times, Film, Scenes, Info). 44 final artist entries researched and populated (30 with bios, 32 with links). Worker architecture sketched.
Thu Mar 27 Worker + custom domain live
all day
Cloudflare Worker + KV cache deployed. 5-min cron polling Google Sheets API. Six endpoints serving the five app tabs. fetchSheetCSV resolver, JSON cache, freshness headers.
afternoon
Custom domain live at app.didyoupeakyet.com via CNAME from the festival's Wix DNS. SSL active.
evening
Operator UX shipped. Sheet dropdowns (stage / day / type / alert / featured / secret), color-coded tabs, frozen headers, header row protection, README tab in plain English, cell notes on every column header.
~8:55 PM
Sheet shared with Jesse. Quote: "I will definitely try to sell this for you to other festivals."
Fri Apr 3 on-site testing push
all day
Eric Sumner joins for on-site testing. 8-feature push in one deploy:
· 12hr AM/PM display + festival-aware sort (late-night reads as continuation of evening, not next morning)
· Stage-off banners (per-filter) + StageOverview "now at each stage"
· Event reminders w/ vibration 15 min before favorited sets
· Schedule conflict detection on overlapping favorites
· Walking time between stages via Haversine (~60m/min)
· Lock-screen lineup export (canvas render + Web Share API)
· Offline map fallback with tile-error detection
· Type column on Set Times, Stage Off rows auto-filtered to /api/stage-status
~6:44 PM
Eric: "Looking pretty awesome." 52 prompts, $113 in Claude.
Wed Apr 8 contract decision
midday
Synchronica Volunteer Deal Memo + Personal Release arrives via attorney in Culver City. Counsel flags 15 amendments across 7 sections. Section 5.0 Work-for-Hire would have transferred all IP in perpetuity.
afternoon
Decline sent. Offer to send redlines. Response: "She won't accept any redlines. Maybe just come as my guest." Accepted guest status. App stays on my infra. No code or infrastructure was touched in this round.
Tue Apr 14 pre-festival prep
all day
Final data audit + small touches. 63 messages, $23 in Claude. Categories verified: Schedule, Map, Film, Info, Alerts. Workshops not caught in this audit (the gap that surfaced on Day 4).
Wed Apr 15 Day 0 // pre-fest
all day
Film shoot begins. App ready. Worker stable. No deploys.
Thu Apr 16 Day 1 // gates open
~3:20 PM
Ash flags Thursday times wrong in the app. Areen (sheet maintainer) gets the ping. Jesse to the prod-leads group: "Areen, he knows what to do. Be patient. This was a gift app to us."
afternoon
Refresh button removed. Manual refresh was silently no-op'ing during the 5-min cooldown and confusing users. With the cron about to drop to 1 minute, it was redundant. Sync indicator simplified to last-sync time + favorites filter.
evening
Cron dropped 5 min → 1 min. Tighter sync window for live schedule edits. Worker config push, KV cache refreshed.
Fri Apr 17 Day 2 // overnight hotfix run
~12:36 AM
Ash: "We need things taken off the map. There are things labeled there that are not things for people to see but are hot sets." Production map had film-set locations leaking into the public app.
~1:24 AM
Parser bug spotted. Sheet structure had changed mid-festival; parser was getting confused. Patched same hour.
~2:00 AM
Map georeference correction. Original illustrated-map bounds were off by up to ~155m. Recalibrated via 10-point least-squares using production GPS coords. New bounds: SW [30.1664592, -97.1017564] / NE [30.1725300, -97.0924121]. Max residual reduced to ~48m.
~2:00 AM
Off-map venue cleanup. Three film-set venues removed (Aurora's Tent, Yoga Tent, RV Village). Five missing public venues interpolated and added: El Pulpo, Wellness Village, Foam Tub, VIP Lounge, Shuttle Pickup.
~2:30 AM
IndexedDB seed bump. SEED_VERSION 12 → 13 to flush stale offline data on existing installs. Fresh installs got the corrected coordinates immediately.
Sat Apr 18 Day 3 // steady-state, no fires
all day
No incidents. Worker cron stable, sheet edits propagating in under 90 seconds end-to-end. App holding up under peak attendance.
Sun Apr 19 Day 4 // last day
~6:33 PM
Jesse: "What sheet drives the workshop?" The workshop schedule had been seeded from a static JPG earlier in the build and never wired to a live source. The other five categories were live; this one was hardcoded.
~9:00 PM
Workshop schedule wired to live sheet. Custom 8-column wide-grid parser shipped (parseWorkshopSchedule). fetchSheetCSVByName resolver added (fetch by tab name, not gid). 36 workshop events now live from the "Workshop Set Times" tab. Sanity guard adjusted (60 → 26 minimum events).
~11:47 PM
Final sync confirmed. 125 events live from sheet across all six stages. App holds through end-of-event messaging.
Peak State official illustrated festival map: stages, venues, art cars, food, vendors, parking, the loch ness monster gag in the lake, the UFO over the parking lot
The official illustrated festival map (with the Loch Ness gag and the UFO over the parking lot).

A few things I'm taking into the next app build (or similar project). None of these are the kind of bug you find in a stack trace; they're process gaps that only show up when the lights are flashing.

Pre-launch data-source audit
For every category the app shows (schedule, workshops, artists, alerts, map, info), tag the source as live, seed, or static. Walk every tab with the client. Anything not live before doors open gets a visible badge in the app. No silent staleness.
Pre-festival client walkthrough
Schedule a 20-minute screen-share at least 7 days before doors. Click every tab in front of the client and ask "is this right?" The on-site testing session on Apr 3 caught some bugs. The same exercise with Jesse and Ash would have caught more.
Don't bootstrap from static files
Anything seeded from a JPG or PDF gets a tag in the codebase: // SEED-FROM-STATIC: replace before launch. Pre-launch checklist greps for those tags. None ship to production.
Ship a one-page data map
Plain English, given to the client. "Schedule comes from this tab. Workshops come from this tab." The document the client should be able to pull up to answer their own data questions.
Statement of Work even in gifting mode
No-payment engagements still need scope, deliverables, and walk-away in writing. Signed or not, written down. Protects everyone, keeps us all on the same page.
Hold the line on tooling
Late in the build, the client pivoted away from the CMS we'd designed and insisted on driving everything from a different sheet. I rewired against their sheet to keep them moving. The trade-off, made explicit upfront and decided together, would have saved both sides the rewire and the limitations that followed.
Instrument analytics from day one
No install-event listener, no Web Analytics tag, no Logpush. The app shipped without any way to count installs, page views, or unique devices. Cloudflare's free-tier analytics aggregates server-side without preserving client identifiers, so the data can't be reconstructed after the fact. PostHog, Plausible, or Cloudflare Web Analytics is ~10 minutes on day one. Without it, "how many people downloaded it" has no answer.
Solo full-stack festival deployment
App, CMS, Worker, KV, PWA, offline, brand, hotfix ops. One person, 28 days.
Sheet-as-CMS for non-technical operators
Edit a cell, see it in the app in 60 seconds. Three teams editing production data without a code deploy.
Brand alignment from the source
Palette and typography pulled directly from the official PSD. The app felt like part of the festival, not a bolt-on.
Operating under live festival pressure
Hotfixes shipped remotely while the festival was running. Parser bug at 1am, fixed by 2am.
Knowing when to walk through a contract slowly
Declined a work-for-hire that would have transferred all IP. Kept building anyway. Kept the relationship and the portfolio piece.
app.didyoupeakyet.com The live app, still up didyoupeakyet.com Peak State festival site GigCasters case study Tier-one broadcast TD, 2020 to 2022 Highland Sound case study Studio build, podcast TD, documentary AD Livid Instruments case study Acting CEO, Minim Kickstarter, manufacturing pivot More work at travisfixes.com Other case studies