Tossing Text with Node.js and Socket.IO

Post by Josh Reed

One of my favorite quotes comes from Stephen King: “You can’t deny laughter; when it comes, it plops down in your favorite chair and stays as long as it wants.” I feel the same way about certain ideas. They stick around in the back of your mind until you can work them through to a logical conclusion. A recent example of this was an idea to use more physical gestures for interacting with applications and devices. I wanted to be ‘flick’ a message from my phone to a TV in the room so everyone could see the message:

Read on to see how I implemented it with Node.js, Socket.IO, and a bit of javascript.

The idea for the app was simple. We have a bunch of large monitors around the Refactr office. I wanted to set one up and allow anyone in the room to send messages to it for everyone to see. And since submitting forms with a button is so blasé, I figured it would be cool to hook into the accelerometer on the iPhone and allow people to physically ‘fling’ their messages to the screen.

The Server

Having done a bit of work with WebSockets in the past, they seemed like the perfect solution. Standing up a server that can talk the WebSocket protocol is dead simple with Node.js and Socket.IO. The first step was to install Node. I was off and running in a few minutes.

The next step was to install Socket.IO using Node’s package manager, npm. Socket.IO makes it possible to create realtime apps across browsers and devices by supporting WebSockets and other transports if WebSockets are not available. I also needed to serve up static content like images and CSS so I installed Express at the same time:

npm install socket.io express

With all the components installed, the code for the server is literally 20 lines with comments (app.js):

var express = require('express'), socketio = require('socket.io'), app = express.createServer(), io = socketio.listen(app); // serve our static resources app.configure(function() { app.use(express.static(__dirname + '/static')); }); // listen on port 4000 for testing app.listen(4000); // handle websocket connections io.sockets.on('connection', function(socket) { socket.on('message', function(data) { socket.broadcast.emit('message', data); socket.emit('message', data); }); }); 

This snippet creates a server listening on port 4000. Any requests for files will be served up out of the static directory. Any messages that come in from Socket.IO will be broadcast to all connected clients. That’s all we need for the server-side portion of the app.

The Client

The client-side is similarly simple using the Socket.IO JS library (index.html):

var socket = io.connect('http://' + window.location.hostname); // handle incoming messages from the socket function handleMsg(data) { // do something with the message like showing an alert } // send messages to the socket function sendMsg(data) { socket.emit("message", data); } socket.on('message', handleMsg); 

This is embedded in a simple HTML page that has a form for sending messages:

We add a little bit of Javascript to capture the form submit and send it down the socket instead of submitting it normally. When messages come in, we display them in a simple timeline view:

The Toss

With the form, we could send messages down the socket and they would be broadcast to all clients but it required pushing a button. We can do better than that! The iPhone implements the HTML5 DeviceOrientation and DeviceMotion APIs so we can listen for motion events and submit the form when we detect a throwing motion.

We may want to use this throwing motion in other apps so let’s write a simple JQuery plugin for detecting it. This will also give us an opportunity play around with custom events:

(function($){ if (window.DeviceMotionEvent) { var timeout; $(window).bind('devicemotion', function(e) { if (e.originalEvent.accelerationIncludingGravity.x > 16 || e.originalEvent.accelerationIncludingGravity.y > 16) { if (timeout) { window.clearTimeout(timeout); timeout = null; } timeout = window.setTimeout(function() { $(document).trigger('flick'); }, 250); } }); } })(jQuery); 

In the plugin, we add a listener for motion events. If we detect large values in the acceleration along the X or Y axes, we set a timer to fire in 250ms. The throwing motion will likely fire multiple motion events with large acceleration values so the timer collapses all of these into single event. When the timer fires, we trigger a custom flick event on the document. Back in our index.html file, we can listen for that flick and trigger the form submit:

$(document).bind('flick', function() { $('form').submit(); }); 

Wrapping it Up

This was a whirlwind tour of using Node.js, Socket.IO, and the HTML5 DeviceMotion API to build a novel info sharing app. It’s fun being able to type in a message and fling it at a monitor and have the message appear. It’s probably only a matter of time before this happens, though.

As a guy who spends most of his time working with Grails, it was fun to step outside my comfort zone see what all the buzz around Node.js is about. It was easy to get an app up and running quickly. I’d definitely consider using it again on a future project.

The full working code for the app is available in the Refactr Open Source repo out at Github. The spiffy background image in the app was made by the talented Mr. Messall.

This entry was posted in Software Development and tagged , , , , . Bookmark the permalink.

You may also like:

2 Responses to Tossing Text with Node.js and Socket.IO

  1. Is it easier to create applications with Node?

  2. Josh Reed says:

    James,

    I did appreciate how quickly I was able to get up to speed but I don’t feel like I have enough experience to answer that. This was a fun little app but it wasn’t a production ready app. I’d like to take some time and explore more, though.

    Cheers,
    Josh

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>