Introducing TogetherJS

What is TogetherJS?

We’d like to introduce TogetherJS, a real-time collaboration tool out of Mozilla Labs.

TogetherJS is a service you add to an existing website to add real-time collaboration features. Using the tool two or more visitors on a website or web application can see each other’s mouse/cursor position, clicks, track each other’s browsing, edit forms together, watch videos together, and chat via audio and WebRTC.

Some of the features TogetherJS includes:

  • See the other person’s cursor and clicks
  • See scroll position
  • Watch the pages a person visits on a page
  • Text chat
  • Audio chat using WebRTC
  • Form field synchronization (text fields, checkboxes, etc)
  • Play/pause/track videos in sync
  • Continue sessions across multiple pages on a site

How to integrate

Many of TogetherJS’s features require no modification of your site. TogetherJS looks at the DOM and determines much of what it should do that way – it detects the form fields, detects some editors like CodeMirror and Ace, and injects its toolbar into your page.

All that’s required to try TogetherJS out is to add this to your page:

<script src="https://togetherjs.com/togetherjs.js"></script>

And then create a button for your users to start TogetherJS:

<button id="collaborate" type="button">Collaborate</button>
<script>
document.getElementById("collaborate")
  .addEventListener("click", TogetherJS, false);
</script>

If you want to see some of what TogetherJS does, jsFiddle has enabled TogetherJS:

jsfiddle with Collaborate highlighted

Just click on Collaboration and it will start TogetherJS. You can also use TogetherJS in your fiddles, as we’ll show below.

Extending for your app

TogetherJS can figure out some things by looking at the DOM, but it can’t synchronize your JavaScript application. For instance, if you have a list of items in your application that is updated through JavaScript, that list won’t automatically be in sync for both users. Sometimes people expect (or at least hope) that it will automatically update, but even if we did synchronize the DOM across both pages, we can’t synchronize your underlying JavaScript objects. Unlike products like Firebase or the Google Drive Realtime API TogetherJS does not give you realtime persistence – your persistence and the functionality of your site is left up to you, we just synchronize sessions in the browser itself.

So if you have a rich JavaScript application you will have to write some extra code to keep sessions in sync. We do try to make it easier, though!

To give an example we’d like to use a simple drawing application. We’ve published the complete example as a fiddle which you can fork and play with yourself.

A Very Small Drawing Application

We start with a very simple drawing program. We have a simple canvas:

<canvas id="sketch"
        style="height: 400px; width: 400px; border: 1px solid #000">
</canvas>

And then some setup:

// get the canvas element and its context
var canvas = document.querySelector('#sketch');
var context = canvas.getContext('2d');
 
// brush settings
context.lineWidth = 2;
context.lineJoin = 'round';
context.lineCap = 'round';
context.strokeStyle = '#000';

We’ll use mousedown and mouseup events on the canvas to register our move() handler for the mousemove event:

var lastMouse = {
  x: 0,
  y: 0
};
 
// attach the mousedown, mousemove, mouseup event listeners.
canvas.addEventListener('mousedown', function (e) {
    lastMouse = {
        x: e.pageX - this.offsetLeft,
        y: e.pageY - this.offsetTop
    };
    canvas.addEventListener('mousemove', move, false);
}, false);
 
canvas.addEventListener('mouseup', function () {
    canvas.removeEventListener('mousemove', move, false);
}, false);

And then the move() function will figure out the line that needs to be drawn:

function move(e) {
    var mouse = {
        x: e.pageX - this.offsetLeft,
        y: e.pageY - this.offsetTop
    };
    draw(lastMouse, mouse);
    lastMouse = mouse;
}

And lastly a function to draw lines:

function draw(start, end) {
    context.beginPath();
    context.moveTo(start.x, start.y);
    context.lineTo(end.x, end.y);
    context.closePath();
    context.stroke();
}

This is enough code to give us a very simple drawing application. At this point if you enable TogetherJS on this application you will see the other person move around and see their mouse cursor and clicks, but you won’t see drawing. Let’s fix that!

Adding TogetherJS

TogetherJS has a “hub” that echoes messages between everyone in the session. It doesn’t interpret messages, and everyone’s messages travel back and forth, including messages that come from a person that might be on another page. TogetherJS also lets the application send their own messages like:

TogetherJS.send({
  type: "message-type",
  ...any other attributes you want to send...
})

to send a message (every message must have a type), and to listen:

TogetherJS.hub.on("message-type", function (msg) {
  if (! msg.sameUrl) {
    // Usually you'll test for this to discard messages that came
    // from a user at a different page
    return;
  }
});

The message types are namespaced so that your application messages won’t accidentally overlap with TogetherJS’s own messages.

To synchronize drawing we’d want to watch for any lines being drawn and send those to the other peers:

function move(e) {
    var mouse = {
        x: e.pageX - this.offsetLeft,
        y: e.pageY - this.offsetTop
    };
    draw(lastMouse, mouse);
    if (TogetherJS.running) {
        TogetherJS.send({type: "draw", start: lastMouse end: mouse});
    }
    lastMouse = mouse;
}

Before we send we check that TogetherJS is actually running (TogetherJS.running). The message we send should be self-explanatory.

Next we have to listen for the messages:

TogetherJS.hub.on("draw", function (msg) {
    if (! msg.sameUrl) {
        return;
    }
    draw(msg.start, msg.end);
});

We don’t have to worry about whether TogetherJS is running when we register this listener, it can only be called when TogetherJS is running.

This is enough to make our drawing live and collaborative. But there’s one thing we’re missing: if I start drawing an image, and you join me, you’ll only see the new lines I draw, you won’t see the image I’ve already drawn.

To handle this we’ll listen for the togetherjs.hello message, which is the message each client sends when it first arrives at a new page. When we see that message we’ll send the other person an image of our canvas:

TogetherJS.hub.on("togetherjs.hello", function (msg) {
    if (! msg.sameUrl) {
        return;
    }
    var image = canvas.toDataURL("image/png");
    TogetherJS.send({
        type: "init",
        image: image
    });
});

Now we just have to listen for this new init message:

TogetherJS.hub.on("init", function (msg) {
    if (! msg.sameUrl) {
        return;
    }
    var image = new Image();
    image.src = msg.image;
    context.drawImage(image, 0, 0);
});

With just a few lines of code TogetherJS let us make a live drawing application. Of course we had to do some of the code, but here’s some of the things TogetherJS handles for us:

  • Gives users a URL to share with another user to start the session Screenshot of invitation window
  • Establishes a WebSocket connection to our hub server, which echoes messages back and forth between clients
  • Let’s users set their name and avatar, and see who else is in the session Screenshot of avatar/name setting
  • Keeps track of who is available, who has left, and who is idle
  • Simple but necessary features like text chat are available Screenshot of chat window
  • Session initialization and tracking is handled by TogetherJS

Some of the things we didn’t do in this example:

  • We used a fixed-size canvas so that we didn’t have to deal with two clients and two different resolutions. Generally TogetherJS handles different kinds of clients and using resolution-independent positioning (and even works with responsive design). One approach to fix this might be to ensure a fixed aspect ratio, and then use percentages of the height/width for all the drawing positions.
  • We don’t have any fun drawing tools! Probably you wouldn’t want to synchronize the tools themselves – if I’m drawing with a red brush, there’s no reason you can’t be drawing with a green brush at the same time.
  • But something like clearing the canvas should be synchronized.
  • We don’t save or load any drawings. Once the drawing application has save and load you may have to think more about what you want to synchronize. If I have created a picture, saved it, and then return to the site to join your session, will your image overwrite mine? Putting each image at a unique URL will make it clearer whose image everyone is intending to edit.

Want To Look At More?

  • Curious about the architecture of TogetherJS? Read the technology overview.
  • Try TogetherJS out on jsFiddle
  • Find us via the button in the documentation: “Get Live Help” which will ask to start a TogetherJS session with one of us.
  • Find us on IRC in #togetherjs on irc.mozilla.org.
  • Find the code on GitHub, and please open an issue if you see a bug or have a feature request. Don’t be shy, we are interested in lots of kinds of feedback via issues: ideas, potential use cases (and challenges coming from those use cases), questions that don’t seem to be answered via our documentation (each of which also implies a bug in our documentation), telling us about potentially synergistic applications.
  • Follow us on Twitter: @togetherjs.

What kind of sites would you like to see TogetherJS on? We’d love to hear in the comments.

About Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

More articles by Robert Nyman [Editor emeritus]…


35 comments

  1. Phil Leggetter

    This is great. Collaboration is the main use case for realtime web technologies that I feel we need to see innovation in. Presently we seem to have hit a wall with editing documents like Google docs and Cloud9 IDE, share agile/scrum boards and todo lists. It’s not that these aren’t amazing and very useful. It’s just they’ve been around for a while and I want to see what’s next.

    Would it be worth clarifying browser support for TogetherJS?

    For example a brief inspection of the code suggests:

    * WebRTC requirements
    * It looks like it requires WebSocket support and has no HTTP-based fallback

    The reason for this is TogetherJS seems set up to allow *anybody* to add it to their site. But without the “installer” being aware of these requirements they may frequently wonder why some users aren’t getting the expected functionality.

    Maybe TogetherJS provides a callback to indicate when one of the requirements isn’t present or a connection can’t be made to the Hub?

    October 16th, 2013 at 03:05

    1. Robert Nyman [Editor]

      Thanks for the kind words!

      When it comes to web browser support, it’s outlined in the documentation for TogetherJS. It is an interesting thought about warning implementers, though, and potentially worth adding.

      October 16th, 2013 at 03:15

    2. Ian Bicking

      We do try to throw up appropriate error messages when a browser lacks the necessary support. (WebRTC isn’t required to use TogetherJS, only to use audio chat, so the error message there comes up if you try to activate that function without WebRTC.)

      There is also an open ticket for exposing the browser support to site developers: https://github.com/mozilla/togetherjs/issues/776

      October 16th, 2013 at 08:11

  2. Ivan Dejanovic

    To say I am astounded would be the understatement. In the latest web product we developed in the company I work for we implemented chat support using OpenFire. You managed to implement chat, video and audio using only JavaScript. And the best thing is it is very easy to integrate into existing product. Since TogetherJS is using Mozilla servers I will probably never be able to use it at work but I will sure give it a try on my private projects. Looking forward to see where TogetherJS journey will take the web.

    October 16th, 2013 at 12:34

    1. Robert Nyman [Editor]

      Thanks Ivan, we hope you will find good use for it!

      October 16th, 2013 at 13:05

    2. Ian Bicking

      You can setup your own server: https://togetherjs.com/docs/contributing.html#hosting-the-hub-server

      Also it should be possible to use different sorts of servers. There was an experiment to substitute Firebase, for instance: https://github.com/firebase/togetherjs – I’m hoping we can take that and generalize it.

      October 17th, 2013 at 11:34

  3. Eleftherios Kosmas

    I would love to see that on etherpad lite to be honest.

    October 16th, 2013 at 13:45

    1. Marcel Dupont

      I second this, that would be fantastic.

      Or just the chat would be great too.

      October 16th, 2013 at 15:04

  4. Ben Werdmuller

    This is potentially huge. Business collaboration is one major use case, and probably where it’ll see the most use, as you’ve discussed – but in a lot of ways, I’m looking forward to the first game to be written with this. It could also open the door to new kinds of discussion platforms.

    October 16th, 2013 at 14:57

    1. Robert Nyman [Editor]

      Thanks Ben – and definitely, it could be very interesting in games!

      October 17th, 2013 at 00:50

  5. Brett Zamir

    Very exciting! I sure hope we can see full blown screen sharing and control soon…

    If you want to allow users the ability to open local files from anywhere on their desktop and expose these in a file-type-aware manner to a web app for collaborative viewing or editing as Together.js allows (e.g., for editing HTML, SVG, JavaScript, or custom file type files, etc.), you might take a look at WebAppFind at https://github.com/brettz9/webappfind (currently Windows only).

    Although I may be able to get a demo working with Together.js, I expect to be busy for a while with improving WebAppFind and hopefully completing ExecuteBuilder, another add-on which aims to facilitate the building of executables usable by WebAppFind with the added ability to open web apps in a separate task bar instance (via separate profiles), thereby mimicking the former Prism project behavior. So, if anyone is eager to get this working sooner, be my guest! It would be pretty darn cool not only to allow WYSIWYG word processing of local HTML/SVG/image files as WebAppFind allows, but with the collaboration features of Together.js.

    (I also hope to add WebAppFind’s functionality to filebrowser-enhanced: https://addons.mozilla.org/en-US/firefox/addon/filebrowser-enhanced/ so one would be able to open local files in web apps via Firefox’s own desktop browsing pages).

    Feel free to report ideas through the issues tracker or submit pull requests!

    October 16th, 2013 at 16:39

    1. Robert Nyman [Editor]

      Thanks for the heads-up, Brett!

      October 17th, 2013 at 00:50

  6. Mike Conley

    Hey! We were able to get TogetherJS into a extension for Review Board (web-based code review tool).

    Here’s a demo video: http://www.youtube.com/watch?v=tEfbREmTBjc

    We’re really excited to see what the possibilities are for making code review more collaborative with technologies like TogetherJS. Keep up the great work!

    -Mike

    October 16th, 2013 at 20:06

    1. Robert Nyman [Editor]

      Seems great, thanks for sharing!

      October 17th, 2013 at 00:51

    2. Ian Bicking

      Is the extension up somewhere? And/or link it from https://github.com/mozilla/togetherjs/wiki/Libraries-and-Plugins (I put your video up there)

      October 17th, 2013 at 11:27

      1. Mike Conley

        I’ve edited the wiki to include a link to the GH repo. Thanks!

        October 18th, 2013 at 12:26

  7. Jelle Akkerman

    What about making a browser extension out of this, so you can enable together.js on every website? :) Click the browser extension, together.js get injected in the page, share the link w friend, together gets injected in their page.

    October 17th, 2013 at 01:18

    1. Samuel Bosch

      I just had the exact same idea. I’ll try it when I get home tonight.

      October 17th, 2013 at 01:35

    2. Ian Bicking

      There’s a Chrome extension https://github.com/zhuzhuor/TowTruck.crx (may be outdated, I’m not sure), and a rough addon in the repository for Firefox. And a pull request that I don’t know what to do with for another Chrome extension: https://github.com/mozilla/togetherjs/pull/595

      Ultimately I am afraid it won’t be a great experience to use an addon. Just in the way the drawing app doesn’t really work without some integration, I expect similar experiences on a lot of sites.

      October 17th, 2013 at 11:32

  8. Vignesh Swaminathan

    This is just great! One question, would it be possible to continue the session if the web app opened up a new browser tab?

    October 17th, 2013 at 02:01

    1. Ian Bicking

      Any link in a new tab has to have the #&togetherjs=SESSION_ID on the URL. If you can rewrite the link to include that, then yes, the session will continue in both tabs.

      October 17th, 2013 at 11:28

      1. Vignesh Swaminathan

        Awesome!

        October 17th, 2013 at 21:45

  9. Carlos Cabral

    what kind of hub server is Mozilla using? Is it open-source or are you planning on release it as open-source? thanks

    October 17th, 2013 at 05:21

    1. Ian Bicking

      You can read about the hub here: https://togetherjs.com/docs/contributing.html#hosting-the-hub-server

      Not only open source, it self-publishes its source ;) https://hub.togetherjs.com/server-source

      October 18th, 2013 at 12:30

    2. Carlos Cabral

      nevermind… should’ve checked github before asking :)

      October 18th, 2013 at 12:47

  10. Bill McCoy

    I don’t understand the documentation about browser support. It’s made clear that IE support is not a priority (although it’s not stated why) but there’s zero mention of Safari, even though WebSockets also works on there (both OS/X and iOS). Is there some other obvious problem with supporting TogetherJS on Safari? Stepping back the question is about the brand promise of TogetherJS wrt the Open Web Platform. It’s described as “a real-time collaboration tool… you add to an existing website”. But who wants to add something to an existing website that doesn’t work for half of our visitors? I would love to see a clear statement in the docs that the intention is for TogetherJS to work on all modern browsers and any temporary problems are simply (understandable) ramp-up issues.

    October 17th, 2013 at 06:00

    1. Ian Bicking

      Safari does work, we don’t test it consistently but it’s enough like Chrome that it has been pretty stable. We are increasing the priority of Internet Explorer, we’ll discuss that more in an upcoming post on the Labs blog: https://blog.mozilla.org/labs/

      October 18th, 2013 at 12:29

  11. Phil Wolff

    Anyone packaged this as a WordPress widget?

    October 17th, 2013 at 11:16

    1. Ian Bicking

      Yes! There are two: http://wordpress.org/plugins/collaboration/ and http://wordpress.org/plugins/wp-togetherjs/

      October 18th, 2013 at 12:26

    2. Aaron Druck

      Yup, here are some plugins: http://wordpress.org/plugins/tags/togetherjs

      October 18th, 2013 at 14:03

  12. Arnaud Sahuguet

    That’s super awesome.

    It is even more awesome when you package it as a bookmarklet. Click and collaborate on a given page.
    >>
    var script = document.createElement(‘script’);
    script.setAttribute(“type”,”text/javascript”);
    script.setAttribute(“src”, “https://togetherjs.com/togetherjs.js”);
    document.getElementsByTagName(“head”)[0].appendChild(script);
    setTimeout(function() { TogetherJS(); } , 3000);
    <<
    minimized into
    javascript:(function(){var script=document.createElement('script');script.setAttribute("type","text/javascript");script.setAttribute("src","https://togetherjs.com/togetherjs.js&quot;);document.getElementsByTagName("head")[0].appendChild(script);setTimeout(function(){TogetherJS();},3000);})()

    October 18th, 2013 at 12:29

  13. kman

    Hi, how to ignore some form elements if we don’t want TogetherJS to pass their content?

    October 18th, 2013 at 17:52

  14. Shane

    Geezus you guys are so cool, we <3 Mozilla!

    October 19th, 2013 at 02:22

  15. James

    you can also view http://www.ucodeme.com that has togtherJS embedded.

    October 29th, 2013 at 17:11

  16. dotBuffer

    Fantastic work great tool guys!

    October 29th, 2013 at 22:37

Comments are closed for this article.