Build a Vimeo alternative

Build a Vimeo alternative as an upload, a player page, and a view count. The card lists $20 to $65 a month. Adaptive transcoding and live streaming stay later.

  • teardown
  • vibe-coding
  • scope

You can build a Vimeo alternative as an upload, a player page, and a view count. Vimeo is that product. The catalog line is “Your videos on your own domain, minus the platform chrome.”

The card lists $20 to $65 a month. The card does not name the plans in that range. The first estimate is 10 hours. The verdict is “The player is the easy part”.

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

The public sources are Vimeo’s posts on the Falkor transcoding pipeline, on Mimir, their adaptive player, on Artax, progressive files built on the fly, and on the 240p profile. Where those pages stop, I call it a guess.

Build a Vimeo alternative from one upload

The Vimeo page splits the work into three rows.

The first row is 10 hours. Uploads, a branded player, unlisted links, and view counts. You upload a file. You get a page. The file plays.

The second row is 3 days. Captions, chapters, folders, password links, and analytics charts. A transcript file and a password can wait until the unlisted page already plays.

The third row is 3 months and up. An adaptive transcoding pipeline, DRM, live streaming, and apps. The table labels that row as the part you should skip.

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

Store Video, ShareLink, and ViewEvent. The card names all three. For these 10 hours, all three exist. Video is the file and the title. ShareLink is the unguessable slug. ViewEvent is a play on a local date, with no visitor id.

A video is one uploaded file, private in object storage. A share link is the slug on the player page. A view event is one play start, counted into that video’s total for the day.

The audience is a founder hosting product demos and course videos. The goal on the card is to upload once and share a player page on your own domain. The same v1 also stands in for Wistia. The card lists Wistia and does not give it a price.

A tab recording and a share link is Loom at $15 to $24 a person a month, in Vibe code your own Loom. Comments pinned to a timestamp are Frame.io at $15 to $25 a person a month. Those are other video jobs. This one is a file you already have, on a page you host.

The guardrail is the rule you paste into the prompt. Validate the file type and the size on the server. Serve the video through an unguessable URL. Never list the bucket in public. Count views without fingerprinting the visitor.

How Vimeo turns an upload into renditions

pin-demo.mp4 is 48 seconds. Lena wants a page that plays that file. Their pipeline is built for a much longer job.

Falkor is the pipeline they wrote up for everything harder than that. A client tells the video API to transcode, with a list of profile sets. Not every video gets AV1. The API checks the request, finds the source, and asks Falkor for an analysis job. That job returns duration, codecs, frame rate, whether the video is HDR, and stores the metadata for the jobs after it.

The video API then picks audio and video profiles. Resolutions, HDR or not. Each profile is its own Falkor job. Audio workers transcode the source audio without downloading the whole file locally, then upload the result. Video is split. Falkor looks at the file’s index and picks cut points, ideally chunks of about one minute. Workers transcode those chunks in parallel. Each worker fetches the byte range it needs and uploads its piece.

When the chunks are done, a compose worker builds a header from the chunk headers, including the moov and SIDX boxes, then concatenates that header with the chunks. Their post says the stitch is an API call to cloud storage. Falkor tells the video API the job finished. The video API adds the new file to the video management system. Playback can start when one H.264 video and one AAC audio are ready, or the client can wait until every profile is done. If a video cannot be split, they transcode it as a single chunk in Tron. The post marks that fallback as temporary. Whether Tron is still the escape hatch is a guess.

A 48-second file sits under the one-minute target. A 20-minute course video is the case the chunks are for. Twenty pieces, one profile, is already a fan-out, and six resolutions multiply it, plus the audio job. The 10-hour clone stores the original and plays it.

Playback storage is CMAF. The Artax post says the current stack writes fragments, audio and video in separate files, cut at six-second intervals. Those cuts are the switching points. HLS and MPEG-DASH both read that layout. The old system wrote a progressive file, one index and the media in a single run. Artax builds that progressive response on the fly for clients that still want it. Their CDN partner starts with a 0-0 range request, caches the header boxes, returns a content length, and pulls later ranges.

The player then chooses a rung. The Mimir post describes segments of 3 or 6 seconds, in several qualities, 360p, 720p, 1080p and the rest. For each segment the player picks from the bandwidth it has seen. The goals they list are a fast start, high quality, no stalls, and few switches. Mimir is a reinforcement-learning picker, A3C, trained by replaying real playback sessions in a simulator. In those simulated tests, Mimir beat a throughput baseline by 26 ± 3 percent on their reward, and cut rebuffering by 69 percent against that baseline.

The 240p post adds the rung under 360p. They stream DASH or HLS, and the new profile is 426 by 240. A conservative read of global bandwidth, including mobile and emerging markets, came out at 380 kb/s. They tuned x264 so the 240p stream fits in 300 kb/s. Lena’s clone has one file and the bitrate she uploaded.

Live streaming and DRM are on the card’s month row. These posts do not describe a live room or a DRM license server, so both stay a guess.

Screens of a Vimeo alternative

v1 needs the upload and the player page. Captions, chapters, and a password live on the 3-day row. A quality picker is the pipeline.

The upload

Lena is adding pin-demo.mp4. The title is “Pin demo”. The type and the size are checked before the object is kept. The progress bar is the upload of the original file. Save creates the video row once the object is stored.

WIREFRAME · UPLOAD Title · Pin demo pin-demo.mp4 · 48 seconds Progress · original file only Save Type and size checked before the row exists
The bar is the upload. Nothing is being turned into six resolutions.

The player page

The slug is unguessable. The page shows the title, a player pointed at that file, today’s view count, and an embed line she can copy. November 6 is the local date on the count. There is no account for the viewer.

WIREFRAME · PLAYER Pin demo · unlisted player · original file Nov 6 · 1 play · no visitor id Embed · copy the player URL Slug is the only way in
One file, one page, one day's count. The bucket stays private.

How a demo gets a page

Lena picks the file. The browser uploads it to private object storage and shows progress. Node.js plus Astro is the default. Use TanStack Start if React is your weekday language. The capability line on the card is Astro, R2, and Workers. R2 holds the object. The Astro app writes the video row when the object is there, with a slug she did not choose.

A bad type or an empty file never gets a row. The check is on the server, because the page can lie about the type. The player URL points at the app, or at a signed read, and the bucket itself is not listed. Someone who guesses /videos/pin-demo.mp4 should miss. Someone with the slug should play.

A play start adds one view event for that video and the local date. November 6 shows 1 after she tests the page. A second refresh can show 2. The guardrail forbids a fingerprint, so this count is plays, not people. Unique viewers would need an id. That is a different product, and it is not this row.

The embed line is the player URL. Paste it on the marketing site. Password links, captions, chapters, and folders are the 3-day row. A chart of the week’s views is the same events, grouped. It can wait until the page plays.

LENA UPLOADS THE FILE pin-demo.mp4 · progress on the page CHECK, THEN STORE Type and size. Private object. Then the row. UNLISTED PAGE Slug she did not pick. Original file plays. COUNT THE PLAY Nov 6 · one event · no fingerprint Embed is that same URL
Ten hours ends when the unlisted page plays the file she uploaded.
LENA'S BROWSER One file, then the player page ASTRO APP Check, slug, play count R2 Private original D1 Video, slug, views THEIR PIPELINE, THEN SKIP Analysis, profiles, one-minute chunks Compose the moov, then CMAF HLS, DASH, and the Mimir picker Artax, progressive on the fly A 240p rung at 300 kb/s Live ingest and DRM, a guess Apps
The original file is the v1. The dashed box is the pipeline in their engineering posts.

The hard half of a Vimeo alternative

Ten hours is a private object, a slug, a player, and a play counter that does not identify the viewer. Lena opens the page, the 48-second file plays, November 6 shows 1.

Falkor is analysis, a profile decision, parallel chunks, a compose step, and a rule for when one H.264 and one AAC are enough to press play. A failed chunk has to be redone while the finished chunks stay. The post describes the path where every chunk succeeds. A retry rule is a guess.

CMAF is a new file every six seconds, and the player has to pick a quality for each one. Mimir scores that pick in a simulator. The 240p rung is the measured answer when a link has about 380 kb/s. Artax is the path for a client that still wants one progressive file while storage is fragments, including the CDN’s 0-0 range request.

DRM and live are on the card, and they are absent from these posts. A license server and a live ingest are separate systems. The non-goal says v1 plays the uploaded file as-is.

10-HOUR V1 Upload the original, keep it private Unlisted player and an embed A play count for the local date Play count on the original file HARD HALF Analysis, chunks, compose CMAF, HLS, DASH Mimir picking a rung per segment 240p at 300 kb/s, Artax on the fly Live ingest and DRM licenses Apps on top of that ladder
Ten hours plays the file she uploaded. The ladder and the live path are the month row.

Paste the private-bucket 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 Vimeo. Copy the Astro prompt, or TanStack Start if that is what you write.

Tell the agent the first estimate only. Uploads, a branded player, unlisted links, and view counts. Ten hours.

Upload a 48-second demo, open the slug in a private window, and confirm the file plays and the bucket is not listed. If you need six resolutions, a live room, or DRM, keep the plan you already pay for that part.

Keep reading

Read all articles →