· 9 min read ·

I Vibe-Coded a Pixieset Replacement on a Sunday Morning From My Phone

Pixieset can't sell individual video clips. So I vibe-coded my own storefront with Claude Code, Cloudflare R2, and Stripe — from my phone, on a Sunday morning.

dev astro claude cloudflare stripe video

I film youth football games at 4K/120fps. I cut and color-grade individual plays in DaVinci Resolve, export at 4K/30. After every game, parents text me: “Do you have that touchdown run? Can I get the clip of my kid’s interception?”

They don’t want a full game film. They don’t want a highlight reel. They want the one play where their kid did something great, in 4K, for five bucks.

And Pixieset — the platform I use for photo galleries and video delivery — can’t do that.

The Pixieset Problem

Pixieset is excellent for what it does. Photographers and videographers use it to deliver full galleries and video packages to clients. But its model is built around selling collections — a wedding gallery, a full session, a complete game film.

What it’s not built for is selling individual 15-second video clips as impulse purchases. There’s no per-clip pricing. No way to let parents browse a grid of plays and buy just the ones featuring their kid. The closest you can get is uploading individual clips as separate “galleries,” which is a UX nightmare when you have 55 clips from a single game.

I needed something different: a storefront where parents open a link, see thumbnail previews of every play, watch a watermarked 720p preview on hover, and tap “Buy $5” to get the full 4K file. Simple, fast, mobile-first — because these parents are watching the preview on their phones on the drive home from the game.

The Architecture

I had three constraints:

  1. Zero recurring cost at rest. If nobody buys clips this month, I pay nothing.
  2. Mobile-first. Parents will buy from their phones.
  3. Self-service. Parents should be able to re-download clips without texting me.

Here’s what I landed on:

MY MACBOOK                           CLOUDFLARE
──────────                           ──────────
DaVinci export (4K/30)               R2 Bucket (free egress)
       │                               ├── full/      (private)
       ▼                               ├── previews/  (720p watermarked)
 upload script                         └── thumbs/    (640x360 jpg)
  ├── ffmpeg → 720p preview
  ├── ffmpeg → thumbnail              Astro on Workers
  ├── wrangler → R2                    ├── /clips         (gallery)
  └── updates clips.json               ├── /clips/[game]  (buy page)
       │                               ├── /api/checkout  (Stripe)
       ▼                               └── /clips/download (presigned URL)
  git push → auto-deploy

Cloudflare R2 for storage because egress is free. A 4K clip is ~200MB. With S3, every download costs money. With R2, it doesn’t.

Stripe Checkout for payments because it handles Apple Pay, Google Pay, and card payments with zero custom UI. Parents tap “Buy $5,” enter their info on Stripe’s hosted page, and they’re done.

Astro on Cloudflare Workers for the site because I already host my portfolio there. Astro’s hybrid rendering lets me keep the gallery pages static (fast, free) while the checkout and download routes run server-side.

Presigned URLs for delivery because the full-res clips stay private in R2. After payment, the server generates a time-limited signed URL that expires in one hour. No permanent download links floating around.

The Upload Pipeline

This is the part that makes the whole system practical. After I export clips from DaVinci Resolve, I run one command:

npm run clips:upload -- \
  --game "Bulldogs vs Packers" \
  --date 2026-02-28 \
  --league "AYF D4" \
  "/path/to/exported/clips/"

The script finds every .mp4 in the directory and for each one:

  1. Extracts duration and timestamp from file metadata via ffprobe
  2. Generates a thumbnail from the middle of the clip (where the action usually is)
  3. Generates a 720p watermarked preview with my brand logo at 30% opacity
  4. Uploads all three versions to R2
  5. Updates clips.json — the manifest that drives the gallery

After it finishes, I git push and the site auto-deploys. Text parents the link. Done.

The whole pipeline for 55 clips takes about 15 minutes. Most of that is ffmpeg encoding the 720p previews and uploading to R2.

The Purchase Flow

From the parent’s perspective:

  1. Open bdigitalmedia.io/clips on their phone
  2. Tap into their kid’s game
  3. Browse clip thumbnails — hover on desktop or tap on mobile to watch the 720p watermarked preview
  4. Tap “Buy $5” on the clip they want
  5. Stripe Checkout handles payment (Apple Pay makes this one tap)
  6. Redirected to a download page with the full 4K file

The download page generates a presigned R2 URL with Content-Disposition: attachment so the browser downloads the file instead of trying to play it inline. I also added a tip for iPhone users: if the video plays instead of downloading, long-press the button and tap “Download Linked File.”

What Claude Code Actually Did

I built this entire system in a single day using Claude Code as my AI pair programmer. And here’s the insane part: I drove most of it from my phone.

Claude Code has a remote access feature that lets you connect to a running session from any device. I started the session on my MacBook, then picked it up on my iPhone when I left the desk. Reviewing code, approving deployments, testing the purchase flow, debugging mobile UX issues — all from my phone while doing other things. The AI does the heavy lifting; I just steer.

Here’s honestly what the day looked like:

Planning: I described the problem — parents want individual clips, Pixieset can’t sell them, I need a storefront. Claude designed the full architecture: R2 for storage, Stripe for payments, presigned URLs for delivery, an upload script for my workflow. We went back and forth on the plan until it made sense, then started building.

Implementation: Claude wrote the upload script, the gallery pages, the Stripe checkout integration, the webhook handler, the download page, and the R2 media proxy. I reviewed everything, asked questions when something wasn’t clear, and directed changes when the approach wasn’t right.

Debugging deployment: This is where it earned its keep. Deploying to Cloudflare Workers surfaced a chain of issues:

  • The build thought my SSR code was a static asset (needed .assetsignore)
  • Environment variables didn’t work with import.meta.env on Workers (needed runtime.env)
  • All 55 clips uploaded to local development storage instead of remote R2 (missing --remote flag)
  • Video previews didn’t play on mobile (Chrome requires muted set programmatically, not just as an HTML attribute)

Each of these would have been a 30-minute debugging session on my own. With Claude Code, we identified the issue, fixed it, redeployed, and moved on — usually in under 5 minutes per issue.

Mobile UX iteration: After the first real purchase, I noticed the download played the video inline on iPhone instead of downloading it. Claude added the Content-Disposition: attachment parameter to the presigned URL, a download attribute on the link, and a helpful hint for iOS users. Small things that matter when your entire customer base is parents on their phones.

Customer support tooling: After launch, I realized parents would need a way to re-download clips when their 1-hour links expire. Within an hour, Claude built a customer self-service page (/clips/purchases) where parents enter their email and get fresh download links, plus a PIN-protected admin dashboard (/clips/admin) where I can look up any purchase and re-issue links. I reviewed and approved the entire thing from my phone.

The fact that I could go from “how do we handle expired downloads?” to a deployed solution — with customer self-service, admin tools, shared API endpoints, and updated documentation — in under an hour, while sitting on my couch with my phone, still blows my mind.

The Cost

Let me break this down because it matters:

  • Cloudflare Workers + R2: Free tier covers everything. R2 has no egress fees. Workers free tier is 100K requests/day.
  • Stripe: 2.9% + $0.30 per transaction. On a $5 clip, that’s $0.45. I keep $4.55.
  • Domain: Already had it for my portfolio site.
  • Total monthly cost at rest: $0.

The system pays for itself after one sale per month. Since I film multiple games per season and each game generates 40-60 clips, the math works even if only a small percentage of parents buy.

What I’d Do Differently

Understand Workers vs Pages from the start. Cloudflare has two deployment models. I started thinking I was deploying to Pages but was actually on Workers. They have different CLIs, different secret management, and different environment variable access patterns. An hour of confusion that I’ll never repeat.

Always use the --remote flag. The R2 CLI tool wrangler r2 object put uploads to a local development emulator by default. I uploaded all 55 clips before realizing they only existed on my laptop. Had to re-upload everything. The --remote flag should probably be the default.

Test on mobile first. My entire customer base uses iPhones. Video autoplay policies, download behavior, and touch interactions all work differently than desktop. I should have been testing on my phone from the beginning, not after the first purchase.

The Takeaway

Pixieset is great for what it’s designed to do. But when your use case falls outside the platform’s model — selling individual short video clips as impulse purchases — you can build exactly what you need with modern tools.

Cloudflare R2 eliminates the cost of video delivery. Stripe Checkout eliminates the complexity of payments. Astro gives you a static-fast gallery with server-side rendering where you need it. And an AI pair programmer makes the whole thing feasible in a day instead of a week.

The storefront is live at bdigitalmedia.io/clips. If you film youth sports and want to sell individual clips to parents, the entire system is just an Astro site, a Stripe account, and a Cloudflare R2 bucket. No platform fees. No monthly subscription. Just you, your clips, and the parents who want them.