WebSockets Draw EM

This example implements a shared canvas where multiple clients can draw lines. The communication of the mouse events happens throught the websockets. The server is built with the EM-Websocket gem and uses the channel functionality of the ruby event machine (see EventMachine::Channel ).

Gemfile
source "http://rubygems.org"

gem "haml"
gem 'em-websocket'
gem 'sinatra'
Terminal
ruby server.rb

websocketsDrawEM/server.rb
require 'em-websocket'
require 'json'

EventMachine.run {
  @channel = EM::Channel.new
  @users = {}
  @messages = []

  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
      
    ws.onopen {
      #Subscribe the new user to the channel with the callback function for the push action
      new_user = @channel.subscribe { |msg| ws.send msg }
      
      #Add the new user to the user list
      @users[ws.object_id] = new_user
      
      #Push the last messages to the user
      @messages.each do |message|
        ws.send message
      end
   }

    ws.onmessage { |msg|
      
      #append the message at the end of the queue
      @messages << msg
      @messages.shift if @messages.length > 10

      #Broadcast the message to all users connected to the channel
      @channel.push msg
    }

    ws.onclose { 
      @channel.unsubscribe(@users[ws.object_id])
      @users.delete(ws.object_id)
    }
  end

  #Run a Sinatra server for serving index.html
  class App < Sinatra::Base
    set :public_folder, settings.root
    
    get '/' do
      send_file 'index.html'
    end
  end
  App.run!
} 
websocketsDrawEM/index.html.haml
!!! 5
%html
  %head
    %meta(charset="utf-8")
    %meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible")
    %meta(name="viewport" content="width=device-width, initial-scale=1")  
    %link(rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css")
    %script(type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js")
    %script(type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js")
    %title
      WebSockets Drawing
  %body
    :javascript
      var WebSocket = window.WebSocket || window.MozWebSocket;

      $(function() {

        var socket = new WebSocket("ws://" + location.hostname + ":8080");
        
        var currentX = 0;
        var currentY = 0;
        var lastX;
        var lastY;
        var lastReceivedX;
        var lastReceivedY;
        
        var ctx = $('#canvas')[0].getContext('2d');

        $('#canvas').bind('vmousemove',function(ev){
          ev = ev || window.event;
          currentX = ev.pageX || ev.clientX;
          currentY = ev.pageY || ev.clientY;
        });
        
        socket.onopen = function(event) {
          setInterval(function(){
            if(currentX !== lastX || currentY !== lastY){
              lastX = currentX;
              lastY = currentY;
              socket.send( JSON.stringify({x:currentX, y: currentY}) );
            }
          }, 30); // send every 300 milliseconds if position has changed
        }    
        socket.onmessage = function(event) {
          var msg = $.parseJSON(event.data);
          
          ctx.beginPath();
          ctx.moveTo(lastReceivedX,lastReceivedY);
          ctx.lineTo(msg.x,msg.y);
          ctx.closePath();
          ctx.stroke();
          lastReceivedX = msg.x;
          lastReceivedY = msg.y;
        }
      });

    %div(data-role="page")
      %canvas#canvas(width='1000' height='1000')