Create a Video Conference App using Laravel, PHP, and Vue.js

April 14, 2020
Written by

Create a Video Conference App in Laravel 7.png

During times where we can’t be together physically, video conferencing helps reinforce and establish relationships by allowing us to monitor visual cues sometimes lost over the phone. Conference calls are great, but studies show that when we can’t see who’s talking, we’re more likely to get distracted by our favorite apps. Not only is video conferencing an excellent way to increase focus during meetings, but it’s also a great method for keeping in touch with family and friends during the coronavirus pandemic.

In this tutorial you will learn how to create your own video conferencing app in Vue.js, using the Laravel framework and Twilio Programmable Video. There is no software to download, nor tutorials to follow. The final result is a link to a mobile-friendly video chat room, capable of hosting up to 50 participants.

NOTE: This tutorial is written specifically for Laravel 7+ (although it may work for previous versions).

Technical Overview and Prerequisites

When your application loads in the browser, a unique identifier is generated for the user to authenticate their connection to the chat room. Once verified by Twilio’s servers, the user will be added to the existing chat room.

Our project will use Vue.js and Laravel to generate the front and backend respectively.

In order to get started, you will need the following requirements set up and ready to go:

Let’s get started by creating a new Laravel project.

Create a new Laravel project

In order to create the base application, open the folder where you normally store your projects and run the following command:

$ laravel new laravel-video-chat-room && cd laravel-video-chat-room

Add the Twilio PHP SDK to the Project

We’ll need an easy method to connect to the Twilio Video API so that chat rooms and tokens can be created and authenticated for the users. Luckily, this method has already been created for us in the Twilio PHP SDK. Let’s add it as a requirement of our project using Composer.

$ composer require twilio/sdk

Create an API Endpoint to Generate Tokens

Creating APIs in Laravel is a pretty seamless process. As a modern web application framework, Laravel provides the necessary scaffolding to create the controller and route needed to generate tokens on the server-side.

Create a New Controller to Generate Access Tokens

A controller, or class that defines application logic, will need to be created to generate all user access tokens via the Twilio Video API. To begin, run the artisan command in your terminal for creating new controllers:

$ php artisan make:controller API/AccessTokenController

This command created a new file for us at app/Http/Controllers/API/AccessTokenController.php.

Open the newly created controller and take a moment to inspect it. You’ll notice that right now it’s empty. Outside of a couple of namespace declarations, the artisan command has generated an empty class.

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class AccessTokenController extends Controller
{
    //
}

We’re now ready to add a couple of namespaces to the controller that will autoload the Twilio SDK for use. Add the following lines of code beneath the use Illuminate\Http\Request; namespace.

use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VideoGrant;

The preceding declarations give us the support needed to actually connect to the Twilio Video API and generate an access token. Now, you need to create a method to utilize the classes declared.

Add the following generate_token method below to the AccessTokenController class:

public function generate_token()
    {
        // Substitute your Twilio Account SID and API Key details
        $accountSid = env('TWILIO_ACCOUNT_SID');
        $apiKeySid = env('TWILIO_API_KEY_SID');
        $apiKeySecret = env('TWILIO_API_KEY_SECRET');

        $identity = uniqid();

        // Create an Access Token
        $token = new AccessToken(
            $accountSid,
            $apiKeySid,
            $apiKeySecret,
            3600,
            $identity
        );

        // Grant access to Video
        $grant = new VideoGrant();
        $grant->setRoom('cool room');
        $token->addGrant($grant);

        // Serialize the token as a JWT
        echo $token->toJWT();
    }

You’ll notice the first three lines of our method search for environment variables to define our Twilio credentials. These lines reference three variables that we haven’t defined in our dotenv .env file yet. Let’s open the Twilio console to access them.

Twilio Account SID screenshot

You’ll need to copy the Account SID from your account dashboard. An API Key and API Secret will need to be created from your API Keys list.

Twilio API Key form

All three of these values will need to be copied to the .env file located in the project directory as follows:

TWILIO_ACCOUNT_SID="Insert your Account SID"
TWILIO_API_KEY_SID="Insert your API Key"
TWILIO_API_KEY_SECRET="Insert your API Secret"

The last step in generating an access token is to create a route (or API endpoint) that connects to our AccessTokenController. This route will provide a publicly accessible endpoint to the application logic we previously created.

Open routes/api.php and add the following declaration:

Route::get('access_token', 'API\AccessTokenController@generate_token');

This line of code registers the endpoint http://127.0.0.1:8000/api/access_token and responds to the GET verb.

To test our work thus far, run the following artisan command in your terminal:

$ php artisan serve

The serve command will load the PHP web server and deploy our Laravel application locally.

Install Laravel UI and Vue Packages

Because the back-end functionality is complete for our application, we are ready to scaffold the front end. As of version 6, Laravel has decoupled the JavaScript and CSS scaffolding from the application logic, so we will need to add it back using the new Laravel UI package. Once installed, it will open up our application to easily use any popular styling or scripting framework to modify the user interface and experience.

Run the following command in your terminal:

$ composer require laravel/ui

Now that the base package has been installed, we are ready to install the Vue.js front-end scaffolding. To install, run the following:

$ php artisan ui vue

Followed by:

$ npm install && npm run dev

In order to see what we’ve done and test that Vue is loaded and working, we need to update the Laravel blade responsible for displaying the homepage at resources/views/welcome.blade.php. Open the file and replace its contents with the following:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Laravel Video Chat</title>
        <link href="https://fonts.googleapis.com/css2?family=Oxygen&display=swap" rel="stylesheet">
        <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">

        <style>
            * {
                font-family: 'Oxygen', sans-serif;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <example-component></example-component>
        </div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

NOTE: Because our HTML has been modified, it needs to be compiled in order to see the changes. To see your changes run npm run dev again or npm run watch for live reloading.

This HTML will load the app.js file, which is responsible for transforming the #app div into the stage of our Vue.js UI. By default, the Laravel UI package installs an ExampleComponent which is seen on the line reading <example-component></example-component>. This sample Vue component simply displays some HTML rendered as:

Example Component
I'm an example component.

If you see this in your browser, then everything is working as expected!

Create a New Vue.js Component

Let’s create a new Vue component VideoChat.vue in the resources/js/components folder that will house our video chat room logic. This component will be responsible for connecting to the endpoint we created earlier and rendering the chat window.

After the component has been created, add the following code:

<template>
    <div class="p-5">
        <h1 class="text-2xl mb-4">Laravel Video Chat</h1>
        <div class="grid grid-flow-row grid-cols-3 grid-rows-3 gap-4 bg-black">
            <div id="my-video-chat-window"></div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'video-chat'
}
</script>

This component will just display the title “Laravel Video Chat”. Vue.js doesn’t recognize it yet so we will need to update the welcome.blade.php blade again to display our component.

Replace the <example-component></example-component> line in  resources/views/welcome.blade.php with <video-chat></video-chat>.

Lastly, open resources/js/app.js and add the following line of code underneath the ExampleComponent to globally define our new component:

Vue.component('video-chat', require('./components/VideoChat.vue').default);

Connect the VideoChat Component to the Route

Before we make an AJAX request to our access token endpoint, the Twilio Programmable Video JavaScript SDK needs to be installed. This SDK, written in JavaScript, is responsible for managing all of the API requests for Twilio Programmable Video.

In your terminal, install the Twilio Programmable Video JavaScript SDK by running the following command:

$ npm install --save twilio-video@2.0.0

Now that the SDK is installed, we can add the getAccessToken() method to connect to our endpoint.

Replace the code in VideoChat.vue with the following:

<template>
    <div class="p-5">
        <h1 class="text-2xl mb-4">Laravel Video Chat</h1>
        <div class="grid grid-flow-row grid-cols-3 grid-rows-3 gap-4 bg-black/]">
            <div id="my-video-chat-window"></div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'video-chat',
    data: function () {
        return {
            accessToken: ''
        }
    },
    methods : {
        getAccessToken : function () {

            const _this = this
            const axios = require('axios')
            
            // Request a new token
            axios.get('/api/access_token')
                .then(function (response) {
                    _this.accessToken = response.data
                })
                .catch(function (error) {
                    console.log(error);
                })
                .then(function () {
                    console.log( _this.accessToken )
                });
        }
    },
    mounted : function () {
        console.log('Video chat room loading...')

        this.getAccessToken()
    }
}
</script>

The code above defines an empty variable accessToken and assigns it an empty string. Once the component is mounted, getAccessToken() is called to request a new access token from http://127.0.0.1:8000/api/access_token. Upon a successful response, the data is assigned to accessToken.

We’re now ready to add the code which will connect us to the room we defined in our controller earlier and display a live feed from our local device.

Add the following code to the list of available methods after the getAccessToken method:

connectToRoom : function () {

            const { connect, createLocalVideoTrack } = require('twilio-video');

            connect( this.accessToken, { name:'cool room' }).then(room => {
                
                console.log(`Successfully joined a Room: ${room}`);

                const videoChatWindow = document.getElementById('video-chat-window');

                createLocalVideoTrack().then(track => {
                    videoChatWindow.appendChild(track.attach());
                });

                room.on('participantConnected', participant => {
                    console.log(`A remote Participant connected: ${participant}`);
                });
            }, error => {
                console.error(`Unable to connect to Room: ${error.message}`);
            });
        }

The connectToRoom method takes the accessToken we initialized in getAccessToken and creates a local video track upon successful authentication. You’ll notice that there’s also a placeholder for when a participant connects to the chat room. We’ll add that logic shortly.

For now, let’s call this method from the final then promise in getAccessToken as follows:

getAccessToken : function () {

            const _this = this
            const axios = require('axios')
            
            // Request a new token
            axios.get('/api/access_token')
                .then(function (response) {
                    _this.accessToken = response.data
                })
                .catch(function (error) {
                    console.log(error);
                })
                .then(function () {
                    _this.connectToRoom()
                });
        },

Finally, we’re ready to add the logic to display the other participants in the video chat. Replace the room.on('participantConnected', participant => { code with the following:

room.on('participantConnected', participant => {
                    console.log(`Participant "${participant.identity}" connected`);

                    participant.tracks.forEach(publication => {
                        if (publication.isSubscribed) {
                            const track = publication.track;
                            videoChatWindow.appendChild(track.attach());
                        }
                    });

                    participant.on('trackSubscribed', track => {
                        videoChatWindow.appendChild(track.attach());
                    });
                });

This function will listen for any new connections from remote participants, and upon connection, add them to the gallery of chat windows.

Testing

In case you haven’t done so in a while, you’ll need to re-compile the code for Vue to detect the changes we’ve made. Run npm run dev, make sure you’re running php artisan serve in a separate terminal, and refresh your browser.

Using two windows or browsers, load http://127.0.0.1:8000/ and wait for your participants to connect.

Laravel Video Chat

Conclusion

This tutorial has not only taught us how to implement Twilio’s Programmable Video, but it also helped us to develop a starter Video Chat application. If you’re interested in extending this code further, you could:

  • Display the number of participants
  • Hide the chat room behind a secure portal/wall
  • Add a text-chat feature

With all of the world turning towards remote work and video-conferencing, there’s no better time than now to have your own custom implementation. I can’t wait to see what you build!

Marcus Battle is Twilio’s PHP Developer of Technical Content where he prompts and rallies PHP developers to build the future of communications. You can download the full repo from Github or contact him for any questions regarding this tutorial at: