Tracking connected clients with Presence

Presence enables clients to be aware of other clients that are currently “present” on a channel. Each member present on a channel has a self-assigned name — a “client ID” — along with an optional payload that can be used to describe the member’s status or attributes. Presence allows you to quickly build apps such as chat rooms and multiplayer games by automatically keeping track of who is present in real time across any device.

When someone enters or leaves a channel, or updates their member data, a presence event is emitted to all presence subscribers on that channel. Subscribing to presence events makes it incredibly easy to build an app that shows, in real time, any changes to clients connected to Ably and present on a channel.

The optional payload can be a string, JSON object, JSON array, or binary data: the same as with message data.

Using the Presence API is straightforward. Follow the steps in this tutorial to learn how.

Step 1 – Create your Ably app and API key

To follow this tutorial, you will need an Ably account. Sign up for a free account if you don’t already have one.

Access to the Ably global messaging platform requires an API key for authentication. API keys exist within the context of an Ably application and each application can have multiple API keys so that you can assign different capabilities and manage access to channels and queues.

You can either create a new application for this tutorial, or use an existing one.

To create a new application and generate an API key:

  1. Log in to your Ably account dashboard
  2. Click the “Create New App” button
  3. Give it a name and click “Create app”
  4. Copy your private API key and store it somewhere. You will need it for this tutorial.

To use an existing application and API key:

  1. Select an application from “Your apps” in the dashboard
  2. In the API keys tab, choose an API key to use for this tutorial. The default “Root” API key has full access to capabilities and channels.
  3. Copy the Root API key and store it somewhere. You will need it for this tutorial.

    Copy API Key screenshot

Step 2 – Install Ably

Create a blank HTML page called example.html, to host your web app:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Ably Presence</title>
  </head>
  <body>
  </body>
</html>

To start using Ably in your web app, you first need to include the Ably client library. In this example, we recommend that you include the latest client library from our CDN using a simple <script> tag. You should then instantiate the client library with the API key you copied in Step 1.

Note: in production, Ably recommends that you always use the token authentication scheme for browser clients, however in this example you will use an API key for simplicity.

Include the code below just before your closing </html> tag.

<script src="https://cdn.ably.com/lib/ably.min-1.js"></script>
<script>
  const apiKey = "YOUR_ABLY_API_KEY"; // Replace with your API key
  const randomId = Math.random().toString(36).slice(-10); 
  const realtime = new Ably.Realtime.Promise({
    key: apiKey,
    clientId: randomId, // Your ID in the presence set
  });
</script>

To start using Ably you first need to install the NPM module. The NPM module can be installed as follows:

npm install ably

The client library must be instantiated with the API key you copied in Step 1. API keys used with basic authentication for your own servers is generally preferred, however clients running on insecure devices should always use the token authentication scheme instead. In this tutorial, you will use an API key for simplicity.

Add the following to a file named example.js to instantiate the Ably library inside your Node.js server:

const Ably = require("ably");

const apiKey = "YOUR_ABLY_API_KEY"; // Replace with your API key
const randomId = Math.random().toString(36).slice(-10); 
const realtime = new Ably.Realtime.Promise({
  key: apiKey,
  clientId: randomId, // Your ID in the presence set
});

This code uses the Promise-based version of the Ably client library (Ably.Realtime.Promise) that enables you to use the much tidier await/async syntax, instead of relying on callbacks. It generates a random clientID that will be used to identify this particular client in the presence set.

Below the line that instantiates the Ably client library, create an async function called doPresence() and call it. This is where you will connect to Ably. It is also where you will write the code for the subsequent steps in this tutorial.

<script>
  ...
  const realtime = new Ably.Realtime.Promise({
    key: apiKey,
    clientId: randomId, // Your ID in the presence set
  });

  async function doPresence() {
    // Connect to Ably
    await realtime.connection.once("connected");
    console.log("Connected to Ably!");
    // Your code goes here
  }
  doPresence();
</script>
...
const realtime = new Ably.Realtime.Promise({
  key: apiKey,
  clientId: randomId, // Your ID in the presence set
});

async function doPresence() {
  // Connect to Ably
  await realtime.connection.once("connected");
  console.log("Connected to Ably!");    
  // Your code goes here
}
doPresence();

To start using Ably you first need to install the Ably RubyGem. The RubyGem can be installed as follows:

gem install ably

Or if using bundler, simply add the following to your Gemfile and run bundle install:

gem 'ably'

The client library must be instantiated with the API key you copied in Step 1. API keys used with basic authentication for your own servers is generally preferred, however clients running on insecure devices should always use the token authentication scheme instead. In this tutorial, you will use an API key for simplicity. As you’ll be using presence, you will also include a client_id, which is what will identify your client in the presence set.

The Ruby realtime library uses EventMachine to run the client library within an asynchronous event loop. However, typically when using Ruby on servers, most developers use the synchronous REST client library API. As this tutorial needs realtime access to be present and subscribe to presence messages, it uses the Ruby realtime library.

Add the following to a file named example.rb to instantiate the Ably library inside an EventMachine reactor:

require 'ably'
EventMachine.run do
  ably = Ably::Realtime.new(key: api_key, client_id: "jamie")
end

Step 3 – Enter the presence set

With Ably, you can be attached to a channel (that is: be receiving messages published on that channel, or publishing them yourself) without necessarily being present on the channel. If you want to be present, and be seen by other users, you need to explicitly enter the presence set. In this tutorial, you will create a channel called “chatroom” by attaching to it, and then enter the channel’s presence set:

// Attach to the "chatroom" channel
const channel = realtime.channels.get("chatroom");
await channel.attach((err) => {
  if (err) {
    return console.error("Error attaching to the channel.");
  }
});

// Enter the presence set of the "chatroom" channel
await channel.presence.enter("hello", (err) => {
  if (err) {
    return console.error("Error entering presence set.");
  }
  console.log("This client has entered the presence set.");
});
channel = ably.channels.get('chatroom')
  channel.attach do
    channel.presence.enter(data: 'hello') do
      puts "We are now in the presence set"
    end
  end
end

You are now in the presence set. The name you will appear as is the client ID that you used to instantiate the Ably client library in step 2.

Similarly, you can call channel.presence.leave to leave the presence set.

Side note: Although in this tutorial you can choose any client ID, in most situations you don’t want that. Instead you want your server to constrain what client ID people can use — for example, to their logged-in username — so that no-one can pretend to be someone else. When using Token authentication, your server can issue tokens with a specific client ID. When such a token is used by someone, their connection is bound to that client ID.

A single client ID may be present multiple times on the same channel, via different client connections; but each will be seen as a different member of the presence set, as they are differentiated by their unique connection ID. For example, if a client with client ID “Sarah” is connected to a chat channel on both a desktop and a mobile device simultaneously, “Sarah” will be present twice in the presence member set with the same client ID, each with a different connection ID. A member of the presence set is therefore unique by the combination of the client ID and connection ID strings.

Updating your presence data (optional)

In the previous step, you set a payload for your entry in the presence set: the string “hello”, which other clients will receive with the notification that this particular client has entered the channel. You can update that whenever you want, and other clients will receive the update. This is not required for the tutorial, but the following code shows how to achieve it:

channel.presence.update("howdy!", (err) => {
  if(err) { return console.error("Error updating presence data"); }
  console.log("Update OK");
})
channel.presence.update(data: 'howdy!') do
  puts "Update OK"
end

Step 4 – Subscribe to presence changes

In this step, you will subscribe to receive presence events from the “chatroom” channel. Events include when new members enter the channel, or existing ones leave the channel or update their data.

Presence event messages will include the client ID of the member, and an action that tells you what happened to that member: either enter, update, or leave.

// Subscribe to the presence set to receive updates
await channel.presence.subscribe((presenceMessage) => {
  const { action, clientId } = presenceMessage;
  console.log("Presence update:", action, "from:", clientId);
});
channel.presence.subscribe do |presence_msg|
  puts "Received a #{presence_msg.action} from #{presence_msg.client_id}"
  channel.presence.get do |members|
    puts "There are now #{members.length} clients present on this channel"
  end
end

Step 5 – List present members

Every time the presence set changes, you want to call presence.get to get the new state of the presence set and update the list of channel members.

Modify the channel.presence.subscribe event handler in the preceding step as follows:

// Subscribe to the presence set to receive updates
await channel.presence.subscribe((presenceMessage) => {
  const { action, clientId } = presenceMessage;
  console.log("Presence update:", action, "from:", clientId);

  // Update the list of channel members when the presence set changes
  channel.presence.get((err, members) => {
    if (err) {
      return console.error(`Error retrieving presence data: ${err}`);
    }
    document.getElementById("presence-set").innerHTML = members
      .map((member) => {
        return `<li>${member.clientId}</li>`;
      })
      .join("");
  });
});
// Subscribe to the presence set to receive updates
await channel.presence.subscribe((presenceMessage) => {
  console.log(
    "Presence update: " +
      presenceMessage.action +
      " from " +
      presenceMessage.clientId
  );
  // Update the list of channel members when the presence set changes
  channel.presence.get((err, members) => {
    if (err) {
      return console.error(`Error retrieving presence data: ${err}`);
    }
    const clientIDs = members.map(({ clientId }) => clientId);
    console.log("The presence set now consists of: ", clientIDs);
  });
});
channel.presence.get do |members|
  puts "There are #{members.length} clients present on this channel"
  first = members[0]
  puts "The first member is #{first.clientId} and their data is #{first.data}"
end

As soon as you are attached to the channel, the presence set starts to sync with Ably. When you make this call to get the presence set, it will wait until it has finished syncing before completing. For small presence sets this will take only a few milliseconds; for larger ones (with hundreds of members) the full sync can take a little longer.

Open this file in multiple browser tabs and inspect the browser’s console. You should see new members appearing in the list of present members as you open more tabs and disappearing from the presence set when you close a tab.

Run your code with node example.js in multiple terminal windows and inspect the output. You should see new members appearing in the list of present members when you run your app in a new terminal window. They will disappear from the presence set when you terminate the running program in a terminal window.

Run your code with ruby example.rb in multiple terminal windows and inspect the output. You should see new members appearing in the list of present members when you run your app in a new terminal window. They will disappear from the presence set when you terminate the running program in a terminal window.

Step 6 – Conclusion

In this tutorial, you learned how to:

  • Attach to a channel
  • Enter (and leave) the presence set for the channel
  • Access the list of members present on a channel
  • Update a present member’s status
  • Listen for presence changes

To see another example of Presence in action, try out the live demo below.

Download tutorial source code

The complete source code for this tutorial is available on Github.

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout presence-javascript

And then run the demo locally by adding your Ably API key to example.html and opening the page in your browser.

The complete source code for this tutorial is available on Github.

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout presence-nodejs

And then run the demo locally by adding your Ably API key to example.js and running the demo node example.js

The complete source code for this tutorial is available on Github

We recommend that you clone the repo locally:

git clone https://github.com/ably/tutorials.git

Checkout the tutorial branch:

git checkout presence-ruby

And then run the demo locally by adding your Ably API key to example.rb and running the demo bundle exec ruby example.rb

Next steps

1. If you would like to find out more about how channels, publishing and subscribing works, see the Realtime channels & messages documentation
2. If you would like to find out more about how presence works, see the Realtime presence documentation
3. Learn more about Ably features by stepping through our other Ably tutorials
4. Gain a good technical overview of how the Ably realtime platform works
5. Get in touch if you need help