# WebRTC Video Broadcast: one sender, many watchers, plain Node

> WebRTC-Video-Broadcast is a small JavaScript example that lets one broadcaster push a camera and microphone stream to every connected watcher over WebRTC, with an Express and Socket.IO server. It ships a Dockerfile and no TURN configuration, so most real deployments need work before they work at all.

**TannerGabriel/WebRTC-Video-Broadcast** — WebRTC video/audio broadcast

- Repository: https://github.com/TannerGabriel/WebRTC-Video-Broadcast
- Stars: 533 · Forks: 197
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/tannergabriel-webrtc-video-broadcast

## One broadcaster feeding every watcher

The project is a WebRTC peer-to-peer broadcast application. The model is asymmetric: a single broadcaster captures a video and audio stream and sends it to all connected users, who the README calls watchers. That shape is the interesting part, because a plain WebRTC peer connection is one to one. Fanning a single upstream stream out to many receivers means every watcher needs its own peer connection with the broadcaster, and the media has to be routed to each of them rather than sent to a server that fans it out. The people who benefit are developers who need a reference for that topology, and teams who want a quick internal tool, for example a demo page where one presenter pushes their screen or camera to a room of colleagues. The topic tags name socket-io, webrtc, webrtc-call and webrtc-video, and the repository is small enough to read in one sitting, which is the main argument for using it.

## An Express server with Socket.IO doing the signalling

The server side is a Node application whose entry point is server.js, declared as main in package.json. The dependency list is short and tells you the shape of the design:

```json
  "dependencies": {
    "express": "^4.18.2",
    "socket.io": "^4.6.1"
  }
```

Express serves the static pages, and socket.io provides the realtime channel that the browser code uses to announce a broadcaster and to reach the watchers. The repository layout matches that split: server.js at the top level, and a public directory holding the client side files, among them broadcast.html and watch.js, which the TURN section names. The README does not spell out the message flow between them, and instead points to a separate tutorial on the author's website that explains the code and the functionality. That is the honest shape of the documentation here: the mechanics live in the tutorial, while the repository gives you the code and the run instructions.

## First run with Node

The quick start is two commands. The getting started section says to start the application using Node, first installing the dependencies for the server and then running it:

```bash
# Install dependencies for server
npm install

# Run the server
node server
```

There is no build step, no bundler and no environment configuration to set first. npm install resolves express, socket.io and, as a development dependency, eslint. The node server invocation starts the Express app and the Socket.IO server together, since both live in the same process. What you should see is the application listening on port 4000, because the Dockerfile exposes 4000 and the Docker run command maps it. The package.json also carries a test script, and it is worth reading what it does before you rely on it: the script is set to echo an error message and exit with status 1, meaning the repository ships no automated test suite at all.

## The Docker route and what the image actually contains

A Dockerfile is provided for containerised runs, and it is short enough to read in full:

```dockerfile
FROM node:lts

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 4000:4000

CMD [ "node", "server.js" ]
```

Two details matter. The base image is node:lts, so you get whatever the current LTS tag points to rather than a pinned version, and the build runs npm install from the copied package files before the source is copied in, which keeps the dependency layer cacheable. The image listens on 4000 and starts the same server.js entry point as the local run. The build and run commands from the README are:

```bash
# Building the image
docker build --tag webrtcvideobroadcast .

# Run the image in a container
docker run -d -p 4000:4000 webrtcvideobroadcast
```

The -p 4000:4000 mapping publishes the container port on the host, and the detached flag is what lets the container keep running while you open the pages.

## Two pages, and a step that is easy to miss

Testing the running application takes two browser tabs, and the order matters. The README says the application should be running on your localhost:4000 and that you test it by connecting to localhost:4000/broadcast.html to add a new broadcaster. After that, you visit localhost:4000 to connect to the server as a client, and you should get the video that is streamed from the broadcaster. So the broadcaster page creates the upstream stream and the root page plays it, which is a different arrangement from most video tools where the viewer is a separate route. The consequence is that the browser asking for camera and microphone access is the one that publishes, and every other tab is a receiver. The repository also includes a .eslintrc and an eslint development dependency, so the client code is linted even though it is not tested.

## TURN is commented out, and that is the real limitation

The most consequential sentence in the README is about a feature the repository does not enable. A TURN server is used to relay traffic when a direct peer-to-peer connection fails, and the README says it is required for most WebRTC applications, because a direct connection is often not possible between two clients that are not on the same network. This repository does not include TURN usage by default; you add it by uncommenting the turn configuration in the broadcast.js and watch.js files and filling in your TURN credentials. In other words, the default configuration works for viewers and broadcaster on the same network, and breaks for anything else. The README points at two self-hosted options, Coturn and the Golang WebRTC pion library TURN examples, and mentions that cloud providers offer TURN servers as well, with a separate tutorial on setting up Coturn and attaching a domain. A deployment that leaves this commented out will fail for every viewer outside the local network, and the failure looks like a connection that never establishes.

## MIT terms, no releases and a renovate configuration

The project is licensed under the MIT License, with the terms in the LICENSE file, and the package.json repeats the same MIT licence identifier for the npm metadata. There is a supporting link to a coffee shop page under the support heading, and the author is Gabriel Tanner, who also wrote the linked tutorial and the TURN guide. On the maintenance side, the repository publishes no GitHub releases, the default branch is master, and the last push was on 2026-10-10, so the code is recent even though there is no versioned artefact to install. The presence of renovate.json, alongside a package-lock.json in the top level, indicates that dependency updates are handled by automation rather than by hand. The upgrade cost is therefore low in effort and low in ceremony: no semver contract to honour, and no release notes to read, which also means no stability guarantee for anyone who depends on a specific version.

## Conclusion

Adopt WebRTC-Video-Broadcast if you want a readable reference implementation of one-to-many WebRTC media, or a starting point for a low-latency internal broadcast page, and you are willing to supply your own TURN credentials and your own production concerns around HTTPS and media permissions. Do not adopt it as a ready-made streaming service: the TURN configuration is commented out in broadcast.js and watch.js, the only test script exits with an error, and the project publishes no GitHub releases, with the last push on 2026-10-10. Before the first run, build the Docker image, open localhost:4000/broadcast.html to create the broadcaster, then load localhost:4000 in a second browser and confirm the stream arrives before you change anything else.

## FAQ

### How do I run the WebRTC Video Broadcast application locally?

The getting started section says to install the server dependencies with npm install and then run node server, which serves the application on port 4000. A Dockerfile is included as an alternative, built with docker build and run with docker run -d -p 4000:4000.

### How do I add a TURN server to WebRTC Video Broadcast?

The README says TURN is not included by default and that you enable it by uncommenting the turn configuration in the broadcast.js and watch.js files and filling in your TURN credentials. It suggests Coturn or the pion TURN examples, or a cloud provider.

### Which page do I open to broadcast and which one to watch?

You connect to localhost:4000/broadcast.html to add a new broadcaster, then visit localhost:4000 as a client to receive the streamed video. The two roles are separate pages, and the broadcaster is the one that needs camera and microphone access.

## Sources

- [Issues](https://github.com/TannerGabriel/WebRTC-Video-Broadcast/issues)
- [License: MIT](https://github.com/TannerGabriel/WebRTC-Video-Broadcast/blob/master/LICENSE)
- [README](https://github.com/TannerGabriel/WebRTC-Video-Broadcast/blob/master/README.md)
- [TannerGabriel/WebRTC-Video-Broadcast on GitHub](https://github.com/TannerGabriel/WebRTC-Video-Broadcast)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tannergabriel-webrtc-video-broadcast
