Skip to content

krish9219/openloom

Repository files navigation

openloom

Open-source screen recording with shareable links. Self-hosted in 60 seconds.

demo

stars license next no transcoding setup tests

openloom captures your screen and mic in the browser, uploads to your own server, and gives you a /v/<id> URL anyone can play. No SaaS account, no third party storing your videos, no 5-minute free-tier cap. Roughly 500 lines of TypeScript end to end.

Quick start

git clone https://github.com/krish9219/openloom
cd openloom
cp .env.example .env
npm install
npm run setup           # creates the SQLite DB
npm run dev
# open http://localhost:3000

Click Start recording, pick a screen, click Stop, click Upload. You get a share URL. Done.

How it works

flowchart LR
    subgraph Browser
        DM[getDisplayMedia<br/>screen track]
        UM[getUserMedia<br/>mic track]
        MR[MediaRecorder<br/>webm/mp4 blob]
        DM --> MR
        UM --> MR
    end
    MR -->|POST /api/recordings| API[Next.js API route]
    API --> N[nanoid id]
    API --> FS[STORAGE_DIR/&lt;id&gt;.webm]
    API --> DB[(SQLite via Prisma)]
    API -->|share URL| U[/v/&lt;id&gt;/]
    U -->|GET /api/recordings/&lt;id&gt;| FS
Loading

That's the whole architecture. SQLite for metadata, your local disk for blobs, the browser's built-in MediaRecorder API for capture. No transcoding service in the middle — what your browser records is what your viewers see.

What you get

  • One-click recording — screen + mic, no extension to install. Works in any modern Chromium / Firefox.
  • Local preview — review the recording before upload, discard if you flubbed.
  • Shareable links — every recording gets a /v/<id> URL anyone can watch.
  • Library page — your last 50 recordings, with view counts.
  • No external services — your videos never leave your server.
  • Sandboxed storagepathFor() rejects path traversal in id and ext; tested.
  • Range-friendly playbackAccept-Ranges: bytes headers so the browser can seek.

Configuration

Env var Default Notes
DATABASE_URL file:./openloom.db SQLite. Switch to Postgres by editing prisma/schema.prisma and re-running prisma db push.
STORAGE_DIR ./uploads Where recording blobs are written. Use an absolute path in production.

Browser support

Screen capture (getDisplayMedia) requires HTTPS or localhost. It works in Chrome, Edge, Firefox, and Opera. Safari supports it as of macOS 14, but with quirks around audio capture. Mobile browsers do not support screen capture at all (platform limitation).

What this is NOT

  • No transcription / captions. Real Loom transcribes your video with Whisper-grade quality. openloom doesn't.
  • No real-time streaming. Recording happens entirely client-side; the upload is one chunk at the end.
  • No CDN out of the box. Files serve from your Next.js process. For >100 viewers, put Cloudflare in front of /api/recordings/*.
  • No authentication. Anyone who can reach the deployment can upload and view. Add a reverse-proxy auth layer if you need privacy.

Each of these is a finite addition, but the point of openloom is the smallest thing that does the core job: record, store, share.

vs. the alternatives

openloom Loom Screenity Screen.studio
Self-hosted yes no yes (Chrome extension) no
Shareable URL out of the box yes yes no yes
Account required no yes no yes
Storage your disk Loom's cloud local download Screen.studio's cloud
Transcoding service none yes none yes
Built-in transcription no yes no yes
5-min free-tier cap n/a yes (free plan) n/a n/a
Best for self-host on a VPS non-technical sharing quick local recordings polished marketing demos

The pitch isn't "we have more features than Loom." The pitch is: the video never leaves your server. If that's not a constraint for you, Loom is genuinely good.

FAQ

Does it work on mobile? No. Browser screen capture (getDisplayMedia) is a desktop-only API. iOS and Android don't allow web apps to capture the screen for privacy reasons.

Does it work on Safari? Yes since macOS 14, but audio capture is finicky. Chrome / Edge / Firefox give the most reliable results.

Can I add transcription? Yes — run the saved file through OpenAI Whisper or whisper.cpp after upload. About 30 lines in api/recordings/route.ts (run async after the blob is saved). Issue welcome if you'd like a PR walkthrough.

Will it work behind nginx? Yes, but set client_max_body_size 200M; (matches the in-app upload cap). Otherwise nginx returns 413 before Next.js sees the upload.

Where do I deploy this? Single-server VPS is simplest. For Vercel / serverless you need object storage (S3, R2) — swap lib/storage.ts for an S3 client (~80 lines of changes).

Is there a viewer auth option? Not yet. Every uploaded recording is reachable by URL. Reverse-proxy auth or a per-recording token (one PR away) gets you private recordings.

Tests

npm test

Tests cover MIME-to-extension mapping, save/load round-trip, path-traversal rejection in storage, and idempotent removal. They run without a database — just the storage module.

Deploy

Single-server VPS: works as-is. npm run setup && npm run build && npm start. SQLite + local disk just work.

Vercel / serverless: needs object storage (S3, R2). Swap lib/storage.ts for an S3 client and a presigned-URL flow. About 80 lines of changes.

Behind nginx: increase client_max_body_size to at least 200 MB (the openloom upload cap), and proxy /api/recordings/* directly so video streaming doesn't buffer in nginx.

Contributing

See CONTRIBUTING.md. Security: see SECURITY.md.

Star history

Star History Chart

License

MIT — see LICENSE.

About

Open-source Loom alternative. Browser screen + mic recording, self-hosted, share via /v/<id>.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors