56°F

Aaron Parecki

  • Articles
  • Notes
  • Photos
  • Realtime IndieWeb Comments

    October 13, 2013

    At the last IndieWeb Dinner, we discussed implementing realtime display of indieweb comments when viewing a note. This is analogous to when you're looking at a post on Facebook and someone adds a comment, it shows up automatically with no page refresh.

    Given the realtime nature of webmention, I was able to add this feature to p3k in just one evening!

    The video below is a demonstration of realtime comments on one of my posts in p3k! Unfortunately it's me commenting on my own post, but you get the idea.

    The left window shows what someone sees when viewing a post.

    The right window is me typing a reply to my note. After I click "post," the note is posted, which sends a webmention to the "in-reply-to" URL, which the left window picks up on through the Websockets connnection!

    Realtime IndieWeb Comments

    There is a slight delay between when the note is posted on the right and when it appears on the left, mostly because I was tethered to my phone's Internet in the middle of San Francisco with poor reception.

    Show me the code!

    Below is the code that makes this possible.

    Client-Side WebSockets

    First, the browser makes a WebSocket connection to my server. It sends the URL the browser window is currently open to which registeres a listener on the server. You can see the live version of this code at the bottom of the source code of this page.

    var commentContainerSelector = '.references ul';
     
    if($(commentContainerSelector).length > 0 && "WebSocket" in window) {
      var ws = new WebSocket(window.location.origin.replace("http","ws")+":8077");
      ws.onopen = function(event) {
        // Send the current window URL to the server to register to receive notifications about this URL
        ws.send(window.location);
      };
      ws.onmessage = function(event) {
        var data = JSON.parse(event.data);
        if(data && data.type == "webmention") {
          // Check if we've already added a comment for this ID, and update the existing one if so
          if($("#"+data.element_id).length == 0) {
            $(commentContainerSelector).append(data.html);
          } else {
            $("#"+data.element_id).html(data.html);
          }
        }
      };
    }

    WebSocket Server

    The server script is literally the code below.

    var WebSocketServer = require('ws').Server;
    var Redis = require('redis');
    
    var port = 8077;
    
    var wss = new WebSocketServer({port: port});
    
    wss.on('connection', function(ws) {
      // console.log("New websockets connection");
      ws.on('message', function(message) {
        var redis = Redis.createClient(6379, 'localhost');
        var channel = 'replies::' + message;
        redis.subscribe(channel);
        // console.log('Listening for comments on channel ' + channel);
        redis.on('message', function (channel, message) {
          console.log('Sent comment to channel ' + channel);
          ws.send(message);
        });
        ws.on('close', function(){
          // console.log('Killing listener for channel ' + channel);
          redis.unsubscribe();
          redis.end();
        });
        ws.on('error', function(){
          // console.log('Killing listener for channel ' + channel);
          redis.unsubscribe();
          redis.end();
        });
      });
    });
    
    console.log("WebSocket Server Listening on port "+port);
    

    This simply sets up a WebSocket server that echos messages received on a Redis pub/sub channel to a WebSocket listener.

    The Glue

    The piece that brings it to life was simply a matter of adding a little bit of code to my code when a new webmention is received by my server.

    At the bottom of my webmention handling code, I added these four lines of code.

    <?php
    ob_start();
    p3k\Site::displayComment($sourceURL); // Renders the comment HTML
    $html = ob_get_clean();
    redis()->publish('replies::' . $targetURL, json_encode(array(
      'type' => 'webmention',
      'element_id' => 'external_' . preg_replace(array('/[\/:\.]+/'), array('_'), $sourceURL);,
      'html' => $html
    )));
    ?>
    

    This works great since I already have a method in p3k which handles rendering an HTML version of a comment given a URL. I just broadcast the HTML on a Redis channel matching the URL of the comment.

    How can you get started?

    You can try adding a comment in realtime to one of my notes! Try replying to the test note I posted here.

    You'll need to:

    • Write a reply to my note on your own site, linking to my note (See indiewebcamp.com/comment for more details)
    • Optionally add h-entry markup (it will look better if you do!)
    • Send me a webmention from your site or by simply pasting the URL in the webmention form at the bottom of my note.

    You should nearly instantly see your reply appear on my site!

    Sun, Oct 13, 2013 12:19am -07:00 #indieweb #realtime #comment #p3k
    3 replies 3 mentions
    • tilgovi webmention.dokku.hypothes.is/u/tilgovi
      tilgovi on Realtime IndieWeb Comments at Oct 30, 2014, 10:01:22 AM You should nearly instantly see your reply appear on my site! Let's try it out!
      Thu, Oct 30, 2014 11:56am -07:00
    • tilgovi webmention.dokku.hypothes.is/u/tilgovi
      tilgovi on Realtime IndieWeb Comments at Oct 30, 2014, 10:01:22 AM You should nearly instantly see your reply appear on my site! Let's try it out!
      Thu, Oct 30, 2014 3:07am -07:00
    • tantek.com
      Well done @aaronpk! Real-time #indieweb comments:
      http://aaronparecki.com/articles/2013/10/13/1/realtime-indieweb-comments

      I only mentioned the idea at the @indiewebcamp dinner at 21st Amendment http://aaronparecki.com/events/2013/09/30/1/indieweb-dinner-at-21st-amendment and he's already implemented it live on his site! ...
      Sat, Oct 12, 2013 10:32pm -07:00

    Other Mentions

    • Kyle Mahan kylewm.com
      RQ is pretty perfect for my use case (small number of infrequent jobs). I’m on a 512mb VPS, so I really appreciate that it forks a short-lived process for each task. The master only uses like 10mb (as opposed to Celery which was closer to 50-70mb, which is still pretty small but enough to run out of memory on my tiny server).
      Asyncio is awesome (though it makes me a little sad that gevent vs. asyncio widens the gulf between Python 2 and 3 instead of narrowing it). So this is literally the ...
      Mon, Mar 16, 2015 10:38am -07:00
    • Kyle Mahan kylewm.com
      Still thinking about tildes
      Tue, Nov 4, 2014 11:00pm -08:00
    • Kyle Mahan kylewm.com
      Still thinking about tildes
      Tue, Nov 4, 2014 11:00pm -08:00
Posted in /notes

Hi, I'm Aaron Parecki, Director of Identity Standards at Okta, and co-founder of IndieWebCamp. I maintain oauth.net, write and consult about OAuth, and participate in the OAuth Working Group at the IETF. I also help people learn about video production and livestreaming. (detailed bio)

I've been tracking my location since 2008 and I wrote 100 songs in 100 days. I've spoken at conferences around the world about owning your data, OAuth, quantified self, and explained why R is a vowel. Read more.

  • Director of Identity Standards at Okta
  • IndieWebCamp Founder
  • OAuth WG Editor
  • OpenID Board Member

  • 🎥 YouTube Tutorials and Reviews
  • 🏠 We're building a triplex!
  • ⭐️ Life Stack
  • ⚙️ Home Automation
  • All
  • Articles
  • Bookmarks
  • Notes
  • Photos
  • Replies
  • Reviews
  • Trips
  • Videos
  • Contact
© 1999-2025 by Aaron Parecki. Powered by p3k. This site supports Webmention.
Except where otherwise noted, text content on this site is licensed under a Creative Commons Attribution 3.0 License.
IndieWebCamp Microformats Webmention W3C HTML5 Creative Commons
WeChat ID
aaronpk_tv