How to Build a Real Time MMS Photostream with Twilio and Socket.IO

November 13, 2014
Written by

Selection_142

Since our launch a few weeks ago of Twilio MMS, your friendly neighborhood devangel crew has been busy having a barrel of monkeys building sick hacks on this new medium.  From a tasty mustacher to an on demand GIF generator to a Instagram-for-every-phone, thousands of people have created hilarious photos using Twilio MMS.

Sharing these on a big screen for a room full of folks to see was one of the first requests we received. With Ricky, Kevin and Carter kicking out tons of tasty node.js tutorials, I thought I’d dust off my own JavaScript chops and try my hand at solving this with Express. Today we’re going to build a photostream we can project on a wall that updates in real time based on MMS we send to a Twilio phone number.

How It Works

Our real time photostream hack will have two components – a Node.js server running Express and then a web browser client.  Users will text our Twilio phone number which will make a POST request to our Node.js app with the contents of the message, including a link to the photo.  Our Node.js app will then emit the url to the client using socket.io, after which our client will add the image to a Photobox photo gallery.

Here’s what it’ll look like by the end:

Want to skip to the code?  Check out the repo on GitHub. Want to see it in action yourself first?  Deploy this project right now on Heroku using the button below:


Deploy

What We Will Need

To get baking this real time photostream app, we’ll need a few ingredients.

Getting Started
Let’s get cracking on this hack.  First, we’ll need to get the environment up for our Node.js application.  We can use a package.json file to whip this up.  Navigate to the directory you wish to serve your Node app from and create a file called package.json.  Inside, we’ll define our app’s dependencies:

{
  "name": "realtime-mms-photostream",
  "version": "0.0.1",
  "description": "A quick real-time photo gallery of photos received from Twilio MMS.",
  "dependencies": {
    "express": "^4.10.2",
    "body-parser": "^1.9.2",
    "socket.io": "^1.2.0",
    "twilio": "^1.7.0"
  }
}

Then we’ll use npm to install the dependencies for us by running a command in the root of that directory:

npm init

Next, we need to create a subdirectory where we’ll keep the index.html.  That file will serve our users for our photostream.  It will also store the stylesheets and JavaScript files for Photobox, Yair Even-Or’s slick jQuery photo gallery we’ll use to display the Twilio MMS we receive.  First we’ll create the directory:

mkdir static

Then we’ll download Photobox and place the project’s photobox directory in our subdirectory static.

Finally, we need to point our Twilio MMS-enabled phone number to the address hosting our Node.js app.  We’ll first navigate to our numbers tab in our Account Dashboard, then click on our MMS-enabled number.  We’ll then put our domain in under Messaging Request URL and add the endpoint we’ll create for this app: /message.

 

With our Node environment setup, Photobox installed and our Twilio phone number wired up, we’re ready to get hacking.  Note: If you would prefer to develop this project locally, consider using a tunnel like ngrok to allow Twilio to connect to your app.  Here’s a great guide on how to get started.

Building The Server

Our real time photostream hack is going to have two components – a client and a server.  First let’s write the server, which will leverage the Express framework for node.js.  We’ll start by opening a file called app.js.

First, we’ll include all our dependencies that we’ll need for our app.

// Require dependencies
var http = require('http');
var path = require('path');
var express = require('express');
var twilio = require('twilio');
var bodyParser = require('body-parser');

Next, we’ll create our app object, create an HTTP server with that app object and extend our HTTP server with socket.io for the tasty real time stream effect.

// Create Express app and HTTP server, and configure socket.io
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);

 

Next, we’ll configure our app to use body-parser middleware so we can access the parameters Twilio POSTs to our app when our phone number receives an MMS.  We’ll also configure our app to use the static subdirectory we created in the Getting Started section to serve our static files.  Finally, to make deployment to PaaS services like Heroku easy, we’ll check if a PORT environment variable is set and store that value for our HTTP server to use later.  If PORT isn’t set, we’ll just use port 3000.

// Middleware to parse incoming HTTP POST bodies
app.use(bodyParser.urlencoded({ 
    extended: true 
}));

// Serve static content from the "static" directory
app.use(express.static(path.join(__dirname, 'static')));

// Configure port for HTTP server
app.set('port', process.env.PORT || 3000);

Now we need to define the endpoint we pointed our Twilio phone number to in the Getting Started section.  As we mentioned, we’ll be using an endpoint called /message to receive the POSTs Twilio will send to our app when our phone number receives a text message.  We’ll check the message to see if it has any photos.  If so, we’ll emit them to our web browser client using socket.io.  If not, we’ll let the user know we didn’t receive any photos in the message.

// Handle incoming MMS messages from Twilio
app.post('/message', function(request, response) {
    console.log('Received message.');
    var twiml = twilio.TwimlResponse();
    var numMedia = parseInt(request.body.NumMedia);

    if (numMedia > 0) {
        for (i = 0; i < numMedia; i++) {
            var mediaUrl = request.body['MediaUrl' + i];
            console.log('Displaying MediaUrl: ' + mediaUrl);
            io.emit('newMedia', mediaUrl);
        }
        twiml.message('Photo received - check the screen to see it pop up!');
    } else {
        twiml.message(':( Doesn\'t look like there was a photo in that message.');
    }

    response.type('text/xml');
    response.send(twiml.toString());
});

With our web endpoints in place, we just need to add a little code to send a notification to the web browser client that it successfully connected to our app over socket.io and then some boilerplate to launch our app.

io.on('connection', function(socket){                                            
    socket.emit('connected', 'Connected!');                              
});

server.listen(app.get('port'), function() {
    console.log('Express server listening on *:' + app.get('port'));
});

Blam-bliggity! We’re done with our server.  Let’s see all the pieces put together.

// Require dependencies
var http = require('http');
var path = require('path');
var express = require('express');
var twilio = require('twilio');
var bodyParser = require('body-parser');

// Create Express app and HTTP server, and configure socket.io
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);

// Middleware to parse incoming HTTP POST bodies
app.use(bodyParser.urlencoded({ 
    extended: true 
}));

// Serve static content from the "static" directory
app.use(express.static(path.join(__dirname, 'static')));

// Configure port for HTTP server
app.set('port', process.env.PORT || 3000);

// Handle incoming MMS messages from Twilio
app.post('/message', function(request, response) {
    console.log('Received message.');
    var twiml = twilio.TwimlResponse();
    var numMedia = parseInt(request.body.NumMedia);

    if (numMedia > 0) {
        for (i = 0; i < numMedia; i++) {
            var mediaUrl = request.body['MediaUrl' + i];
            console.log('Displaying MediaUrl: ' + mediaUrl);
            io.emit('newMedia', mediaUrl);
        }
        twiml.message('Photo received - check the screen to see it pop up!');
    } else {
        twiml.message(':( Doesn\'t look like there was a photo in that message.');
    }

    response.type('text/xml');
    response.send(twiml.toString());
});

io.on('connection', function(socket){                                            
    socket.emit('connected', 'Connected!');                              
});

server.listen(app.get('port'), function() {
    console.log('Express server listening on *:' + app.get('port'));
});

Now on to writing the client.

Building the Client

After we complete writing our server, we’re ready to write our web browser client.  First, we’ll open static/index.html and block out the skeleton of our HTML page.  In it, we’ll link to the stylesheet for Photobox and include the JavaScript files for the same, jQuery and socket.io.  We’ll also create a div with the id media_urls to serve as the DOM container for our Photobox gallery.

<!DOCTYPE html>
<html lang='en'>
  <head>
    <title>Real Time Twilio MMS Stream</title>
    <meta content='width=device-width, initial-scale=1' name='viewport'>
    <link rel="stylesheet" href="/photobox/photobox.css">
  </head>
  <body>
    <div id='media_urls'>
        <h1>Send an MMS to start the photostream.</h1>
    </div>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src='/photobox/jquery.photobox.js'></script>
    <script src="/socket.io/socket.io.js"></script>
  </body>
</html>

Next, we’ll add the guts of our client in a script tag as the last element before we close our .  First, we’ll define two functions – one to create markup for an image based on a Twilio MMS Media Url and another to start our photo gallery.  Next, we’ll instantiate an object that will serve as our socket.io client called io.    Then, we’ll create two socket events – one for when we connect to the server and another when we receive a newMedia event emitted by our server when we receive Twilio MMS.  Finally, we will use $(document).ready from jQuery to turn our media_urls div into our Photobox instance.

<script>
      function createImage(url) {
          var markup = '<a href="' + url + '"><img src="' + url + '" /></a>';
          return markup;
      }       
      function startGallery() {
         $('#media_urls h1').hide();
         $('#media_urls a:first').click(); 
      }
      var socket = io();
      socket.on('connected', function(socket) {
        console.log("Connected!");
      });
      socket.on('newMedia', function(url){
          console.log("Adding new media...");
          var markup = createImage(url);
          $('#media_urls').prepend(markup);
          window.setTimeout(startGallery, 500);
      });
      $(document).ready(function() {
        $('#media_urls').photobox('a', {autoplay: true, loop: true});
      });
    </script>

Finally to make the presentation a little tidier, let’s add a style in our to hide the display of any of the anchor elements we create with each image we receive.

<style>
        #media_urls a { display: none; }
    </style>

Ba-donk! Our client is complete.  Let’s see what it looks like together.

<!DOCTYPE html>
<html lang='en'>
  <head>
    <title>Real Time Twilio MMS Stream</title>
    <meta content='width=device-width, initial-scale=1' name='viewport'>
    <link rel="stylesheet" href="/photobox/photobox.css">
    <style>
        #media_urls a { display: none; }
    </style>
  </head>
  <body>
    <div id='media_urls'>
        <h1>Send a MMS to start the photostream.</h1>
    </div>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src='/photobox/jquery.photobox.js'></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      function createImage(url) {
          var markup = '<a href="' + url + '"><img src="' + url + '" /></a>';
          return markup;
      }       
      function startGallery() {
         $('#media_urls h1').hide();
         $('#media_urls a:first').click(); 
      }
      var socket = io();
      socket.on('connected', function(socket) {
        console.log("Connected!");
      });
      socket.on('newMedia', function(url){
          console.log("Adding new media...");
          var markup = createImage(url);
          $('#media_urls').prepend(markup);
          window.setTimeout(startGallery, 500);
      });
      $(document).ready(function() {
        $('#media_urls').photobox('a', {autoplay: true, loop: true});
      });
    </script>
  </body>
</html>
node app.js

Then we’ll open a web browser and visit the app using the URL we defined in our Twilio phone number.

Finally, let’s send an MMS to our Twilio phone number.  Then another.  Then another.  Then another!  Now if we look at our web browser again, holy biscuits – we have a real time photostream!

Take a look at the video below:

Next Steps

Killer – we just created a simple app to display photos we receive over Twilio MMS real time in a browser photostream.  Now what could we do next with this?  Here are a few thoughts:

  • Adjust our inbound endpoint to receive StatusCallback requests instead of inbound MMS, allowing us to display outbound MMS as well.
  • Add content moderation for public venues like billboards and tradeshows.
  • Superimpose captions that users send in as message bodies over the images themselves.

I’ve been having a lot of fun playing with Twilio MMS and indeed dusting off my JavaScript with Node.js.  What are you hacking on with Twilio and Node?  I want to hear about it – hit me up at rob [at] twilio [dot] com or @dn0t.