stolley.dev

  • I’m Writing a Book about WebRTC for Pragmatic Programmers

    • 21 March 2021

    I went from not even realizing I was going to write a book to having a signed contact with PragProg in 82 days: December 17, 2020 was the first day I worked in earnest on the proposal, and March 9, 2021 was the day I received and signed the contract.

    I’d only just submitted the proposal itself eight days earlier, on March 1. At 9:18am. I note the time, because it was just 28 minutes later that I heard from an Author Relations contact at PragProg with some of the best words a writer can hear:

    You’ve done a great job with the proposal and sample chapter, so I think I can present your proposal without any revisions. It’s clear that you get the PragProg style and I’m grateful for that fact and happy to hear you are using PragProg books in your classroom!

    (Yes, I use PragProg books in the classroom all the time, and no, I wasn’t ashamed to slip that little detail into my proposal itself.)

    Since signing the contract, I’ve just been getting up to speed with Prag’s publication system—which I’m contractually bound not to discuss in detail, and I can see why. It’s pretty great. I’ve also begun working with my development editor, and I can say that this is book project is fixing to be whatever the opposite of lonely is. It’s an incredibly collaborative effort already, and I’ve only just gotten started.

    Okay, but what about the book? What’s it actually about? Let me try out my elevator pitch on you:

    You’re a web developer. You’ve spent the pandemic racking up untold hours on Google Meet, Zoom, FaceTime, or whatever real-time video-chat service. But what you might not know is that all modern browsers ship with an implementation of a web API called WebRTC: Web Real-Time Communication Between Browsers. WebRTC allows you to build real-time applications that run directly in the browser. And sort of like with FaceTime or whatever, you can build your own video-chat app. Only unlike all of those popular services, WebRTC works peer to peer: the app you build directly connects one web browser to another. There’s no server involved. And video chatting is only just the most obvious example. With WebRTC, you can open up channels to exchange any arbitrary data that might power all kinds of real-time activities: text-based chats, secure peer-to-peer file transfers, collaborative brainstorming sessions, and even multiplayer gaming. This book will have you doing all of that. And not just with two connected peers: an entire chapter of the book is devoted to engineering multi-peer WebRTC apps, using a mesh-network architecture.

    Okay. So that needs some work. But hopefully the gist of it comes across.

    So where did the book come from? Last fall, I taught a course on WebRTC for the first time. As I wrote in the proposal, putting that class together didn’t go very smoothly:

    The biggest challenge I faced in putting together my WebRTC class over the last year was the lack of a suitable, up-to-date book for teaching or engaging in self-study of WebRTC—especially in the context of web development. As I detail in the competing books section below, there are a few rapidly aging titles on the market that are now little more than works of history. There is little, in book form or otherwise, that offers a substantial, pragmatic application of the WebRTC specification as it has matured since achieving Candidate Recommendation status in late 2017. (The spec became a full W3C Recommendation on January 26, 2021, while I was preparing this proposal).

    Having scoured the web for as many articles and resources as I could to try to structure my class, I still had to write a lot of material for students—especially source-code examples. It was only after the fog had lifted at the end of fall semester that I realized I might have the material necessary to actually write a book about the topic.

    It was really important to me to have the class structured around the native WebRTC APIs found in the browser, rather than rely on a third-party library. The book will be the same, native-API deal: although I’ll be using Socket.IO as a signaling server, all of the client-side code uses nothing but the available browser APIs (occasionally with an assist from the adapter.js to shim certain browsers as needed).

    So that’s my news. I’ll be posting about the book here as I write it. And you’re welcome to message me about it on Twitter @stolleydotdev, of course.

  • Stanford researchers identify four causes for ‘Zoom fatigue’

    • 25 February 2021
    • news.stanford.edu

    An article about psychology as well as interface design:

    “Videoconferencing is a good thing for remote communication, but just think about the medium – just because you can use video doesn’t mean you have to,” Bailenson said.

    On its surface, video seems like it would add a more personal dimension to a connection. But it’s too different from the in-person conversations that most everyone craves after a year living in a pandemic.

    A few of my friends and some of my professional colleagues have found ourselves deliberately trying to hold more audio-only chats when possible. (Fun fact that too few Apple users seem to know: You can have audio-only chats on FaceTime with crystal-clear audio, even on your Mac.)

  • Dynamic Namespaces in Socket.io

    • 30 October 2020

    I’m teaching socket.io as a convenient WebRTC signaling channel in my WebRTC class this semester. As part of prepping that, I finally had to sit down and figure out dynamic namespaces in socket.io. There is some really tricky business to namespaces, dynamic or otherwise, particularly when it comes to listening for messages and events server-side sent from connected clients on the namespace.

    In short, namespaced sockets work differently on the client from how they do on the server.

    On the client, we simply create a namespaced socket connection (ns, here with a Google Meet–like namespace of /jkl-mnop-qrs) and both listen and emit events on it directly:

    // Client-side code (site.js)
    
    var ns = io('/jkl-mnop-qrs'); // ordinarily set dynamically in JavaScript, somehow
    
    ns.on('message', function(data) {
      console.log('Message received: ' + data);
    });
    
    document.querySelector('body').addEventListener('click', function(e) {
      console.log('Body was clicked');
      // the `send()` method is essentially shorthand for `emit('message', data)`;
      // that is, the `send()` method emits the `message` event for us:
      ns.send('Someone clicked the body element');
    });
    

    In that example, the listener ns.on(...) handles incoming messages and sends a pre-determined message to the socket server when someone clicks anywhere in the <body> element. Both use the ns object created from calling the io() constructor.

    On the server, though, it’s a completely different, more complicated story.

    // Server-side code (app.js)
    
    const namespaces = io.of(/^\/[a-z]{3}-[a-z]{4}-[a-z]{3}$/);
    
    namespaces.on('connection', function(socket) {
      const namespace = socket.nsp;
    
      // You can emit messages directly on the `namespace` object...
      namespace.emit('message', `Successfully connected on namespace: ${namespace.name}`);
    
      // ...BUT--BUT, BUT, BUT--you must listen for messages coming from the clients
      // on the socket (`socket`) object, NOT the namespace:
      socket.on('message', function(data) {
        console.log('A message was received from a client: ', data);
        // AND if you want to do a broadcast emit -- which sends the message to
        // all the connected clients *except* for the sender -- you MUST use the
        // socket object (`socket`), as the `namespace` does not understand the
        // `broadcast` method:
        socket.broadcast.emit('message', data);
      });
    });
    

    To summarize the content of the comments: you listen for connections on the namespaces that match the pattern in socket.of(...). You can emit messages on the namespace returned by namespaces.on(...), but you cannot listen for incoming messages or any other events on namespace. Instead, you listen on the socket object (socket) created on the connection event.

    Additionally, if you want to broadcast a message (which sends the message to all connected clients except the sending client), you must use socket.broadcast.emit.

    So a simplified version of the code above looks like this:

    // Server-side code (app.js)
    
    const namespaces = io.of(/^\/[a-z]{3}-[a-z]{4}-[a-z]{3}$/);
    
    namespaces.on('connection', function(socket) {
      const namespace = socket.nsp;
    
      socket.emit('message', `Successfully connected on namespace: ${namespace.name}`);
    
      socket.on('message', function(data) {
        console.log('A message was received from a client: ', data);
        socket.broadcast.emit('message', data);
      });
    });
    

    Now the namespace variable is only being used for diagnostic purposes (Successfully connected on namespace: ${namespace.name}).

    Everything else is listening or emitting on the socket object (socket) returned to the initial namespaces.on(...) callback on each client connection.

    The thing that is tricky to grasp (and that cost me about 3 hours of my life) is that that socket object is unique to the connection on each namespace. This is not properly reflected ANYWHERE in socket.io’s documentation, which makes me crazy. And yes, I should write something and submit a pull request. I know.

  • Styling Empty Grid Cells with Generated Content

    • 9 October 2020
    • www.smashingmagazine.com

    Although from 2018, this article from the incomparable Rachel Andrew was exactly what I needed to solve a CSS grid problem today, where an empty cell needed to have the same color as an adjacent one’s content. And because the adjacent cell’s height was determined by another cell’s content, I couldn’t simply fudge it with padding.

    Enter Rachel’s technique:

    CSS Generated Content uses the ::before and ::after CSS pseudo-classes along with the content property to insert some kind of content into the document. The idea of inserting content might lead you to think that this is for inserting text, and while this is possible, for our purposes we are interested in inserting an empty element as a direct child of our Grid Container. With an element inserted we can style it.

    There’s also a bonus section to the article for using this technique to add typographic flourishes, such as lines to either side of a heading’s text.

  • The lazysizes lazy-loader

    • 4 October 2020
    • github.com

    A weird little side-obsession of mine for a few years has been trying to imagine a native, JavaScript-free way of making use of either srcset or the <picture> element on something like a set of images in a flex container, where each individual image might not be the same size as the others, depending on the way the flex is configured. (You can see the effect I’m talking about on my overview page of courses that I teach.)

    Obviously in a server-generated page of HTML, it would be easy to count the total number of items, figure out the nth item at each viewport size that might be larger, and insert the correct sizes values for srcset or <picture>.

    But what about just vanilla, hand-written HTML? I’ve yet to imagine a viable solution (let alone try to write one), but I did stumble upon this JavaScript library called lazysizes that’s using JavaScript to do what I’m thinking about (but that I’d rather do with pure HTML and CSS):

    It can automatically calculate the sizes attribute for your responsive images, it allows you to share media queries for your media attributes with your CSS, helping to separate layout (CSS) from content/structure (HTML) and it makes integrating responsive images into any environment really simple.

    Hanging onto this for experimentation and at least a study in prior art.

  • Archive of All Posts
Site content & design by Karl Stolley. Source on Github.