Appbase.io News

Database service for the #realtimeweb

Jam Together in Realtime Over the Web

If you ever dreamed of playing in a band, now's your chance to be a rock star. Jam with friends is a live piano you can play with your friends built on Appbase. You can also watch what others are playing!

We took Peter Coles' awesome piano app as our starting point and added support for playing it in realtime. We created rooms so people can share their room with their friends and jam together over the web. Don't not miss the cute icon!

The Before / After Pics

Mrcoles' original piano app

Jam with friends on Appbase


Let's Dig Deeper: How Did We Do this?

Our root namespace is pianoapp. All data endpoints are arranged in this namespace.

Data Endpoints

  • pianoapp/piano/{room_id}
  • pianoapp/piano/{roomid}/keys/{keyid}
  • pianoapp/piano/{roomid}/users/{userid}

Managing Rooms

pianoapp has a vertex called piano, which contains the piano rooms. Everytime a new user connects to the app, we listen to the edge_added event at the endpoint pianoapp/piano.

mainRef.on('edge_added', function(error, edgeRef, edgeSnap) { //new room; mainRef is Appbase.ref('pianoapp/piano')  
    var handler = edgeRef.on('properties', function(error, ref, vSnap) { //get {room_id} properties
    updateList(vSnap.properties().name, edgeSnap.name()); //add the room to the list
    ...
    });
});

Inside the Room

From playing piano in realtime to handling users, a room is where the real action happens. Perhaps, the most important question is, how do we play the live piano here? So let's take a closer look, with the code snippets.

Pushing Key Presses to Appbase

// Mouse and keyboard events call below function.
var triggerKey = function(key) {  
  playKeyInTheView(key, myColor, myName); //play locally before pushing to Appbase
  pushToAppbase(key);
}

var pushToAppbase = function(key) {  
  keysRef.setEdge(keysRef, encodeKey(key, userUUID), throwIfError);
}

Listening to Key Presses

keyRef.on('edge_added', function (error, edgeRef, edgeSnap) {  
  var keyObj = decodeKey(edgeSnap.name());
  if(userUUID !== keyObj.userUUID) {  //ignore if key is from this user
    playKeyInTheView(keyObj.key, keyObj.color, keyObj.name);
  }
}

User Presence Management

We store three things for a user: handle, color code and last updated timestamp.

userRef.setData({name: myName, color: myColor, time: getTime()}, throwIfError);  

When a user enters a room, we add the user as an edge to the users endpoint in the current room.

usersRef = Appbase.ref(room.path() + '/users');  
usersRef.setEdge(userRef, userUUID, throwIfError); //Register the user inside the room  

Detecting When a User has Left the Room

There are three cases when a user leaves the room:

  • A user switches to a different room. Here we listen to the edge_removed event on the users endpoint.
usersRef.on('edge_removed', events.usersRef.edge_removed, true);  
events.usersRef.edge_removed : function (error, edgeRef, edgeSnap) {  
  throwIfError(error);
  $scope.users.filter(function(each){
    return each.id !== edgeSnap.name();
  });
}
  • A user closes the jam-with-friends piano tab. Here we remove the current user's edge from the usersRef object.
usersRef.removeEdge(userUUID, throwIfError);  
  • User disconnects from the internet or closes the browser accidently. Appbase doesn't support the former right now and since we cannot detect the latter, we use a time polling to detect dead users.
timePolling : {  
  interval : 60000, // 1 minute
  timeout : 600000, // 10 minutes
  update : function(){
    var now = getTime();
    userRef.setData({time: now});
    $scope.users = $scope.users.filter(function(each){
      return each.time > now - events.timePolling.timeout;
    }); //if their time is greater than the timeout barrier, keep. else, remove
  }
}

That's all folks!

Here are the demo and github source links. Happy hacking!

Author image
Founder at Appbase, read my musings on the db world.