Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Video Chat Laravel Package

php-junior/laravel-video-chat

Laravel Video Chat adds one-to-one WebRTC video calling to Laravel apps. It handles signaling through Laravel events and Echo, includes ready-made Vue call components, and lets you add real-time peer-to-peer video chat without building the WebRTC plumbing yourself.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Laravel wrapper around a WebRTC client and a Socket.IO signaling server. It gives you a working /video-chat page out of the box; treat it as a reference implementation you will fork and harden rather than a drop-in production feature.

Minimal path:

  1. composer require php-junior/laravel-video-chat
  2. Register PHPJunior\VideoChat\VideoChatServiceProvider in config/app.php if you are on a Laravel version that does not auto-discover it (the package predates 5.5 conventions in places).
  3. Publish config, views and assets: php artisan vendor:publish --provider="PHPJunior\VideoChat\VideoChatServiceProvider"
  4. Point the config at your signaling server (typically a socket_url / host+port entry, often fed from VIDEO_CHAT_SOCKET_URL).
  5. Start the bundled Node/Socket.IO server (server.js in the package, per the README) with npm install && node server.js.
  6. php artisan serve, open http://localhost:8000/video-chat in two browser tabs, grant camera/mic, and confirm the offer/answer exchange.

Where to look first:

  • README.md — the only reliable source for the exact route, config keys and server entrypoint, since the package is unmaintained.
  • config/videochat.php (published) — socket URL, port, and any ICE server config.
  • routes/ inside the package — confirms the route name, URI and whether middleware is applied.
  • resources/views/ — the Blade template that boots the WebRTC client.
  • server.js — the signaling contract (event names, room semantics) you will need to extend.
  • The client JS (published to public/vendor/... or bundled in the view) — this is where RTCPeerConnection, getUserMedia and socket.emit calls live.

First real use case: a two-party, authenticated room inside an existing app. Get the raw package page working first on localhost, then immediately replace the public route with an auth-gated one before wiring it into a product.

Implementation Patterns

Drop-in page vs. embedded widget. The fastest integration is to keep the package route for internal demos and iframe it inside your app layout when you need isolation from your CSS/JS. For anything customer-facing, publish the views and re-implement them inside your own layout — the bundled Blade is minimal and you will want your own chrome (participant list, mute/camera toggles, hang-up button).

Auth-gated rooms. The default route is effectively public. Wrap it with auth (and any tenant/authorization middleware) and generate a room identifier server-side instead of trusting a URL segment. A simple pattern:

  • GET /rooms/{room} → controller checks membership → renders the video view with a signed room token.
  • The token is passed to the signaling server on connect and verified there; never trust a client-supplied room name alone, or anyone can join any room.

Room model. Add a video_rooms table (or reuse an existing conversation/meeting model) so rooms have owners, participants and expiry. Use the model's UUID as the room id — this avoids guessable/incrementing room names and gives you a place to hang recording metadata, transcripts, etc.

Signaling contract. Expect to fork server.js. Typical extensions: emit a participants list on join, broadcast peer-joined / peer-left, add a signal passthrough (offer/answer/ICE), and namespace events per room (socket.join(room) in Socket.IO). Keep the server stateless per-connection except for the room map; store durable state in Laravel via events if you need call history.

ICE configuration. Configure STUN and TURN in the client RTCPeerConnection config. STUN alone works on LAN and friendly NATs; a large fraction of real users need TURN. Run coturn (or a hosted TURN provider) and inject credentials from config rather than hardcoding them in the published JS.

Deployment topology. Three moving parts: PHP-FPM/Nginx for Laravel, a long-running Node process for signaling, and a TURN server for media relay. Supervise Node with systemd/supervisor, and make sure your reverse proxy forwards WebSocket upgrades:

location /socket.io/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

Serve the app over HTTPS and the socket over wss:// in production — getUserMedia is blocked on insecure origins except localhost.

Extending the client. The published JS is the extension point for screen sharing (getDisplayMedia), device switching (enumerateDevices), mute/camera toggles (track.enabled), and renegotiation. Keep these as small composable modules rather than editing the vendor file in place, so re-publishing does not destroy your work.

Scaling shape. Default topology is mesh: every participant connects to every other participant. This is fine for 2–4 people and collapses beyond that (upload bandwidth). For larger rooms you need an SFU (Janus, mediasoup, LiveKit) — the package will not get you there; plan the migration before you need it.

Gotchas and Tips

  • It is effectively abandoned. Last release December 2017. Composer constraints target Laravel 5.x; on Laravel 8/9/10 expect conflicts. Options: fork into a path repository and patch the service provider, or lift the views and server.js into your app and drop the dependency entirely. The latter is usually the right call — the amount of useful code is small.
  • Service provider discovery may not fire. If routes 404 after install, confirm the provider is registered manually and that php artisan route:list shows the video-chat route.
  • config:cache and env(). Config values read via env() inside the package config are fine, but once cached, changing .env has no effect until php artisan config:clear. This bites people who rotate the socket URL or TURN credentials.
  • vendor:publish --force overwrites your edits. If you customized published views or JS, keep them in your own resources/ and public/ paths instead of the vendor-published locations.
  • Socket.IO client/server version mismatch. A v2 server with a v4 client (or vice versa) fails silently or with opaque CORS errors. Pin both sides together; check what the bundled server.js declares in package.json.
  • No TURN by default. "Works on my machine, fails for the client" is almost always NAT/firewall. Check chrome://webrtc-internals and confirm ICE candidate pairs succeed; if only host/srflx candidates appear, add TURN.
  • Mixed content and permissions. HTTPS everywhere, and handle NotAllowedError / NotFoundError from getUserMedia with a real UI message — users deny camera permission more often than you expect. Safari also enforces stricter autoplay and requires a user gesture to start media in some flows.
  • CORS on the signaling server. Restrict origin in the Socket.IO server config; the default is often permissive. Combine with token verification on the handshake (socket.handshake.auth) so the server is not an open relay.
  • Stateful Node process. Rooms live in memory; a restart or deploy drops every active call. Add graceful drain (stop accepting, wait for rooms to empty) and health checks; do not scale to multiple Node instances without a Redis adapter and sticky sessions.
  • Media is not end-to-end encrypted by your app. WebRTC gives you DTLS-SRTP hop-by-hop, which is fine, but do not advertise E2EE. If regulatory requirements demand it, that is a different architecture (insertable streams / SFU with E2EE support).
  • Debugging toolkit. Browser console for socket.on('connect_error'), chrome://webrtc-internals for ICE/bitrate graphs, node server.js stdout for signaling events, and tcpdump/TURN logs when media never flows. Reproduce two-party failures with two different networks (phone hotspot + Wi-Fi), never two tabs on the same machine.
  • Firewall reality check. Media uses UDP in a wide ephemeral range; corporate networks block it. A TURN server on TCP 443 is the fallback that saves these users.
  • Upgrade path. Before committing, decide whether you are maintaining a fork. If yes, vendor the two files that matter (the Blade view and server.js), write your own controller and route, and keep the composer dependency only for the signaling event names you copied. The dependency itself contributes little beyond scaffolding.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin