Drop-in WebRTC video calls for any web app. One iframe, one npm install, or one React/Vue hook — whichever fits your stack. Self-hosted, open source, runs as a single Docker container.
Live demo → Examples → Integration guide → Deploy your own →
Copy any of these and paste into your page. They all talk to our hosted
demo server at meet.dokaz.online so you can try without deploying.
<iframe
src="https://meet.dokaz.online/embed?room=hello-world&autoJoin=1"
allow="camera; microphone; display-capture"
width="100%" height="600"
style="border:0; border-radius:8px"
></iframe>That's it. Open in a browser. Open the same page on another device. Done.
<video id="me" muted autoplay playsinline style="width:300px"></video>
<div id="remotes" style="display:flex; gap:8px"></div>
<script src="https://meet.dokaz.online/sdk/meetapp-sdk.umd.cjs"></script>
<script>
const room = new Meetapp.MeetappRoom({
serverUrl: 'https://meet.dokaz.online',
roomId: 'hello-world',
})
room.on('trackPublished', ({ peerId, stream }) => {
const v = document.createElement('video')
v.autoplay = true; v.playsInline = true; v.srcObject = stream
document.getElementById('remotes').appendChild(v)
})
await room.join()
room.attachLocalVideo(document.getElementById('me'))
</script>npm install meetapp-react meetapp-sdkimport { useMeetapp, MeetappVideo } from 'meetapp-react'
export function Call() {
const { localStream, peers } = useMeetapp({
serverUrl: 'https://meet.dokaz.online',
roomId: 'hello-world',
autoJoin: true,
})
return (
<>
<MeetappVideo stream={localStream} muted />
{[...peers].map(([id, p]) => (
<MeetappVideo key={id} stream={p.stream} />
))}
</>
)
}npm install meetapp-vue meetapp-sdk vue<script setup>
import { useMeetapp, MeetappVideo } from 'meetapp-vue'
const { localStream, peers } = useMeetapp({
serverUrl: 'https://meet.dokaz.online',
roomId: 'hello-world',
autoJoin: true,
})
</script>
<template>
<MeetappVideo :stream="localStream" muted />
<MeetappVideo v-for="[id, p] in peers" :key="id" :stream="p.stream" />
</template>Runnable examples for each of these (npm scripts, full project
scaffolding) live in examples/ — clone and npm install && npm run dev.
Full integration cookbook with recipes (chat, recording, screen share, device picker, common patterns): docs/INTEGRATION.md.
The demo at meet.dokaz.online is a real instance. Use it for prototypes
and demos, but for production you'll want your own. One Docker compose
file, one domain, five minutes:
# On a fresh VPS (Ubuntu 22.04+)
curl -fsSL https://get.docker.com | sudo sh
mkdir -p ~/meetapp && cd ~/meetapp
curl -O https://raw.githubusercontent.com/dixydo/meetapp/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/dixydo/meetapp/main/Caddyfile
curl -o .env https://raw.githubusercontent.com/dixydo/meetapp/main/.env.example
nano .env # set DOMAIN, MEETAPP_TURN_*, optionally MEETAPP_S3_*
docker compose pull && docker compose up -dDNS, TLS, firewall, TURN, S3 recording: docs/DEPLOY.md.
- SFU on Pion v4. Each client uploads its tracks once; server fans them out. 4+ participants don't melt your users' laptops.
- Embedded TURN. Runs as a goroutine inside the same Go binary — most setups don't need a separate coturn.
- Auto-TLS. Bundled Caddy fetches Let's Encrypt certs on first hit.
- Server-side recording. Records each track as IVF/Ogg, muxes via ffmpeg into one MP4, uploads to DigitalOcean Spaces / any S3-compatible.
- No accounts. Anyone with a room URL joins. JWT room tokens are on the roadmap.
- One binary, one ~140 MB Docker image. Ships with ffmpeg.
Architecture deep dive: docs/ARCHITECTURE.md.
┌─────────────────────────────────┐
│ Browser │
│ (Nuxt SPA OR meetapp-sdk) │
└───────────┬─────────────────────┘
│
HTTPS + WSS │ WebRTC (SRTP)
┌───────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ Caddy 80 │ │ Go binary │ │ Go binary │
│ /443 │──▶│ HTTP+WS+SFU │ │ embedded TURN│
└──────────┘ │ │ │ (UDP 3478 + │
(auto-TLS) │ pion v4 │ │ ephemeral) │
└──────┬───────┘ └──────────────┘
│
RTP fan-out
│
▼
┌──────────────┐
│ Recording │ IVF + Ogg per track
│ ffmpeg → MP4 │──▶ s3://bucket/...
│ S3 upload │ (DO Spaces)
└──────────────┘
| Layer | Pick |
|---|---|
| Backend | Go 1.24, pion/webrtc v4, pion/turn v3, coder/websocket, aws-sdk-go-v2 |
| Frontend (built-in UI) | Nuxt 3 (SPA mode), Vue 3, Tailwind |
| SDK | TypeScript, Vite (ESM + UMD + .d.ts) |
| Infra | Docker, Caddy 2, GitHub Actions, ffmpeg |
| Recording target | DigitalOcean Spaces (or any S3-compatible bucket) |
| INTEGRATION.md | Embed via iframe / SDK / React / Vue with recipes |
| DEPLOY.md | Production VPS deploy + TURN + S3 + auto-deploy |
| ARCHITECTURE.md | SFU design, signaling protocol, recording pipeline |
| DEVELOPMENT.md | Local dev, repo layout, release flow |
| CONTRIBUTING.md | How to file issues and PRs |
| CHANGELOG.md | Release notes |
How is this different from Jitsi / LiveKit / Daily? Smaller in scope. meetapp is for the case where you want a single Docker image you can read end-to-end in a weekend (~3K Go + ~3K TypeScript), no signup, no per-minute pricing. Jitsi / LiveKit are more featureful and more complex; Daily is hosted and great but commercial.
Can I use it commercially? Yes — MIT licensed. The hosted demo at
meet.dokaz.online is for experimentation; for production traffic, run
your own.
What about group call limits? Tested cleanly to 6 publishers per room. Bottleneck at scale is server bandwidth (each publisher × every subscriber). Cascaded SFUs are a v2-if-needed feature.
Mobile apps? Browser only — both iOS Safari and Chrome on Android work. Native apps would need Pion bindings (possible, not done).
Auth? Open: anyone with the room URL joins. JWT-based room tokens are on the roadmap. Until then, treat room IDs as the secret.
Why VP8 + Opus only? The recording writers (Pion IVF + Ogg) are codec-specific. Restricting the MediaEngine to VP8/Opus makes recording work predictably across browsers. H264/VP9 + simulcast are on the roadmap.
Can I disable recording? Yes — leave the MEETAPP_S3_* env vars
unset. The Record button hides itself in the UI.
MIT — see LICENSE.
Built on the shoulders of Pion (WebRTC in Go), Caddy (the TLS-by-default reverse proxy), and Nuxt.