Building Real-Time Systems: A Deep Dive into Laravel Reverb and ReactPHP

Overview of Real-Time Web Architecture

Traditional web applications operate on a request-response cycle. This model, while reliable, creates a significant lag when users need instant updates, such as chat messages or live status indicators. Developers often resort to short polling, where the client repeatedly hits the server to ask for new data. This is inefficient; it wastes server resources and creates a "stuttering" user experience.

solves this by providing a first-party, high-performance websocket server for the
Laravel
ecosystem. It creates an open "pipe" between the client and server, allowing bidirectional data flow. When something changes on the backend, the server pushes it to the client instantly. This guide explores how Reverb leverages an asynchronous event loop to handle tens of thousands of concurrent connections on a single
PHP
process.

Prerequisites

To follow this tutorial, you should be comfortable with:

  • PHP 8.2+: Knowledge of modern PHP syntax and the
    Composer
    package manager.
  • Laravel 11: Familiarity with the slimmed-down application skeleton and Artisan commands.
  • JavaScript Basics: Understanding of how to handle events in the browser.
  • Networking Concepts: Basic awareness of HTTP vs. WebSockets and UDP vs. TCP.

Key Libraries & Tools

  • Laravel Reverb
    : The core websocket server package.
  • Laravel Echo
    : The JavaScript library used to subscribe to channels and listen for events on the frontend.
  • ReactPHP
    : A low-level library providing the asynchronous event loop that powers Reverb.
  • Datagram Factory: A ReactPHP component for UDP communication (used for hardware integration).
  • FFmpeg: A multimedia framework used here to process and stream video frames.
  • Redis
    : Used as a Pub/Sub mechanism to scale Reverb horizontally across multiple servers.

Under the Hood: The Event Loop

Reverb doesn't use the typical synchronous PHP execution model. Instead, it relies on the

event loop. This loop is essentially an infinite while(true) loop that performs three critical tasks on every "tick":

  1. Future Ticks: It processes tasks deferred from previous iterations to avoid blocking the main thread.
  2. Timers: It executes scheduled tasks, such as pruning stale connections or heartbeats.
  3. I/O Streams: It monitors active sockets for incoming data.

By using non-blocking I/O, a single PHP process can keep thousands of connections "parked" in memory, only using CPU cycles when a connection actually sends or receives data. This is how Reverb achieves its massive scalability.

Code Walkthrough: Hardware Control via Websets

In advanced implementations, you can use Reverb's event loop to interface with hardware, like a drone, using

.

1. Initializing the Custom Server

To gain access to the raw loop, you might hand-roll a command instead of using the default reverb:start.

// Getting the underlying ReactPHP loop
$loop = \React\EventLoop\Loop::get();

// Starting the Reverb server with the loop
$server = ReverbServerFactory::make($loop, $config);

2. Communicating via UDP

Drones often require UDP for low-latency commands. We use the Datagram\Factory to create a client within the Reverb process.

$factory = new \React\Datagram\Factory($loop);
$factory->createClient('192.168.10.1:8889')->then(function ($socket) {
    // Send an initialization command
    $socket->send('command');
    
    // Store the socket in a service for later use
    app()->singleton(FlyService::class, fn() => new FlyService($socket));
});

3. Handling Client Whispers

To send commands from the UI (like "flip" or "move") without a full Laravel controller cycle, we can intercept Client Whispers. These are lightweight messages sent from one client to others that Reverb normally just passes through.

// Listening for Reverb's MessageReceived event
Event::listen(MessageReceived::class, function ($event) {
    $message = $event->message;
    
    if (str_starts_with($message, 'client-fly-')) {
        $command = str_replace('client-fly-', '', $message);
        // Resolve our UDP service and fire the command to the hardware
        app(FlyService::class)->send($command);
    }
});

Syntax Notes

  • Non-blocking logic: Never use sleep() or long-running foreach loops inside the event loop. This stops the entire server. Use timers or chunking instead.
  • Singleton Pattern: When working with hardware sockets (UDP/TCP), bind the connection as a singleton in the Laravel container so it persists across different parts of the application.
  • Client Whispers: These always start with the client- prefix by convention in
    Laravel Echo
    .

Practical Examples

  • Telemetry Dashboards: Streaming sensor data (battery, temperature, altitude) from IoT devices to a web UI in real-time.
  • Video Streaming: Using FFmpeg to pipe video frames into a Reverb event, base64 encoding the image chunks, and rendering them onto a <canvas> element on the frontend.
  • Live Collaborative Tools: Real-time cursor tracking or document editing where sub-100ms latency is required.

Tips & Gotchas

  • Scaling with Redis: If you run multiple Reverb servers behind a load balancer, you must use the
    Redis
    publish/subscribe adapter. This ensures that an event received by Server A is broadcasted to clients connected to Server B.
  • Avoid Deadlocks: Do not perform synchronous HTTP requests or database queries inside the MessageReceived listener unless they are wrapped in an asynchronous wrapper. Doing so will block the event loop and potentially crash the websocket server.
  • Memory Usage: Since connections stay in memory, monitor your server's RAM.
    Laravel Pulse
    integrates directly with Reverb to provide real-time monitoring of connection counts and message throughput.
5 min read