Irrespective of the size of your web application, a voice, and video chat feature is an addon that will not only allow your users to communicate in real time, have a face to face interaction or meeting without necessarily being in the same location or region but also improve the engagement and interactivity of your application. While the implementation of voice and video chat might sound so cool, trust me, you don’t want to build this from scratch. This is where an awesome tool like CometChat really shines.

So rather than build a backend for your chat application from scratch, you can easily build the entire functionalities using CometChat API which will enable you to build communication features like voice and video chat in real-time.

Together in this tutorial, we will build a voice and video chat application by leveraging on some of the API made available by CometChat. You will be able to run this application on two separate windows(browser) locally and have the ability to make, receive and reject a call amongst other things after a successful implementation. Once we are done, you will have built an application similar to:

Ongoing voice and video call

This application will be built with Vue.js and CometChat Pro SDK. The complete source code for this tutorial can be found here on GitHub if you will prefer to head straight into the code.

Getting started

To begin, we will create and install a new Vue.js application using an awesome tool named Vue CLI. This is a standard tool created by the team at Vue.js to help and allow developers to quickly scaffold a new project without hassle. Run the following command from the terminal to install it globally on your computer:

npm install -g @vue/cli

Once the installation is complete, proceed to use the vue command to create a new Vue.js project as shown here:

vue create comet-voice-video

Choose the “manually select features” options by pressing Enter on your keyboard and check the features you will be needing for this project by pressing space on your computer to select one. As shown below you should select Babel, Router, and Linter / Formatter:

Select the required features

For other instructions, type y to use history mode for router. Ideally, the default mode for Vue-Router is hash(#) mode as it uses the URL hash to simulate a full URL so that page won’t be reloaded when the URL changes. Choosing history mode here will help to get rid of the hash mode in order to achieve URL navigation without a page reload and add this configuration to the router file that will be generated automatically for this project. In addition, select ESLint with error prevention only in order to pick a linter / formatter config. Next, select Lint on save for additional lint features and save your configuration in a dedicated config file for future projects. Type a name for your preset, I named mine vuecomet:

 Save present terminal image

Immediately after the configuration, Vue CLI will start the installation of the application and install all its required dependencies in a new folder named comet-voice-video

Start the application

Now that the installation of the new application is completed, move into the new project and start the development server with:

// move into the app
cd comet-voice-video

// Start the server
npm run serve

View the welcome page of the application on http://localhost:8080:

Homepage of a new Vue.js application.

In addition, since we will be depending on CometChat Pro to easily build our application, let’s install the SDK before proceeding with video chat implementation. Stop the development server from running by hitting CTRL + C on your machine and run the following command from the project directory:

npm install @cometchat-pro/chat --save

Now we can easily import CometChat object wherever we want to use CometChat within our application like this:

import { CometChat } from '@cometchat-pro/chat';

Create the CometChat Pro account, APP ID and API Key

Since we will be leveraging on the hosted service of CometChat to successfully build our voice and video chat application, head over to the website and create a free CometChat Pro account. Fill in all the required information to setup a trial account.

Log in to view your CometChat dashboard and let’s create a new project. This will give us access to a unique APP ID and an API Key

CometChat Pro app dashboard.

In the ‘Add New App’ dialog, enter a name and click the plus sign to create a new application. Once you are done, click on the Explore button for the new app created. You will be redirected to a new page as shown below:

CometChat dashboard

Next, from the left side menu, go to “API Keys” tab. You will see a page similar to this:

List of automatically created API keys

Immediately after you create a new application from the dashboard, CometChatgenerates an API Key for the new demo application. This gives you full access to the functionality offered by CometChat. Don’t forget to note or better still, copy the automatically-generated Full access API Key and application ID as we will need these shortly.

Now that we are done setting up all the necessary tools and credentials needed to successfully create our application, we will start building properly in a bit.

What we want to achieve

Before we start building the application properly, let’s quickly discuss the application structure and how we intend to structure the flow.

Basically, we want users to log in from different locations and be able to chat using voice and video once we host our application on a live server, but for the sake of this tutorial, we will use two different windows locally. Once the user logs in:

List of automatically created API keys

We will redirect to a different page where he or she can input the UID of another user and start a video chat. Each user of CometChat is uniquely identified using his or her UID, you can consider this or relate it with a typical unique primary ID of the user from your database, which gives an opportunity to identify such user:

Start a video chat page

Initialize CometChat

To begin, the typical workflow when using CometChat requires that the settings for CometChat must be initialized by calling the init() method before any other method from CometChat. To start, create a new file named .env within the root directory of the application and paste the following code in it:

// .env
VUE_APP_COMMETCHAT_API_KEY=YOUR_API_KEY 
VUE_APP_COMMETCHAT_APP_ID=YOUR_APP_ID

This will make it very easy to reference and use our application credentials within our project. Don’t forget to replace YOUR_API_KEY and YOUR_APP_ID placeholder with the appropriate credentials as obtained from your CometChat dashboard.

Next, navigate to ./src/App.vue file which is the root component for Vue.js applications and replace its content with:

// ./src/App.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
import { CometChat } from "@cometchat-pro/chat";
import "./App.css";
export default {
  data() {
    return {};
  },
  created() {
    this.initializeApp();
  },
  methods: {
    initializeApp() {
      var appID = process.env.VUE_APP_COMMETCHAT_APP_ID;
      CometChat.init(appID).then(
        () => {
          console.log("Initialization completed successfully");
        },
        error => {
          console.log("Initialization failed with error:", error);
        }
      );
    }
  }
};
</script>

What we have done here is to include the functional component that will render any matched component for a given path from Vue Router. We will configure the router later in this tutorial. Next, within the section, we imported the CometChat object and a CSS file that we will create next. Lastly, we initialize CometChat by passing the application ID as a parameter.

Now create a new file named App.css within ./src/App.css and paste the following content in it:

// ./src/App.css
@import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';
@import 'https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css';
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}

#auth {
  width: 600px;
  margin: 0 auto;
}

#callScreen {
  width: 500px;
  height: 500px;
}

.home {
  width: 600px;
  margin: 0 auto;
}

We imported the CDN files for Bootstrap and Font awesome and then proceeded to add some default style for the application. Feel free to modify this content as you deem fit.

Login component

One of the key concept when building chat applications with CometChat is to ensure that users are authenticated before they can have access to use CometChat and start a chat. To ensure this, we will create a Login component that will handle the logic for authenticating a user and redirecting such user to the appropriate page for a chat.

To begin, create a new folder named auth within the views folder and within the newly created folder, create a new file and call it Login.vue. Open this new file and paste the following contents:

// ./src/views/auth/Login.vue

<template>
  <div id="auth">
    <div id="nav">
      <router-link to="/login">Login 
    </div>

    <p> Enter your username to start video chat </p>
      <p>Create an account through your CometChat dashboard or login with one of our test users (superhero1, superhero2)</p>
      <form v-on:submit.prevent="authLoginUser">
        <div class="form-group">
          <input name="username" id="username" class="form-control" placeholder="Enter your username" v-model="username"/>
        </div>
        <div class="form-group">
          <button type="submit" class="btn btn-success"> Login <span v-if="showSpinner" class="fa fa-spin fa-spinner"></span></button>
        </div>
      </form>
  </div>
</template>

<script>
  import { CometChat } from "@cometchat-pro/chat";
  export default {
    data() {
      return {
        username: "",
        showSpinner: false
      };
    },
    methods: {
      authLoginUser() {
        var apiKey = process.env.VUE_APP_COMMETCHAT_API_KEY;
        this.showSpinner = true;
        CometChat.login(this.username, apiKey).then(
          () => {
            this.showSpinner = false;
            this.$router.push({ name: "home" });
          },
          error => {
            this.showSpinner = false;
            console.log("Login failed with error:", error.code);
          }
        );
      }
    }
  };
</script>

What we have done here is pretty simple. First, we included an HTML form and added an input field that will accept the username of a user during the authentication process. Once the form is submitted, it will be processed using a method named authLoginUser()

Next within the <script> tag, we imported the CometChat object and created the authLoginUser() method attached to the form. Within the authLoginUser() method, we called the login() method from CometChat and passed our application API and the username to uniquely identify the user.

Once the user logs into the application, we used the Vue Router to redirect the user to the home page which will be handled by a different component name HomeComponent. We will create that in the next section.

Home component

In chronological order, what we want to achieve with this component is:

  • Display the UID of the currently logged in user
  • Initiate a video chat
  • Accept or reject a call
  • And finally be able to logout of the application.

Let’s start adding these functionalities. To begin, open ./src/views/Home.vue and replace its content with:

// ./src/views/Home.vue

<template>
  <div class="home">
    <div id="nav">
      <button class="btn btn-success" @click="logoutUser">Logout</button> 
    </div>
    <div class="form-group">
      <div class="form-group">
        <p>Welcome <b>{{ this.username}}</b>, your UID is <b>{{ this.uid }}</b> <br>
        Enter the receiver Id to start a chat </p>
        <p v-if="error">
          <b class="text-danger"> Receiver ID is required </b>
        </p>
        <input type="text" class="form-control" placeholder="Enter receiver UID" v-model="receiver_id">
      </div>

      <div v-if="incomingCall">
        <button class="btn btn-success" @click="acceptCall">Accept Call</button>  
        <button class="btn btn-success" @click="rejectCall">Reject Call</button> 
      </div>
      <div v-else-if="ongoingCall">
        <button class="btn btn-secondary"> Ongoing Call ... </button>
      </div>
      <div v-else>
        <button  @click="startVideoChat" class="btn btn-secondary"> Start Call <span v-if="showSpinner" class="fa fa-spin fa-spinner"></span> </button>
      </div>
    </div>
    <div id="callScreen"></div>
  </div>
</template>

Here, we added contents to display the username and the UID of the logged in user. Next we included an input field that will accept the UID of the receiver in order to uniquely identify such user and be able to initiate or send a call request.

Once a call request has been received, the receiver has an option of either rejecting or accepting the call. We have included click events for this process and will define the methods next.

Get the logged in user

First, let’s retrieve the details of the currently logged in user by adding this <script> section to our code. Place the contents below within the Home.vue immediately after the closing tag of the <template> section:

// ./src/views/Home.vue

<script>
import { CometChat } from "@cometchat-pro/chat";
export default {
  name: "home",
  data() {
    return {
      username: "",
      uid: "",
      session_id: "",
      receiver_id: null,
      error: false,
      showSpinner: false,
      incomingCall: false,
      ongoingCall: false
    };
  },
  created() {
    this.getLoggedInUser();
  },
  methods: {
    getLoggedInUser() {
      var user = CometChat.getLoggedinUser().then(
        user => {
          this.username = user.name;
          this.uid = user.uid;
        },
        error => {
          this.$router.push({ name: "homepage" });
        }
      );
    },
  }
};
</script>

Basically, we imported the CometChat method, defined some properties and corresponding initial values within the data option. And finally, once the component is created, we called a method named getLoggedInUser() to automatically retrieve the details of a logged in user and update the view with it.

Start video chat

To initiate a chat from the app, we will add this method to the function associated with the Vue instance:

// ./src/views/Home.vue

<script>
import { CometChat } from "@cometchat-pro/chat";
export default {
...
  methods: {
    ...
   startVideoChat() {
      if (!this.receiver_id) this.error = true;
      this.showSpinner = true;
      var receiverID = this.receiver_id;
      var callType = CometChat.CALL_TYPE.VIDEO;
      var receiverType = CometChat.RECEIVER_TYPE.USER;
      var call = new CometChat.Call(receiverID, callType, receiverType);
      CometChat.initiateCall(call).then(
        outGoingCall => {
          this.showSpinner = false;
          console.log("Call initiated successfully:", outGoingCall);
          // perform action on success. Like show your calling screen.
        },
        error => {
          console.log("Call initialization failed with exception:", error);
        }
      );
    }
  }
};
</script>

This method will be used to specify the type of a call and that of a receiver. For our application, this will be a CALL_TYPE.VIDEO and RECEIVER_TYPE.USER respectively. Finally, we passed these details through an object of call into the initiateCall() method from the CometChat API to send a call request.

Listen and receive calls

Once a call has been initiated, we need to listen to the call events within our application and based on the actions that will be carried out by the user to either receive or reject the call, we will call the appropriate method. To do this successfully, we need to register the CallListener listener using the addCallListener() method from CometChat. Add the following contents within the created() method:

// ./src/views/Home.vue

<script>
import { CometChat } from "@cometchat-pro/chat";
export default {
...
  created() {
  ...
  let globalContext = this;
    var listnerID = "UNIQUE_LISTENER_ID";
    CometChat.addCallListener(
      listnerID,
      new CometChat.CallListener({
        onIncomingCallReceived(call) {
          console.log("Incoming call:", call);
          globalContext.incomingCall = true;
          globalContext.session_id = call.sessionId;
        },
        onOutgoingCallAccepted(call) {
          console.log("Outgoing call accepted:", call);
          globalContext.ongoingCall = true;
          CometChat.startCall(
            call.sessionId,
            document.getElementById("callScreen"),
            new CometChat.OngoingCallListener({
              onUserJoined: user => {
                /* Notification received here if another user joins the call. */
                console.log("User joined call:", user);
                /* this method can be use to display message or perform any actions if someone joining the call */
              },
              onUserLeft: user => {
                /* Notification received here if another user left the call. */
                console.log("User left call:", user);
                /* this method can be use to display message or perform any actions if someone leaving the call */
              },
              onCallEnded: call => {
                globalContext.ongoingCall = false;
                globalContext.incomingCall = false;
                /* Notification received here if current ongoing call is ended. */
                console.log("Call ended:", call);
                /* hiding/closing the call screen can be done here. */
              }
            })
          );
          // Outgoing Call Accepted
        },
        onOutgoingCallRejected(call) {
          console.log("Outgoing call rejected:", call);
          this.incomingCall = false;
          this.ongoingCall = false;
          this.receiver_id = "";
          // Outgoing Call Rejected
        },
        onIncomingCallCancelled(call) {
          console.log("Incoming call calcelled:", call);
        }
      })
    );
  },
  methods: {
    ...
  }
};
</script>

Once the recipient accepts an outgoing call in the onOutgoingCallAccepted() callback of the CallListener, we called another method startCall() which takes the

  • sessionId: The unique session ID available in the CometChat.Call object,
  • callScreen: DOM element where you want to show the call user interface and
  • OngoingCallListener: a CometChat.OngoingCallListener where the real-time events will be received

as parameters.

Accept and reject calls

Once an incoming call event is received, we will call a method named acceptCall(). This method also takes the unique session ID available in the CometChat.Call object as a parameter. Add this method immediately after the startVideoChat() method:

// ./src/views/Home.vue

<script>
import { CometChat } from "@cometchat-pro/chat";
export default {
...
  methods: {
    ...
   acceptCall() {
      let globalContext = this;
      this.ongoingCall = true;
      this.incomingCall = false;
      var sessionID = this.session_id;
      CometChat.acceptCall(sessionID).then(
        call => {
          console.log("Call accepted successfully:", call);
          console.log("call accepted now....");
          // start the call using the startCall() method
          console.log(globalContext.ongoingCall);
          CometChat.startCall(
            call.sessionId,
            document.getElementById("callScreen"),
            new CometChat.OngoingCallListener({
              onUserJoined: user => {
                /* Notification received here if another user joins the call. */
                console.log("User joined call:", user);
                /* this method can be use to display message or perform any actions if someone joining the call */
              },
              onUserLeft: user => {
                /* Notification received here if another user left the call. */
                console.log("User left call:", user);
                /* this method can be use to display message or perform any actions if someone leaving the call */
              },
              onCallEnded: call => {
                /* Notification received here if current ongoing call is ended. */
                console.log("Call ended:", call);
                globalContext.ongoingCall = false;
                globalContext.incomingCall = false;
                /* hiding/closing the call screen can be done here. */
              }
            })
          );
        },
        error => {
          console.log("Call acceptance failed with error", error);
          // handle exception
        }
      );
    }
  }
};
</script>

Here, once an incoming call has been accepted, we then called the startCall() method. With this in place, the sender and the recipient can start a chat. Next, add another method to reject calls:

// ./src/views/Home.vue

<script>
import { CometChat } from "@cometchat-pro/chat";
export default {
...
  methods: {
    ...
    rejectCall() {
      var sessionID = this.session_id;
      var globalContext = this;
      var status = CometChat.CALL_STATUS.REJECTED;
      CometChat.rejectCall(sessionID, status).then(
        call => {
          console.log("Call rejected successfully", call);
          globalContext.incomingCall = false;
          globalContext.ongoingCall = false;
          globalContext.receiver_id = "";
        },
        error => {
          console.log("Call rejection failed with error:", error);
        }
      );
    }
  }
};
</script>

And lastly, add another method to Log out users from the application and redirect back to the login page:

// ./src/views/Home.vue

<script>
  import { CometChat } from "@cometchat-pro/chat";
  export default {
    ...
    methods: {
      ...
      logoutUser() {
        CometChat.logout().then(
          success => {
            console.log("Logout completed successfully");
            this.$router.push({ name: "homepage" });
            console.log(success);
          },
          error => {
            //Logout failed with exception
            console.log("Logout failed with exception:", { error });
          }
        );
      } 
    }
  };
</script>

The CometChat.logout() will log the user out of CometChat and also from our application.

In case you miss anything, the complete contents of the Home.vue file can be found here.

Update router

Earlier, when we installed Vue.js, we also selected an option of installing Vue Router with it. So open ./src/router.js and replace the default content in it with the following:

// ./src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Login from './views/auth/Login.vue'
Vue.use(Router)
export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      redirect: 'login',
      name: 'homepage'
    },
    {
      path: '/login',
      name: 'login',
      component: Login,
    },
    {
      path: '/home',
      name: 'home',
      component: Home
    }
  ]
})

What we have done here is to map each route within our application to the respective component that will handle the logic. This is a basic format of configuring Vue Router for a Vue.js application. Click here to find out more about Vue Router.

Test the application

Now that you are done adding all the required logic and components, you can now run the application and test it out. Restart the application with npm run serve and navigate to http://localhost:8080 to view the app.

 Log-in page for the chat applicatiom.

Open the application in two separate windows and login with any two of the test users: superhero1, superhero2, superhero3, superhero4 or superhero5

 Initiate a call request

Once you are able to log in from both window, enter the UID of one user and click on Start Call and once the call request is received by the other user, two new action buttons will be displayed. Click Accept Call to accept a call

or Reject Call to reject a call:

If you have followed the tutorial diligently up until now, then you should have a functional voice and video chat application running on your computer.

Conclusion

As seen in this tutorial, you will agree with me that it is so easy to quickly implement a video chat feature in your project. This can be easily integrated into an existing Vue.js application or a new one entirely.

Feel free to explore the source code and add more features as you deem fit.

With CometChat, you can easily extend the functionalities of this application by adding more awesome features. Check the official documentation of CometChat Pro to read more about what its got to offer.

I do hope you found this tutorial quite helpful? Can’t wait to see what you will build with the knowledge that you have acquired here.