Build Loom
KINDAreplaces $18/mosaves $216/yrback to the verdict
A recorder that captures screen and camera together, encodes locally, uploads to storage you own, generates a transcript, and serves a share page with a working link, viewing permissions and an expiry, so the whole loop from recording to a link takes under a minute.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the server, the upload endpoint and the processing pipeline.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Remuxing for seekability and poster generation both go through it.
Get it brew install ffmpeg, your package manager, or a static build. open ↗
Verify
ffmpeg -version prints a banner - have ready5 to 10 USD a month plus a domain
Why Display capture and camera capture both require a secure context, so plain HTTP cannot record at all.
Get it A small server with a domain and automatic certificates, or a tunnel for local development.
Verify
The recorder page can request display capture - API keystorage is cheap; egress varies by an order of magnitude between providers
Why Recordings are large and viewers are elsewhere. Egress from a bucket without a network in front is usually the expensive path.
Get it Create a private bucket and credentials, and put a delivery network in front. Compare egress pricing, since it dominates the cost here.
Verify
An object uploads, is refused directly, and serves through the network with a signature - installfree, but it uses server time per recording
Why Phase 4 only. Sharing works without it and the transcript arrives afterwards.
Get it Build whisper.cpp and download a model sized for your server. open ↗
Verify
It transcribes a short file
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- recordings(id, owner_id, title, duration_ms, width, height, state, created_at, expires_at) - assets(recording_id, kind, key, bytes) · kind is video, poster, transcript or captions - shares(id, recording_id, token, visibility, password_hash, expires_at, created_at) · visibility is link, password or account - views(recording_id, share_id, at, watched_ms, viewer_hash) - comments(id, recording_id, at_ms, author, body, created_at) · anchored to a timestamp in the video - The share token is separate from the recording identifier, so a link can be revoked without destroying the recording.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
PUBLIC_URL | required | https://record.example.com | Used in share links. Must be HTTPS or capture will not work at all. |
BUCKET_NAME | required | recordings | Private bucket holding video, posters and transcripts. |
BUCKET_SECRET_KEYsecret | required | replace-with-secret | Scoped to this bucket only. |
CDN_BASE_URL | required | https://cdn.example.com | What viewers fetch from. The bucket hostname never appears in a share page. |
SIGNED_URL_TTL_SECONDS | required | 7200 | How long a signed video URL lasts. Long enough for the recording, short enough to matter. |
DEFAULT_RETENTION_DAYS | required | 365 | How long recordings are kept unless marked to keep. A warning is sent first. |
CHUNK_SIZE_MB | required | 5 | Upload chunk size. Smaller resumes more finely and costs more requests. |
The build, in order
Recording screen and camera together
One recording contains the screen and a camera inset, with audio, and it plays.
Display capture and camera capture as separate streams, composed onto a canvas each frame.
Files
src/capture.tsMediaRecorder over the canvas stream plus microphone audio. Choose a container the browser actually supports.
Files
src/record.tsListen for the display track ending and finalise. This is the case that produces corrupt files.
Files
src/record.ts
done when · tick each as it passeswatch out- The browser's own stop sharing control ends the display track without telling your application through any of the obvious paths, and a recorder that does not listen for the track ending produces a truncated or corrupt file. Listen for the track's end event and finalise the recording properly.
Upload that survives a bad connection
A ten minute recording uploads reliably, and an interruption does not mean starting again.
Multipart upload with recorded offsets so a reconnection continues.
Files
src/upload.tsChunks go as they are produced, so the wait after stopping is short. This is most of the perceived speed.
Files
src/upload.tsMove metadata to the front so the file is seekable, and extract a frame for the poster.
terminalffmpeg -i in.webm -c copy -movflags +faststart out.mp4 ffmpeg -i out.mp4 -ss 2 -vframes 1 poster.jpg
done when · tick each as it passeswatch out- Browser-recorded files often have unseekable metadata, because the duration is not known until recording ends and the container is written progressively. A viewer cannot scrub such a file. Run a server-side remux to move the metadata to the front, which is fast and makes the file seekable.
The link, and who may open it
You get a link the moment the upload finishes, and you control who can use it.
Separate from the recording identifier, so revocation does not destroy anything.
Files
src/shares.tsThe share page issues short-lived signed URLs after checking the visibility rule. Objects stay private.
Files
src/access.tsRequest the object directly, use a revoked token, and open an expired share. All three must fail.
done when · tick each as it passeswatch out- An unguessable link is obscurity, not access control, and for anything sensitive the video object itself must require a signature. Otherwise the share page's permissions are decoration and the object URL circulates instead.
Transcript and captions
Every recording is searchable and watchable without sound.
After upload, in the background. The share link must not wait for it.
Files
src/transcribe.tsA caption track plus clickable lines that seek. The transcript is also how people skim a recording.
Files
src/player.tsAutomatic is a state, reviewed is another. The distinction matters for accessibility.
Files
src/transcript.ts
done when · tick each as it passeswatch out- An automatic transcript presented as captions without review produces confidently wrong text for the viewers who most depend on it. Label it as automatic, make correcting it easy, and treat a reviewed transcript as a different state from a generated one.
Comments and knowing it was watched
Someone can reply at a moment in the video, and you can tell whether it was watched.
Stored with a millisecond offset, rendered on the timeline, seeking on click.
Files
src/comments.tsWatch time and a count. Not a per-viewer timeline of exactly what they rewatched.
Files
src/views.tsOne line telling the viewer what the sender can see. They cannot read your policy and should not have to.
Files
templates/share.html
done when · tick each as it passeswatch out- Detailed per-viewer watch tracking on a link you sent to a colleague is more surveillance than most people expect from a video message. Record coarse aggregates and be explicit in the interface about what the sender can see, because the recipient cannot see your privacy policy.
Storage cost, retention and backupsproduct builder
Old recordings do not accumulate cost forever, and links keep working.
A notice to the owner before anything goes, with a keep action.
Files
src/retention.tsA deleted recording's link explains rather than breaking. People will follow those links for years.
Files
templates/expired.htmlDatabase always; recordings selectively, since they are large. Restore and confirm links work.
Files
scripts/backup.shREADME.md
done when · tick each as it passeswatch out- Deleting a recording breaks every link to it that anyone has ever shared, including in documents and messages you cannot see. Warn before deletion, keep the share page as a clear expired notice rather than a broken link, and make retention opt-in per recording rather than silent.
That is the whole plan for Loom. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Reliable hosting at scale, which is what the subscription sells.
- Workspace permissions and team libraries.
- Editing beyond trimming.
- A commitment that links keep working for years without your attention.
- instant reliable hosting
- permissions
- comments
- analytics
- workspace libraries
- browser extensions
- Trimming the start and end, which is the only edit most recordings need
- A recording queue so several can upload while you make the next one
- Reactions on the timeline, which are cheaper than comments and used more
Need the files? The project pack on the verdict page hands your agent the whole brief · more screen recording.