Changelog & Story

The honest story behind the builds, the breaks, and the late nights.

~2025-07-07

How it started: the idea

This didn't start as a plan. It started as one of those late-night rabbit holes — the kind where you're supposed to go to sleep, but instead you keep asking slightly better questions.

We went down a rabbit hole together through a ridiculous number of ideas. Some were borderline genius, most were absolute garbage: AI copilots for everyday decisions. A "life debugger." A tool to simulate alternate career paths. Even a half-serious attempt at building something like a personal "Jarvis" running locally. At one point we were sketching out a CMS to dethrone overpriced enterprise tools. Another time we were convinced the real opportunity was in adult-adjacent AI tools. It was chaos — but useful chaos.

One combination stuck: face recognition, and a fun conversation with a friend about how everyone has a pornstar twin.

2025-07-07

Technical research: figuring out what was even possible

Mapped the core idea against what current models and frameworks could actually do. Built a few quick prototypes to test whether face recognition could run in the browser — no server, no upload, nothing.

The answer was yes. And that was more than enough to know something had to be built with it.

2025-07-09

Day one: first commit

A Nuxt scaffold, a blank page, and a question nobody had cleanly answered yet. Could a browser — just a browser — figure out who someone looks like?

  • Nuxt 3 project initialized
  • Basic page structure: index and scan views
  • File selection UI wired up with PrimeVue
2025-07-10 – 2025-07-11

Face matching in the browser

Shipped a fully client-side face recognition pipeline. A compact model, downloaded and run entirely in the user's browser — no server, no uploads. It was gloriously impractical and kind of magical.

  • Loaded a face embedding model directly in the browser via ONNX Runtime Web
  • Implemented L2 normalization and cosine similarity in JavaScript
  • Built the comparison pipeline: face → embedding → nearest neighbours
  • Wired up a Node backend for the gallery side of matching
2025-08-21 – 2025-08-22

Data pipeline: the real battle

The data fetcher ran for hours. Rate limits hit. Retries piled up. Some nights the machine just sat there, churning through performer profiles one by one, hoping the source wouldn't cut the connection. By the end there were thousands of images, a gallery of face embeddings — and a very tired laptop.

The whole performer index lives in memory at runtime. No database, no disk queries mid-request. Just a matrix of vectors and a lookup dict, loaded once at startup. Clean, fast, and honestly kind of satisfying.

  • Built a scraper to fetch performer data and images from public sources
  • Handled rate limiting with backoff and retry logic
  • Preprocessed images without distortion before embedding
  • Normalized gallery embeddings into a flat matrix for fast in-memory lookup
  • Consent-first approach: only public performer profiles from opt-in databases
2025-08-25

The domain

Doppelbänger.fun registered on GoDaddy. The umlaut in the name was a deliberate choice — a typographic nod to the German origin of the word, and an excuse to make the ä the most interesting character on the page.

  • doppelbanger.fun registered
2025-08-28 – 2025-08-29

Moving the brain to Python, new design

The browser model worked. The proof of concept was done and genuinely impressive. But it was technically wrong for a product — who downloads a whole AI model to their phone for a five-minute laugh? My powerhouse of a phone was running hot. Moved the matching logic server-side. Suddenly things got fast.

  • Replaced browser ONNX pipeline with a FastAPI Python backend
  • Removed 200MB+ of ONNX runtime files from the client bundle
  • Rebuilt the matching flow: upload → server embedding → cosine similarity → results
  • Complete UI redesign with Tailwind — dark, moody, on-brand
  • Space Grotesk + Inter font pairing
  • Consent flow and local storage persistence added
2025-09-07

A server to call its own

A DigitalOcean droplet spun up. $20/month. The whole stack — Caddy, FastAPI, Nuxt — running on a single box. Turns out the in-memory design scales surprisingly well: a hundred times the current traffic would still fit on one tier higher.

  • DigitalOcean droplet provisioned
  • Docker Compose setup with Caddy reverse proxy
  • Gallery data deployed via Git LFS
2025-09-07 – 2025-09-08

Launched

First real deployment. Gender filtering, performer cards, analytics. The thing was live.

  • Gender detection via lift scoring over top-k results
  • GenderSelector component for filtering results
  • PerformerCard component with similarity bar and profile link
  • useTrackEvent composable abstracting analytics calls
  • Umami analytics integrated for event tracking
  • TheHeader and TheFooter components extracted
2025-09-14

SEO foundation

First pass at making the site discoverable. Title tags, canonical links, robots.txt, sitemap, copy rewrite. The kind of work that feels invisible but matters.

  • Title, description, lang, and canonical meta tags aligned
  • robots.txt and sitemap.xml added
  • Homepage copy rewritten for keyword relevance
  • Footer with email address added
  • www redirect configured
2025-12-20 – 2025-12-22

Infrastructure fixes and match tracking

Quiet but important work. Storing match results, fixing the dev proxy, making the deployment more stable.

  • Match images and result data stored server-side after each search
  • Docker volume fixed to persist output files across restarts
  • Dev proxy corrected
  • Sitemap and lang property fixes
  • Prop typing cleaned up across components
2026-01-08

Better event tracking

The analytics were there but not telling the full story. Added more granular events to understand what was actually happening per session.

  • matching_started event added
  • matching_failed event with error message payload
  • Confidence threshold event for high-quality matches
2026-02-28

Umami caused a DDoS. Ripped it out.

The self-hosted analytics instance started hammering the server — CPU spiked, the site went down. Killed tracking entirely while a replacement was found.

  • Umami removed from all client pages
  • useTrackEvent stubbed to no-ops to preserve call sites
  • Docker Compose cleaned up
2026-03-02

Google Analytics takes over

Switched to GA4. Proper event schema, no self-hosted liability, and gtag available everywhere.

  • GA4 integrated
  • IP anonymization enabled
  • open_performer_profile event with performer name, score, gender, and position
  • GA disabled in dev environment
2026-03-18

Cross-link to Twinify

Added a referral link to a sister tool for celebrity lookalikes — for users who are more curious than thirsty.

  • Twinify.co cross-link added to results panel
  • UTM parameters set for referral tracking
2026-03-21

Major refactor: smarter matching, hardened backend, cleaner code

The biggest single commit since launch. Z-score normalization replaced raw cosine similarity — genuinely similar faces now stand out instead of everything clustering in a narrow band. The backend got hardened. The frontend got split into components.

  • Z-score normalization applied to similarity scores — better signal, fewer mediocre matches
  • File type and size validation on uploads (25 MB limit, HEIC support added)
  • HTTP 400/413 errors returned properly instead of silent 200s
  • O(1) actor metadata lookup replacing a per-result linear scan
  • Logger aligned to uvicorn for consistent Docker output
  • Path traversal guard added to image serving endpoint
  • app.vue split into UploadPanel and ConsentModal components
  • Backend error messages surfaced to the user with a fun tone
2026-03-22 – 2026-03-23

Analytics, identity, and polish

GA4 funnel set up to track the only metric that matters: did someone find a match interesting enough to click? New logo, favicons, PWA manifest. The GenderSelector got rebuilt as a proper button group.

  • GA4 Funnel Exploration: matching_started → open_performer_profile
  • great_match event removed — profile clicks are the real signal
  • Twinify link aligned visually to result cards, click tracked
  • Custom SVG logo and favicon — the two dots are exactly what you think they are
  • PNG favicons generated at all standard sizes
  • PWA manifest — the site can now be installed to a home screen
  • GenderSelector rebuilt as a segmented button group with live counts
  • Gender order dynamic — the majority gender shown first
2026-03-24

Privacy and Terms pages

Added a privacy policy and terms of service to clarify what data is collected, how it is used, and what rights users have.

2026-04-03

Monetization try #1: Card reveal => ❌

In order to show cards later, I created a blurry effect, and a click-to-reveal interaction. The idea was to make the cards more interactive, and to give users a reason to click through to the performer profile. It got reverted, since it makes no sense to hide something that is free. And also, Claude convinced me that the way to monetize the site is starting with affiliate. Less complexity, work, etc, so it got dropped.

2026-06-04

Celebrity lookalikes

I wanted to show of how it works. So first I wanted to do a demo page, that would show a lokkalike of a random person. But then I needed a random person. Then I thought, why should it be random. It should be famous instead. So I went looking for pictures. And some forums to see which obvous matches my algo missed. The end output is hand curated in a way that I do not showcase false positives, and I added some handpicked matches, but I indicate those.

  • Celebrities page with celebrity selector
  • Celebrity pages with matches
2026-06-18

Monetization try #2: Studio affiliate deeplinks => ❌

So again, it was cooking for a while, and the GA numbers were getting better. Especially after the celebrity pages. I decided, that it is time to try making some money out of it.

  • Registered for most adult affiliate networks, and was shocked, that is was impossible to deeplink to performer, or even search pages for the big ones. Also, there was only a selection of the performers on each of the big ones.
  • Of course: meanwhile, I prematurely did the work, waiting for some of the affiliate managers responses.
  • I could not believe how hard, or even impossible is to get a trackable deeplink for a site like barzzers. But turns out, the affiliate companies are not the studios themselves, so they have their own landing, that registers the clicks. Also, surprisingly, studios do not keep that good public record of their older works. So the catalogue was not good enough for my (users) needs, who can try to search for some pretty niche names in the industry.
  • So I needed something else. Back to the drawing board!
2026-06-18

Monetization try #3: AdultEmpire affiliate

I found it! It allows deeplinking, has VOD, and a wide catalogue. My only concern is, that their UI is not as fresh, or mobile-first as mine.

  • Got in the program, directed, listed, got my <id>, of course the code was ready before.
  • The links are working.
  • It only shows full days.
  • I see the visitor numbers I send towards them ~750 a day, this should be good...
  • As I'm writing this, conversion is at 0.03%, 1 sale out of 3250 visitors that I sent. Abysmal!
  • Getting something to drink, rethinking what I did with my last year.
  • Putting that 1 dollar into a physical bank note. I'll keep it, like Scruuge McDuck. Can be the first of (not so) many!
2026-07-01

ThePornDude link exchange, misc

Adding his link on the header, nothing to lose with it. I want to help with DA, so SEO, lets see...

  • Mobile version of the menu fixed up.
  • Removing the link to twinify, it is noise traffic there.
  • Adding his link, I am inclined to believe, he will showcase mine on his page. Can't wait to see, what he writes about it. And of course whether I get some traffic.
2026-07-12

Monetization #4

0.02% conversion. A farce, really. I am also not happy with their speed, so decided to do an AB testing with AEBN

  • NPM packages updated
  • Improved server side logging
  • Affiliate links: keeping 20% on AEC, 40% on AEBN, 40% on AEBNs Porntube, let see the numbers...
2026-07-15

Monetization #5 - fake paywall + new landing for pornstar reverse image search

AEBN > AE but not with an order of magnitude. I don't know what I was thinking. Creating the fake door, to test willingness to pay for names. Otherwise it'll be ads, no matter how hard I don't want that.

  • I can test the paywall on ~15% of users, the rest remain ungated with the affiliate (20-40-40), lets see the numbers...
  • I noticed, that there's room to grow for keywords like "pornstar finder" and "pornstar by face", so created a new landing page specially for those. Again, let's see the numbers...