Vibe code your own Mixpanel

Vibe code your own Mixpanel as named events and one funnel. Growth is $140 a month at 1.5 million events. Shards, identity, and replay stay later.

  • teardown
  • vibe-coding
  • scope

You can vibe code your own Mixpanel as an ingest endpoint, one funnel, and a daily count. Mixpanel is that product. The catalog line is “Named events and a funnel you can read.”

The card lists $140 to $2,289 a month. Mixpanel’s pricing is the source for the note on the card. Growth at 1.5 million events is $140 a month, through 20 million events at $2,289 a month, billed monthly. The card leaves out the 1 million event free tier and Enterprise. The first estimate is 3 days. The verdict is “The funnel is three steps”.

Last time I wrote about building a ChartMogul alternative. That was a regular post. This one is a teardown.

The public sources are Vijay Jayaram’s Under the Hood of Mixpanel’s Infrastructure (April 9, 2024), Casey Chen’s ingestion and Cloud Spanner post (March 25, 2024), and Mixpanel’s identity management docs. Where those pages stop, I call it a guess.

Vibe code your own Mixpanel from three event names

The Mixpanel page splits the work into three rows.

The first row is 3 days. Event ingest, one funnel, and a daily trend.

The second row is 2 weeks and up. Event properties, saved reports, and a second funnel.

The third row is 3 months and up. Session replay, experiments, and warehouse sync.

If you tell an agent to “clone Mixpanel”, it aims at the third row. Copy the prompt, then pin it to the first one.

Store Project, Event, Funnel, and FunnelStep. The card names all four. An event is an allowed name, a user id the app already has, and a timestamp. A funnel is those names in order. A step count is how many user ids have that event and every earlier one, with timestamps in order, inside the range.

The audience is a product team with a handful of events on one app. The same v1 also stands in for Amplitude and Heap. The card lists both and does not give them prices.

Pageview counts are Plausible at $9 to $19 a month, in Vibe code your own Plausible. Invoice MRR is ChartMogul at $69 to $119 a month, in Build a ChartMogul alternative. This funnel is Opened the app, then Created a note, then Shared a note.

The guardrail is the rule you paste into the prompt. Accept only owner-defined event names. Drop payloads over a size cap. Never store cookies or fingerprints to identify a visitor.

How Mixpanel follows one person through a funnel

Nia ships Fieldnote, a notes app, and looks at November 6, 2026. The allowed names are Opened the app, Created a note, and Shared a note.

Ada’s id is ada. She opened the app at 09:01, created a note at 09:04, created another at 15:00, and shared one at 09:10. Ben opened at 09:02 and created at 09:20. Cam opened at 11:00 and did nothing else. A “Clicked billing” event from Ada is dropped, because that name is not on the list.

The funnel for November 6 reads 3, then 2, then 1. Three people opened. Ada and Ben created. Only Ada shared, and her share is after her create, so she counts at the last step. The daily trend for Created a note reads 3 events, because Ada created twice and the trend counts rows. The funnel still counts Ada once.

Jayaram’s post says each event has a name, a timestamp, a unique identifier, a distinct_id, and a JSON blob of properties. The example is a “Signed up” event. Events are immutable. Arb, their database, shards by distinct_id and sorts by time, so funnels, flows, and retention run without shuffling events or joining a fact table to itself. The engine he describes is columnar, with dictionary encoding, an optimizer, and C and C++.

The identity docs say two events with the same distinct_id are one user, and two different values are two users. In a funnel, inconsistent ids between steps count as a drop-off. Their walkthrough is a website id 123, an identify call with the login charlie, then a phone id ABC. After the second identify, all three sit in one cluster.

Chen’s post says the pipeline takes more than 1 trillion events a month. On the way in, Mixpanel turns the event’s distinct id into a canonical id so that every event for that id routes to the same cluster. Customers send the mapping through the Identify APIs. The mappings live in Cloud Spanner. Each event calls an internal LookupAndUpdate, which returns the mapping and also stores the oldest event date for that id, so later reads know how far back to look. After the work in that post, he writes that they were processing 2 million events a second.

That lookup is what the cluster is for. Ada on a phone and Ada on a laptop stay one user, so the funnel counts her once. Simplified ID Merge is the default for organizations created from April 2024. On that API, $user_id is always the distinct id, and there is no cap on how many device ids merge into it. The original API picks the distinct id for you, and it caps a cluster at 500 ids. Once a project has data, the docs say you cannot switch.

Fieldnote sends ada, the id it already has. A second id, ada-phone, would count as another person. That merge is Chen’s lookup, and it sits outside the 3-day row.

Jayaram also describes a lambda layout. Recent events sit in a row-oriented store so a query can see them within seconds of ingest. Older events move to time-partitioned columnar files. Properties stay schemaless JSON, and a schema is inferred so the menus can autocomplete. Profiles join at query time, which he calls a star schema, so a backfill can land later than the events. Duplicates are dropped at query time, including ones that arrive months later. The APIs are JSON over HTTP.

The same post says Warehouse Connectors load a warehouse into Mixpanel. It does not describe an export the other way. Session replay and experiments are on the card’s month row, and these sources do not describe them, so both stay a guess. The identity docs mention a hot shard, a case where an event may omit distinct_id, and they do not describe that split.

Screens of a Mixpanel alternative

v1 needs the stream and the funnel. Properties and a second saved funnel are the 2-week row.

The stream

Nia sees November 6 in order. Created a note appears twice for Ada. Clicked billing does not appear. Each row is a name, a user id, and a time. There is no property column.

WIREFRAME · STREAM November 6 · allowed names only 09:01 · ada · Opened the app 09:02 · ben · Opened the app 09:04 · ada · Created a note 09:10 · ada · Shared a note 09:20 · ben · Created a note 11:00 · cam · Opened the app 15:00 · ada · Created a note Clicked billing was dropped
Seven stored events. Ada created two notes. The unknown name never landed.

The funnel

The three steps are the allowed names in the order Nia saved. Counts are unique user ids. The trend line under the funnel is a different count, the raw Created a note rows.

WIREFRAME · FUNNEL November 6 · unique user ids Opened the app · 3 Created a note · 2 Shared a note · 1 Trend · Created a note · 3 events
Ada counts once in the funnel and twice in the event trend.

How a note gets counted

Fieldnote’s browser sends a name, a user id, and a timestamp to one Astro app. Node.js plus Astro is the default. Use TanStack Start if React is your weekday language. The capability line on the card is Astro, Workers, and D1. A Worker can take the public ingest. D1 stores the event rows. The funnel reads them back when Nia opens the report.

The server drops unknown names and drops JSON keys it was not asked to store. Properties are the 2-week row. The card says to drop an oversized payload, and it does not print a byte limit. Keep that check on the server, because the page can claim a small body.

The counts are queries over the stored rows. If the 15:00 create never landed, the trend reads 2 and Ada is still in the funnel because of 09:04. The id has to come from Fieldnote. There is no cookie. November 6 is the range, so a share on November 7 does not finish this funnel. A CSV of the same result can wait until the on-screen counts match this day.

FIELDNOTE SENDS ONE EVENT Name, user id, timestamp KEEP OR DROP Allowed name only. No cookie. STORE THE ROW D1 · one row per accepted event COUNT ON READ Funnel 3, 2, 1 · trend 3 events
Three days ends when November 6 shows those two counts from stored rows.
FIELDNOTE An id the app already has ASTRO APP Allow list, then the funnel query D1 Event rows for one project THEIR PIPELINE, THEN SKIP Identify, Spanner, canonical id Shard by user, row store, then columns Query-time dedup and schema-on-read Warehouse connectors, inbound Replay and experiments, a guess Hot-shard split, not described
D1 holds the rows Nia can count. The dashed box is Arb and the identity cluster.

The hard half of a Mixpanel alternative

Three days is an allow list, a row per event, a funnel of unique ids, and a daily count of one name. Nia opens November 6 and sees 3, 2, 1, plus three Created a note events.

The hard half is the lookup before the row exists, and the layout after it. A device id and a login become one canonical id, that id stays on one shard, and a retried signup still counts once months later.

Fieldnote’s seven rows fit in D1. A trillion events a month, and 2 million in one second, with a Spanner lookup on each one, is the pipeline Chen measured. Simplified ID Merge and the original 500-id cap are rules on top of that storage, and you pick one before the project has data.

Replay, experiments, and an outbound warehouse sync stay off these pages. The card’s non-goal leaves them, plus flags, for later.

3-DAY V1 Three names, an id you already have Ordered funnel, unique ids A daily count of one event No cookie, no fingerprint HARD HALF Device id and login, one cluster Spanner lookup on the way in One shard per user, then columns Dedup at query time, months later Inbound warehouse connectors Replay and experiments, a guess
Three days counts seven rows. The cluster, the shard, and the trillion-event month are Arb.

Paste the allow-list rule from How to write a coding agent prompt, and keep the split in How to scope a SaaS clone.

Start with the first row

Go to Build your own Mixpanel. Copy the Astro prompt, or TanStack Start if that is what you write.

Tell the agent the first estimate only. Event ingest, one funnel, and a daily trend. Three days.

Send the seven accepted events, plus Clicked billing, open November 6, and confirm the funnel reads 3, 2, 1 while the dropped name is absent. If you need anonymous ids merged, a replay, or a warehouse, keep the plan you already pay for that part.

Keep reading

Read all articles →