Skip to content

Online presence indicator

Each reader on the page gets a dot. Yours glows. Pick a color and everyone sees your dot change. Close a window and its dot disappears.

Live exampleRoom connecting…
Remix
Test it with another person
  1. Keep this page open in your normal window.
  2. Copy the private-window link, then paste it into a private or incognito window.
  3. Interact in either window and watch the other one update.

Copy the code

Both versions use the same shared data and behavior. The live playground runs the Vanilla HTML version.

Save this as index.html, or open it in the playground to test and change it.

Remix

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Online presence indicator</title>
  <style>
    :root {
      color: #1c1c1c;
      background: #f4efe5;
      font-family: ui-sans-serif, system-ui, sans-serif;
    }
    * { box-sizing: border-box; }
    body { display: grid; min-height: 100vh; margin: 0; padding: 1.5rem; place-items: center; }
    .dots {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem;
    }
    .dot {
      width: 42px;
      height: 42px;
      border: 1.5px solid #1c1c1c;
      border-radius: 50%;
    }
    /* Your own dot glows. */
    .dot.me {
      box-shadow: 0 0 0 3px white, 0 0 0 5px #1c1c1c,
        0 0 22px 6px rgba(245, 169, 15, 0.7);
    }
  </style>
</head>
<body>
  <main>
    <div id="online">
      <div class="dots"></div>
      <p class="count"></p>
    </div>
    <label>
      your color
      <input id="my-color" type="color" value="#4a6a3e" />
    </label>
  </main>

  <script type="module">
    import { playhtml } from "https://unpkg.com/playhtml@latest";

    // Each person here has a presence on this element: a value everyone can
    // see, gone when they leave. Yours starts at `defaultPresence`, and
    // `everyone` lists each person (name, color, isMe) with theirs.
    const online = playhtml.register("online", {
      defaultPresence: { color: "#4a6a3e" },
      update: ({ element, everyone }) => {
        element.querySelector(".dots").replaceChildren(
          ...everyone.map((person) => {
            const dot = document.createElement("span");
            dot.className = person.isMe ? "dot me" : "dot";
            dot.style.background = person.presence.color;
            return dot;
          }),
        );
        const people = everyone.length === 1 ? "person" : "people";
        element.querySelector(".count").textContent =
          `${everyone.length} ${people} here`;
      },
    });

    document.getElementById("my-color").addEventListener("input", (event) => {
      online.setMyPresence({ color: event.target.value });
    });

    await playhtml.init({ developmentMode: true });
  </script>
</body>
</html>

Each reader has a presence on this element: a value everyone can see, never saved, gone when they leave. Yours starts at defaultPresence. everyone lists everyone here right now, including you, each with their name and color and their presence on this element:

everyone.map((person) => person.presence.color);

person.isMe marks your own dot.

Call setMyPresence from a user event. It sets your whole value and reaches everyone else right away:

online.setMyPresence({ color: event.target.value });

See people and presence for page-wide presence and cursors.