Simulate a Streaming Event in NuxtJS

Banner for a MediaJam post

Amarachi Iheanacho

YouTube is an essential piece of popular culture, with the addition of the live events feature consolidating that fact. Live events allow people from all over the world to watch videos in real-time.

In this tutorial, we will be walking through creating a YouTube live-event simulator in Nuxt.

What we will be building

Similar to live YouTube events, our video starting mark for a user is adjusted based on how much time has passed since the video started. For example, a video that is 1 hour long but started 45 minutes ago would have any new user that visits the page automatically begin at the 45-minute mark in the video time.

When the recorded video is complete, we want an overlay with the text ‘The live has ended’.


We completed the project in a CodeSandbox. To get started quickly, fork the CodeSandbox.

GitHub Repository:


To get the most out of this article, we require the following:

  • A basic understanding of CSS, JavaScript, and Vue.js
  • Node and its package manager, npm. Run the command node -v && npm -v to verify we have them installed, or install them from here. We recommended the latest version. Alternatively, we can use another package manager, Yarn.
  • Understanding Nuxt would help us follow this tutorial quicker, but it is not entirely required.
  • A Cloudinary account, create a free account here.

Setting up the Nuxt app

Nuxt is an open-source vue.js frontend development framework that allows us to seamlessly create web applications. The rendered web pages are static Vue.js applications without a server.

We navigate to the preferred directory to create our Nuxt app and run this terminal command.

NOTE: Use the PowerShell terminal to avoid arrow issues on Windows.

1npm init nuxt-app <project-name>
3 #or
5 npx create-nuxt-app <project-name>
7 #or
9 yarn create nuxt-app <project-name>

Running this command will trigger a set of question prompts and we recommend the following defaults.

Nuxt question prompt

Next, we change the directory to the project and start a development server with:

1cd <project name>
3 npm run dev
5 #or
7 yarn dev

To see our app, go to http://localhost:3000/

Nuxt indexjs

Installing the Cloudinary dependency

Cloudinary is a cloud-based service that provides an end-to-end image and video management solution, including uploads, storage, manipulations, optimizations, and delivery.

To enable the Nuxt app to use these Cloudinary features, we will add the video player assets in the head section of the nuxt.config.js file in the root directory.

1// nuxt.config.js
3 export default {
4 head: {
5 ...
6 link: [
7 ...
8 { rel: 'stylesheet', href: '' }
9 ],
10 script: [
11 { src: '' },
12 { src: '' },
13 ],
14 },
16 };

Next up, we create a .env file at the root of the project to store environment variables.

1touch .env

After creating the .env file, we go to our Cloudinary Dashboard, in the Account Details section, we copy and paste our Cloud Name in our .env file.

Cloudinary Dashboard

1CLOUD_NAME = <Cloudinary-cloud-name>

Creating the video player

In the index.vue file, we embed the Cloudinary video player in our project using the native HTML5 video element.

2 <div>
3 <video
4 id= "video-player"
5 class="cld-video-player"
6 >
7 </video>
8 </div>
9 </template>

In Vue’s mounted lifecycle hook, we create a Cloudinary instance. Doing this in the mounted lifecycle hook allows the instance to be created once the app mounts.

1// pages/index.vue
6 export default {
7 data(){
8 return{
9 cld: null,
10 player: null,
11 video: "videoplayback_1_pr2hzi",
12 controls: true
13 }
14 },
15 mounted(){
16 this.cld={
17 cloud_name: process.env.CLOUD_NAME,
18 secure: true
19 })
20 this.player = this.cld.videoPlayer(
21 'video-player', {
22 controls: this.controls,
23 autoplay: true
24 }
25 );
26 this.player.source(;
27 }
29 }
32 </script>

We define four variables in our data object:

  • The cld variable holds the Cloudinary instance we will create.
  • The player variable holds the Cloudinary video player we will instantiate on mount.
  • The video variable holds the video's public ID.
  • The controls variable controls the native player controls.

In our mounted lifecycle hook, we create a Cloudinary instance by passing the cloud name we stored in our .env file and secure: true into the Cloudinary object.

Following that, we instantiate the video player with the Cloudinary videoPlayer method. This method receives two arguments.

  • The video element ID.
  • An object that sets our autoplay to true and the controls to the controls variable.

Now that we have created our player, we specify what video to play using the Cloudinary source() method.

Next, we add our video player styles in the style section of our index.vue file.

1<style scoped>
2 div{
3 margin: 0;
4 padding: 0;
5 display: flex;
6 align-items: center;
7 justify-content: center;
8 }
9 .cld-video-player{
10 width: 500px;
11 height: 500px;
12 }
13 </style>

Our index.vue file should look like the code block below when we have completed this tutorial section.

Here is the embedded Cloudinary video player.

Cloudinary Video Player

Creating the live simulation

To have the video starting time mark adjusted on how much time has passed, we need to subtract the video's starting time from when the user visits the page.

In our index.vue file, we create four variables on the data object to simulate the live event. These variables are:

  • The duration variable is how long our video is in seconds.
  • The startTime variable holds the time in milliseconds our video starts playing. To get the start time, we use the JavaScript getTime() method.
  • The currentTime variable is the time in milliseconds the user visits the webpage.
  • The liveStartTime variable holds the difference between the startTime variable and the currentTime variable in seconds. This liveStartTime variable sets the seconds mark from which the user starts watching the video.
1export default {
2 data(){
3 return{
4 cld: null,
5 player: null,
6 video: "videoplayback_1_pr2hzi",
7 duration: null,
8 startTime: 1642870132613,
9 currentTime: null,
10 liveStartTime: null,
11 }
12 },
13 }

In our index.vue file, we create the functions that will affect the duration, currentTime, and the liveStartTime variables.

2 this.cld={
3 cloud_name: "amarachi-2812",
4 secure: true
5 })
6 this.player = this.cld.videoPlayer(
7 'video-player', {
8 controls: this.controls,
9 autoplay: true
10 }
11 )
12 this.player.source(;
14 // Gets the duration of the video
15 this.player.on('loadeddata', () => {
16 this.duration = this.player.duration();
17 })
18 // Gets the current time at which the user opens the video
19 const d = new Date ()
20 this.currentTime = d.getTime();
21 // Gets the difference betwewn when the user opened the video and when the video started
22 const difference = this.currentTime - this.startTime
23 // Changes the time from millisecond to seconds.
24 this.liveStartTime = difference / 1000;
26 }

In the code block above, we do the following:

  • Update the duration variable with the video's duration after loading our video data.
  • Update the currentTime variable with the date and time in milliseconds a user opens our app.
  • Update the liveStartTime variable with the difference between the startTime and the currentTime variable in seconds.

Next, we set the time mark the video starts playing for a user.

1// Plays the video from a particular time
3 this.player.on('play', ()=> {
4 this.player.currentTime(this.liveStartTime);
5 })

We use the Cloudinary currentTime method to set the video starting mark anytime we call the play video event in the code block above.

Creating the overlay and selectively rendering it.

We create our overlay in the template section of our index.vue file.

1<div class="overlay">
2 The live has ended.
3 </div>

Our overlay is incomplete without the styling. We add these styles in the style section of our index.vue file.

1<style scoped>
3 @import url('');
5 h2{
6 font-size: 20px
7 }
8 .overlay{
9 width: 500px;
10 height: 500px;
11 position: absolute;
12 background-color: rgba(0,0,0, 0.70);
13 color: #fff;
14 font-size: 16px;
15 top: 7px;
16 bottom: 0px;
17 }
18 </style>

Selectively rendering the overlay.

We create an overlay variable in the index.vue file.

2 return{
3 ...
4 overlay: false
5 }
6 }

Next, in the template section of our index.vue file, we use this overlay variable to conditionally render the overlay.

1<div class="overlay" v-if = 'overlay'>
2 The live has ended.
3 </div>

In our index.vue file, we create an onEnded function on the method object. This function changes our overlay variable to true and our controls variable to false.

1export default {
2 data() {
3 return {
4 ...
5 };
6 },
7 methods: {
8 onEnded() {
9 this.overlay = true;
10 this.controls = false;
11 },
12 },
13 mounted() {
14 ...
15 },
16 };

In the mounted section of our index.vue we write this code.

1// Controls the overlay
2 this.player.on('ended', ()=> {
3 this.onEnded()
4 })
6 if (this.liveStartTime > this.duration){
7 this.onEnded()
8 }

The code block above does the following:

  • Call the onEnded function when the video ends.
  • Checks if the liveStartTime variable is greater than the duration . If it is, we call the onEnded function.

Finally, we set our overlay variable to false when we start playing the media.

1this.player.on("play", () => {
2 this.player.currentTime(this.liveStartTime);
3 this.overlay = false;
4 });

Here is how our index.js file looks.

Here is how our live event simulator looks.

Nuxtjs Live event simulator

We can update the livestream start time in the code to simulate the playback in any time.


This article discussed what Cloudinary is and how to handle player events with it to simulate a live event.


Here are some resources that might be helpful:

Amarachi Iheanacho

Frontend Engineer and Technical Writer