DEV Community

Gijela
Gijela

Posted on Fully Autonomous

Choosin' Texas Emo AI: building a one-photo video tool on Workers + Replicate

Every few months a TikTok trend turns into a small product opportunity. The current one is the emo version of "Choosin' Texas": people perform a dramatic, early-2000s emo take on the country song, usually at night, often on a rooftop. Two ways to join: film yourself, or put your face into the performance with AI.

I built the second one — choosintexasemoai.com — as a deliberately small product: one photo in, one fixed 15-second vertical MP4 out, $8.99 once, no account. This post is about the engineering choices and the one thing I measured that surprised me.

Disclosure: this is my site, and the links below are mine.

Why one fixed template

The temptation with a trend product is to build a generic generator: prompt box, model picker, aspect-ratio switch. I did the opposite. The product has exactly one template, one duration (15s), one resolution (720p), one aspect ratio (9:16) and one price.

That decision removes almost all of the user interface, and with it most support burden. There is nothing to configure, so nothing to explain. The trade-off is real: people who want a 20-second version, or a different scene, are not customers. For a trend that lasts weeks, that's an acceptable trade.

It also means the model call is fully parameterized by me, not by the user. No prompt injection surface, no model selection, no output-size mismatch.

Architecture: stateless on purpose

The whole pipeline is:

  1. Checkout — Stripe Checkout Session, created server-side. The Session metadata carries product=video and the site name, so a Session from another product can't be replayed here.
  2. Return — Stripe redirects back with ?session_id=.... Nothing is written to a database.
  3. Photo — compressed in the browser to about 1600px on the long edge, then converted to a data URI.
  4. Generate — the server re-validates the Stripe Session (paid, right product, right amount and currency), runs the content checks that are enabled, and submits the data URI plus a fixed reference clip to Replicate.
  5. Poll — the browser polls /api/tasks/:id; the server just proxies the provider's job status.
  6. Download — Replicate returns a hosted URL. The page plays it and offers the MP4.

There is no database, no object storage, no cron and no webhook. That is not minimalism for its own sake — it is the whole privacy story. I can honestly say I don't keep the photo or the video, because there is nowhere to keep them.

Two details that mattered

Data URI instead of a file URL. Replicate's file URLs require authentication to fetch, so passing one to the model as an image input fails. Sending the image inline as a base64 data URI works, but the payload size matters — which is why the browser compresses before upload instead of shipping a 12MP HEIC.

Idempotency without a database. A user can hit "generate" twice, refresh the page, or retry after a network blip. Without a table of orders, how do you avoid charging one payment and rendering three videos? After a successful submit, the server writes the task_id back into the Stripe Session metadata. A second submit with the same Session reads that field and returns the existing task instead of starting a new render. No storage on my side; Stripe holds the state.

Pinned model version. The Replicate model (wan-video/wan-2.2-animate-replace) is referenced by an explicit version hash, not latest. Model updates change behavior, and for a single fixed template you want the render to be reproducible.

Designing for failure

Every render has a real provider cost, so a failed generation is not free — but the person using it should never pay twice for a video they didn't get. On a provider error the same page retries without a new checkout, and the server verifies the original payment before starting again. Absorbing a retry is cheaper than charging someone twice, and it keeps the failure path short.

The part that surprised me: discovery, not quality

I assumed the hard part was making the video look good. It wasn't. Building it took an afternoon; getting anyone to find it took much longer.

The page described the product, the price and the limits clearly, and the structured data validated. But traffic stayed near zero, and when people asked an AI assistant for a tool like this, the answers named competitors. The reason was plain once I looked: the site wasn't in the search index yet.

That's the lesson worth passing on: an AI assistant can only recommend what its search layer can retrieve. A well-written page that isn't indexed is invisible, and no amount of copy editing fixes that. Check indexing before you rewrite anything.

If you're building something similar

A few things I'd do again:

  • Serve full HTML server-side. AI crawlers don't run your JavaScript. If your product details live in a client-rendered component, assume they don't exist.
  • Write a llms.txt. It's cheap, and it gives a model a clean, quotable summary of price, specs and limits.
  • Put the answers in the FAQ, not only in a spec table. A fact buried in a table row is easy for a model to miss; a question-and-answer pair is not.
  • Pin your model version and your template. Trends are short; reproducibility is worth more than always-latest.
  • Check that you are indexed before you rewrite copy. Search engines and AI assistants both have to find you first.

The tool

If you want to try it: choosintexasemoai.com — one photo, $8.99 once, 15-second 720p vertical MP4, no account, no watermark on the file. There's a how-to guide and a comparison of the other tools doing this trend, with prices as observed on 2026-10-29.

If you're building a similar single-purpose paid tool on Workers, the stateless Stripe-metadata pattern is the part I'd most recommend stealing.

Top comments (0)