Create a Multi-user Presentation with Reveal.js

Share this article

Creating impressive presentation is an art. For a long time PowerPoint stood alone as the de facto tool for creating presentations. Now, things have changed, as the web has become the focal point for all businesses, and as browser capabilities improved tremendously. Modern browsers are now capable of rendering 3-D graphics and animations just like in any other native applications. Then there came some cool presentation libraries based on HTML5 and CSS3. Reveal.js is a highly popular library for creating stunning presentations. Websockets is a new standard defined as a part of HTML5 spec, which enables bi-directional, full-duplex communication in browsers. There are number of JavaScript libraries that make working with Websockets easier, of which Socket.IO is a prominent one. In this article we’ll discuss how to create a Reveal.js presentation that can be controlled by multiple users. We’ll make use of Socket.IO for sending and receiving slide change events in real-time. Reveal.js already comes with a multiplexer plugin, but this is a bit difficult to set up so we’ll ignore that for the time being. Let’s focus on how we can write a Socket.IO server that’ll suit our purpose.

Prerequisites

This article assumes that you have installed and can use the following libraries:
  • Node.js
  • Yeoman
  • Grunt
  • Bower

Initial steps

First we’ll set up an express.js server. Yeoman makes it easy to install and run express.js server with the help of generators. So we’ll first install the yeoman express-generator using npm.
$ npm install –g generator-express
This will install the express-generator in global scope. Now let’s set up the server.
$ yo express
This will ask you which type of express it should install. You can select Basic or MVC; in our case, we need only the basic setup. Then it will install bunch of npm modules along with bower.json and Gruntfile.js files. With the only necessary files, the app directory will look something like: ├── Gruntfile.js ├── app.js ├── bower.json ├── node_modules │ ├── express │ ├── grunt │ ├── grunt-contrib-watch │ ├── grunt-develop │ ├── jade │ └── request ├── package.json ├── public │ ├── components │ ├── css │ ├── img │ └── js └── views Now let’s fire up the express server using grunt.
$ grunt
Running "develop:server" (develop) task
>> started application "app.js".

Running "watch" task
Express server listening on port 3000
Yeoman has created a default app.js file for us, which contains necessary setup to run the server. Also, note that it comes with “watch” library, which will track the changes in the code and auto-reload the server, so that we don’t need to do it manually. Before we move further, we’ll install and setup reveal.js library using bower. Installing reveal.js is pretty simple and straightforward. Just issue the following command in terminal.
$ bower install reveal.js --save
This will fetch the latest stable version of reveal.js library from Github and will install under public/components directory. The --save option automatically updates the dependency section of bower.json file with reveal.js. Now we have everything that we need to create our presentation server. We’ll start by creating the first slide of our presentation. For this create an HTML file inside the views folder.
<!-- views/index.html -->
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Revealer - Reveal.js multiplexer</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <link rel="stylesheet" href="components/reveal.js/css/reveal.min.css">
        <link rel="stylesheet" href="components/reveal.js/css/theme/default.css" id="theme">
    </head>
    <body>
        <div class="reveal">
            <div class="slides">
                <section>
                    <h1>Revealer</h1>
                    <h3>Reveal.js multiplexer</h3>
                </section>
            </div>
        </div>
        <script src="components/reveal.js/js/reveal.min.js"></script>
        <script type="text/javascript">
            Reveal.initialize({history: true});
        </script>
    </body>
</html>
This is the most basic HTML we need to get started with reveal.js. Here we’ve included the Reveal.js CSS and JavaScript files. Reveal.initialize() will make the above HTML a nice looking presentation. Any section inside the div with the class slides will act as a slide. Before we can start the presentation we need to setup our server to serve this file upon request. So we will update the app.js with the code given below.
var express = require('express')
  , http = require('http')
  , path = require('path')
  , app = express();

app.configure(function(){
  app.use(express.static(path.join(__dirname, 'public')));
});

app.get('/', function(req, res){
  res.sendfile(__dirname + '/views/index.html');
});

var server = http.createServer(app).listen(3000, function(){
  console.log("Express server listening on port 3000");
});
The first few lines require the necessary dependencies for our server and then create an object of express. The next line configures the public folder as a static directory where the server will look for requests to static files. Then we add a route to serve the index.html file and start the server. Now we can see the presentation in the browser using http://localhost:3000/ url. But this is not what we really need. We need this presentation to be multiplexed, so that when one user changes the slide, it should be reflect on another user’s browser. Next we’ll install and set up the Socket.io module for enabling bi-directional communication using Websockets.
$ npm install socket.io --save
Once the installation is finished, we are ready to enable websockets in our presentation server. First require the socket.io library in the app.js file by adding the following line in variable declaration section.
var io = require(“socket.io”);
Now we need to pass the express server that we previously created to socket.io and then will tell the server to send a welcome message when a new client is connected.
io.listen(server);
io.sockets.on('connection', function (socket) {
  socket.emit("message", "Welcome to Revealer");
});
The server can respond to clients when they are connected. In the above code, the callback function to the connection event takes the client’s socket as an argument and sends a welcome message back to the client. Let’s move on to the client side JavaScript which will connect to this server. First we need to include the socket.io client library in our HTML.
<script src="/socket.io/socket.io.js"></script>
Next we’ll connect to the Websocket server that we’ve created.
var socket = io.connect("http://localhost:3000");
socket.on("message", function(data){
    console.log(data);
});
io.connect will connect to the server using the given URL. Upon connection, we know our server will respond with a welcome message, which we’ve logged into the console. Now that our client and server are ready and we can move on to the real stuff. When the presenter changes the slide, it should notify the server to update all other clients.
notifyServer = function(event){
    data = {
      indexv : Reveal.getIndices().v,
      indexh : Reveal.getIndices().h,
      indexf : Reveal.getIndices().f || 0
    }
    socket.emit("slidechanged" , data);
  }

  Reveal.addEventListener("slidechanged", notifyServer);

  Reveal.addEventListener("fragmentshown", notifyServer);

  Reveal.addEventListener("fragmenthidden", notifyServer);
When a slide change occurs, Reveal.js dispatches a slidechanged event. In the case of slide fragments it creates a fragmentshown or fragmenthidden event. We are handling all these cases here and when such an event occurs it will call the notifyServer callback function. At any point of time Reveal.getIndices() returns the current slide positions- horizontal, vertical and the fragment index. When the notifyServer function is called it will get the slide positions into a data object. Then the client will emit a slidechanged event to the server along with the created data. In the server side we need the ability to handle the slidechanged event emitted by the client, which should update all the connected clients. In order to do this, add the following code inside the connection handler.
socket.on("slidechanged", function(data){
   socket.broadcast.emit("slidechanged", data);
 });
socket.broadcast.emit will send the data to all clients except the sender. So here when server receives slidechanged
event, it will simply forward the slide data to all other clients. The client should also handle this slidechanged event forwarded by server, by moving to the respective slide or fragment. For this, in the client side add
socket.on('slidechanged', function (data) {
    Reveal.slide(data.indexh, data.indexv, data.indexf);
  });
Reveal.slide() takes three arguments, horizontal index, vertical index and the index of fragment, which will have value in the case of fragmentshown or fragmenthidden events.

Adding security

Now we have created a simple multiuser Reveal.js presentation. But this has a serious problem, in that any user can control the presentation. We can overcome this issue by adding a basic authentication in the server side code, and give an alternate route for non-authenticated users.
var masterUser = 'username'
      , masterPass = 'password';

// Authentication
var auth = express.basicAuth(masterUser, masterPass);

app.get('/', auth, function(req, res){
  res.sendfile(__dirname + '/views/master.html');
});

app.get('/client', function(req, res){
  res.sendfile(__dirname + '/views/client.html');
});
Now when a user requests “/” route, the browser will ask for the authentication credentials. express.basicAuth creates a basic authentication middlware which we have passed into the “/” route. If the login in successful it will send the master.html. Other users can use “/client” route to see the presentation where we won’t send any slide change events to server. The complete code will now look like this.
// server
var express = require('express')
  , http = require('http')
  , path = require('path')
  , ioServer = require('socket.io')
  , app = express()
  , masterUser = 'username'
  , masterPass = 'password';


app.configure(function(){
  app.use(express.static(path.join(__dirname, 'public')));
});

// Authentication
var auth = express.basicAuth(masterUser, masterPass);

app.get('/', auth, function(req, res){
  res.sendfile(__dirname + '/views/presentation.html');
});

app.get('/client', function(req, res){
  res.sendfile(__dirname + '/views/client.html');
});

var server = http.createServer(app).listen(3000, function(){
  console.log("Express server listening on port 3000");
});

var io = ioServer.listen(server);
io.sockets.on('connection', function (socket) {
  socket.emit("message", "Welcome to Revealer");
  socket.on("slidechanged", function(data){
    socket.broadcast.emit("slidechanged", data);
  });
});


//client
(function(){
  var host = 'http://localhost:3000',
    , socket = io.connect(host);
  Reveal.initialize({
    history: true
  });

  /** start - only in master.js **/
  notifyServer = function(event){
    data = {
      indexv : Reveal.getIndices().v,
      indexh : Reveal.getIndices().h,
      indexf : Reveal.getIndices().f || 0
    }
    socket.emit("slidechanged" , data);
  }
  // listeners for slide change/ fragment change events
  Reveal.addEventListener("slidechanged", notifyServer);
  Reveal.addEventListener("fragmentshown", notifyServer);
  Reveal.addEventListener("fragmenthidden", notifyServer);
  /** end - only in master.js **/

  // Move to corresponding slide/ frament on receiving 
  // slidechanged event from server
  socket.on('slidechanged', function (data) {
    Reveal.slide(data.indexh, data.indexv, data.indexf);
  });
  
})();
You can find all the source code on Github.

Summary

In this article we have seen how to build a simple Reveal.js presentation that can be controlled by more than one user. Here we have used Socket.IO library for updating all connected clients in real-time. We have also added a basic security for preventing un-authorized users from controlling the presentation. You can add more features and use technologies like WebRTC to make it more ubiquitous, so I hope you can see that this article is just a beginning.

Frequently Asked Questions about Creating Multi-User Presentations with Reveal.js

What is Reveal.js and why is it used for creating presentations?

Reveal.js is a powerful HTML framework used to create beautiful presentations. It is highly flexible and customizable, allowing users to create unique and interactive presentations. The presentations created with Reveal.js are web-based, making them easily accessible and shareable. Moreover, it supports touch navigation and embedded media, enhancing the overall presentation experience.

How can I create a multi-user presentation using Reveal.js?

Creating a multi-user presentation with Reveal.js involves setting up a Node.js server and using Socket.io for real-time bidirectional event-based communication. This allows multiple users to connect and interact with the presentation simultaneously. The detailed steps for setting up the server and creating the presentation are provided in the article.

Can I customize the look and feel of my Reveal.js presentation?

Yes, Reveal.js presentations are highly customizable. You can modify the CSS styles to change the appearance of your slides. Additionally, Reveal.js supports themes, which you can use to quickly change the look and feel of your presentation.

How can I add multimedia content to my Reveal.js presentation?

Reveal.js supports embedding multimedia content such as images, videos, and audio files. You can add these elements directly to your HTML slides. The framework also supports iframes, allowing you to embed external web content into your presentation.

Can I control the navigation of my Reveal.js presentation?

Yes, Reveal.js provides several options to control the navigation of your presentation. You can choose to navigate through your slides horizontally, vertically, or in a grid. You can also enable touch navigation for mobile devices.

How can I share my Reveal.js presentation with others?

Since Reveal.js presentations are web-based, you can easily share them by providing the URL. If you have set up a multi-user presentation, multiple users can connect and interact with the presentation simultaneously.

Can I use Reveal.js for offline presentations?

Yes, Reveal.js presentations can be exported as a standalone HTML file, which can be opened in a web browser without an internet connection. You can also export your presentation as a PDF for offline viewing.

Is it possible to add interactive elements to a Reveal.js presentation?

Yes, Reveal.js supports adding interactive elements such as quizzes, polls, and forms to your presentation. You can use HTML forms or integrate with third-party services to add these interactive elements.

Can I track the progress of my Reveal.js presentation?

Yes, Reveal.js provides a progress bar that shows the progress of your presentation. You can also use the API to track the current slide and other presentation details.

Is Reveal.js compatible with all web browsers?

Reveal.js is compatible with most modern web browsers, including Chrome, Firefox, Safari, and Edge. However, some features may not work in older browsers or certain mobile browsers.

Shameer CShameer C
View Author

Shameer is a passionate programmer and open-source enthusiast from Kerala, India. He has experience in web development using Scala, PHP, Ruby, MySQL, and JavaScript. While not working, Shameer spends his time coding personal projects, learning, watching screen casts, blogging, etc. His specific areas of interest include cloud computing, and system and database administration.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week