SlideShare a Scribd company logo
Programming Webrtc Build Realtime Streaming
Applications For The Web 1st Edition Karl
Stolley download
https://ebookbell.com/product/programming-webrtc-build-realtime-
streaming-applications-for-the-web-1st-edition-karl-
stolley-59015078
Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be
interested in. You can click the link to download.
Programming Webrtc Build Realtime Streaming Applications For The Web
1st Edition Karl Stolley
https://ebookbell.com/product/programming-webrtc-build-realtime-
streaming-applications-for-the-web-1st-edition-karl-stolley-58962332
Programming Webrtc Karl Stolley
https://ebookbell.com/product/programming-webrtc-karl-stolley-58962336
Programming Webrtc 1st Edition Karl Stolley
https://ebookbell.com/product/programming-webrtc-1st-edition-karl-
stolley-58968442
Programming Languages And Systems 31st European Symposium On
Programming Esop 2022 Held As Part Of The European Joint Conferences
On Theory And Practice Of Software Etaps 2022 Munich Germany April 27
2022 Proceedings Ilya Sergey
https://ebookbell.com/product/programming-languages-and-systems-31st-
european-symposium-on-programming-esop-2022-held-as-part-of-the-
european-joint-conferences-on-theory-and-practice-of-software-
etaps-2022-munich-germany-april-27-2022-proceedings-ilya-
sergey-44887738
Programming 101 Learn To Code Using The Processing Programming
Language 2nd Edition 2nd Jeanine Meyer
https://ebookbell.com/product/programming-101-learn-to-code-using-the-
processing-programming-language-2nd-edition-2nd-jeanine-meyer-46238180
Programming 101 The How And Why Of Programming Revealed Using The
Processing Programming Language Jeanine Meyer
https://ebookbell.com/product/programming-101-the-how-and-why-of-
programming-revealed-using-the-processing-programming-language-
jeanine-meyer-46318424
Programming And Gui Fundamentals Tcltk For Electronic Design
Automation Suman Lata Tripathi
https://ebookbell.com/product/programming-and-gui-fundamentals-tcltk-
for-electronic-design-automation-suman-lata-tripathi-46318712
Programming With Openscad A Beginners Guide To Coding 3dprintable
Objects 1st Edition Justin Gohde
https://ebookbell.com/product/programming-with-openscad-a-beginners-
guide-to-coding-3dprintable-objects-1st-edition-justin-gohde-46410140
Programming In Two Semesters Using Python And Java Quentin Charatan
https://ebookbell.com/product/programming-in-two-semesters-using-
python-and-java-quentin-charatan-46494972
Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley
Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley
Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley
Early praise for Programming WebRTC
Programming WebRTC is an exceptional book that teaches WebRTC theory through
practical application of the specification. Through an iterative approach to imple-
menting a WebRTC application, Programming WebRTC somehow makes an ex-
tremely difficult and nuanced topic approachable and easy to understand. It is a
must-read book on the topic.
➤ Stephen Watzman
Software Engineer
Karl has a passion for WebRTC that shows in this book, and combining that with
his experience as an educator and a technologist, he’s crafted a book that is
very accessible and informative. WebRTC development has a lot of nuances, but
Karl approaches the topic in a way that is both reader-friendly and technically
comprehensive. I highly recommend it to anyone new to WebRTC!
➤ Arin Sime
CEO/Founder, WebRTC.ventures
Dr. Karl Stolley, a developer, technical author, and prominent WebRTC expert,
has consistently addressed the field’s challenges and advancements in conferences,
including the IIT RTC, and other academic settings. His insights highlight the
dynamic nature of WebRTC implementations, yet underscore their current robust-
ness through practical examples often employed in his teaching.
➤ Alberto Gonzalez Trastoy
Software Consultant and CTO, WebRTC.ventures
This book expertly blends technical depth with humor, making WebRTC accessible.
Even complex topics like signaling channels are tackled with wit, reminding us
that a well-designed interface beats the “Imagine this doesn’t look like garbage”
approach any day.
➤ Paul Freiberger
co-author, Fire in the Valley
Programming WebRTC
Build Real-Time Streaming Applications for the Web
Karl Stolley
The Pragmatic Bookshelf
Dallas, Texas
For our complete catalog of hands-on, practical, and Pragmatic
content for software developers, please visit https://pragprog.com.
Contact support@pragprog.com for sales, volume licensing, and support.
For international rights, please contact rights@pragprog.com.
The team that produced this book includes:
Dave Thomas
Publisher:
Janet Furlow
COO:
Susannah Davidson
Executive Editor:
Michael Swaine
Development Editor:
Vanya Wryter
Copy Editor:
Potomac Indexing, LLC
Indexing:
Gilson Graphics
Layout:
Copyright © 2024 The Pragmatic Programmers, LLC.
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or
transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or
otherwise, without the prior consent of the publisher.
When we are aware that a term used in this book is claimed as a trademark, the designation is
printed with an initial capital letter or in all capitals.
ThePragmaticStarterKit,ThePragmaticProgrammer,PragmaticProgramming,PragmaticBookshelf,
PragProg and the linking g device are trademarks of The Pragmatic Programmers, LLC.
Every precaution was taken in the preparation of this book. However, the publisher assumes no
responsibility for errors or omissions, or for damages that may result from the use of information
(including program listings) contained herein.
ISBN-13: 978-1-68050-903-8
Encoded using recycled binary digits.
Book version: P1.0—July 2024
Contents
Acknowledgments . . . . . . . . . . . ix
Preface . . . . . . . . . . . . . . xi
1. Preparing a WebRTC Development Environment . . . . 1
Installing Node.js 2
Downloading the Supporting Code
and Installing Dependencies 2
Serving HTTPS in Development 4
Choosing a Development Browser 6
Starting and Stopping the Server 7
2. Working with a Signaling Channel . . . . . . . 11
Preparing a Basic Peer-to-Peer Interface 11
Adding Video Elements: Self and Peer 14
Styling the Core App Elements 17
Adding Functionality to the Call Button in JavaScript 22
Positioning WebRTC as a Front-End Technology 25
Using a Lightweight Signaling Channel 27
Connecting to the Signaling Channel 32
3. Establishing a Peer-to-Peer Connection . . . . . . 37
Requesting User-Media Permissions 37
Setting Up the Peer Connection 42
Building Connection Logic to the “Perfect Negotiation” Pattern 51
Receiving Media Tracks 59
Testing Out Your First Peer-to-Peer App 60
4. Handling Data Channels . . . . . . . . . . 63
Adding Basic Visual Effects to User Videos 64
Determining Peer-Connection States 68
Applying Filters Remotely with Data Channels 71
Uniquely Identifying Data Channels 73
Adding a Text-Chat Feature 75
Adding Logic to Handle Chat Events 81
Setting Up the Text-Chat Data Channel 83
Building a Message Queue 86
5. Streaming Complex Data . . . . . . . . . . 93
Structuring Chat Messages in JSON 93
Adding Mic and Camera Toggles 98
Refining the Initial Properties on Self and Peer 99
Building A/V Toggles 105
Sharing Features over Data Channels 112
Sending Images over the Chat 117
Sending and Receiving Binary Data 125
6. Managing Multipeer Connections . . . . . . . 133
Learning from a Failed Peer-to-Peer Call 134
Working with a Multipeer-Ready Signaling Channel 136
Revising the Signaling Logic on the Client 139
Generating Video Structures on the Fly 145
Initializing Peers as Needed 150
Fleshing out the Skeletal Signaling Callbacks 152
Working with Peer IDs in the handleScSignal() Callback 156
Restructuring WebRTC Callbacks with Closures 159
Sharing Features over Multipeer Data Channels 165
7. Managing User Media . . . . . . . . . . 173
Determining Device Availability 174
Detecting Device Changes 182
Removing User Media for Remote Peers 187
Programmatically Recognizing Denied Media Permissions 190
Setting and Applying Media Constraints Objects 193
8. Deploying WebRTC Apps to Production . . . . . . 197
Configuring a WebRTC App for Public Deployment 198
Configuring a Server to Host Your WebRTC App 202
Deploying Your App with Git 207
Monitoring Your App with PM2 210
Configuring Nginx for Reverse Proxies 212
Setting Up Your Own STUN/TURN Server 214
Contents • vi
A1. Connection Negotiation in Legacy Browsers . . . . 225
Implementing Backward-Compatible Fixes 225
Bibliography . . . . . . . . . . . . 231
Index . . . . . . . . . . . . . . 233
Contents • vii
Acknowledgments
I wrote this book over the span of two very different careers and three jobs,
with a global pandemic thrown in for good measure. Screenshots found
throughout the book document my various hair lengths and unkempt beard
for posterity.
Thank you to my past and current employers, supervisors, and coworkers
who in ways big and small graciously accommodated and supported my work
on this: Ray Trygstad, Nina Kuruvilla & Kwindla Kramer, and Alya Abbott &
Tim Abbott.
Thank you to the students in my WebRTC classes who read and worked
through the earliest iterations of this book, providing useful feedback that
shaped what this work would ultimately become: Hareem Akram, Jon Andoni
Baranda, Yelitza Castro, Chia-Chi Chang, Julaam J. Diop, Michael P. Kaczowka,
David Singer, Rida Tariq, and Naveed Zahid.
Thank you to my professional colleagues and friends who offered encourage-
ment, interest, and support for this project: Nancy DeJoy, Molly Holzschlag,
Carrie Malone, Jayne Mast, Kate McLaughlin, Adria Neapolitan, Brian Okken,
Arin Sime, Alberto Gonzalez Trastoy, and Brian Watts.
Thank you to my technical reviewers: Martin Deen, Tsahi Levent-Levi, and
Kimberlee Johnson. Additional thanks to Dasha Day Hisholer for also
reviewing this work while it was struggling to become a proposal. Special
thanks to Aman Agrawal for his careful review of the completed book. I am
solely to blame for any errors and shortcomings that remain.
Thank you to the team at or closely orbiting Pragmatic Programmers: Tammy
Coron, Tim Mitra, Erica Sadun, and especially Margaret Eldridge who, among
other things, shepherded this work in its proposal form to a head-spinningly
quick acceptance.
report erratum • discuss
A special thank you to my editor, Michael Swaine, for his encouragement,
bone-dry wit, and saint-like patience in helping me see this book through to
completion. Working with him was the best writing experience of my career.
And the deepest thank you to my family: to my dog, Hank, for his countless
hours of unflagging dog assistance and posing for screenshots in front of a
laptop. And to Amy, my incredible wife of almost 20 years, for her patience,
support, and boundless love: you are the very best one.
Acknowledgments • x
report erratum • discuss
Preface
WebRTC—or Web Real-Time Communication—is a standardized API exposed
in all modern web browsers. The World Wide Web Consortium accepted the
WebRTC specification as a full recommendation after a long decade of devel-
opment.1
Couple a complete, stable specification with browser support for
WebRTC ranging from rock-solid to serviceable, and you find yourself in a
perfect environment for developing and deploying real-time web applications
in the browser.
Like any Web API, WebRTC doesn’t enjoy a perfectly spec-aligned implemen-
tation in any browser. But this book will start you on your journey to devel-
oping real-time streaming applications, all according to the certainty of a
stable specification. You’ll also learn to write elegant, backward-compatible
code to get your WebRTC apps working across the widest possible range of
recent and modern browsers. Desktop and mobile devices, too. Support for
WebRTC is everywhere.
Your WebRTC Journey
You’ll start your journey by getting straight to work on building a basic
WebRTC application to support peer-to-peer video calling. Chapter by chapter,
you’ll refine that app and its core logic to then spin up additional WebRTC-
powered apps that will have your users sharing all manner of data with one
another, all in real time.
This book treats WebRTC as a part of the Web Platform. No third-party
libraries or heavy downloads are required for you to make your way through
this book, or for your users to use the WebRTC apps you build: you’ll be
writing and strengthening your knowledge of modern HTML, CSS, and Java-
Script to get the most out of browser-native WebRTC APIs.
1. https://www.w3.org/TR/webrtc/
report erratum • discuss
From the outside, WebRTC is pretty daunting. Okay, really daunting. I’m
proud of you just for peeking inside this book. But don’t put it down just
because WebRTC will challenge you and twist your brain around in some
profound ways. That’s both expected and totally okay. It means you’re on the
path. We’ll walk it together. Things look better and more manageable from
the inside.
This book will get you on the path to where you want to go with WebRTC right
away. You won’t find any throwaway code or opaque, puzzling examples here.
We’re going to be developing real WebRTC applications together from the
outset. And it will be from those real, functioning applications that we will
tease out how WebRTC works. By the end of your journey, you will have all
the foundational skills and knowledge you need to build your own wildly
imaginative real-time applications. And because work on the WebRTC speci-
fication continues, you will also learn how to stay on top of the latest changes
and discussions.
Who Should Read This Book?
This book is aimed at intermediate and advanced web designers and developers
looking to explore and implement real-time communication features in new
or existing web applications.
Whether you consider yourself a designer or a developer, WebRTC is one of
those rare Web APIs where design and development converge head on: not
just conceptually, but in the actual code you’ll be writing. WebRTC is a front-
end technology that requires only the teeniest, tiniest server-side component,
which I have provided for you in the codebase that accompanies this book.
Almost all of your work will be run and rendered directly in the browser.
You should have at least some knowledge of JavaScript, along with HTML
and CSS. I’ll do my best to fill in any gaps that might arise for you and point
out additional books and resources that you might find useful. You’ll find
yourself working with other Web APIs in this book—not just WebRTC. Many
of those will equip you with added knowledge to enhance your work on all
kinds of web applications, whether or not they include a real-time component.
What’s Covered (And What’s Not)
This book covers WebRTC’s APIs as natively implemented in recent and
modern web browsers. You’ll be working with those APIs directly in vanilla
JavaScript to build your knowledge and command of WebRTC independent
of any third-party libraries. The promise of WebRTC has always been to provide
Preface • xii
report erratum • discuss
real-time communication right in the browser without requiring users to
download special plugins or add-ons. Your users’ browsers already have
everything needed to power your real-time app. All you have to do is build it!
But to build a stunningly accessible and usable real-time application requires
more than just a strong JavaScript foundation. It is an all-too-common mistake
to think of WebRTC as just another conduit for media streams and application
data. Because of its real-time component, WebRTC knits together two or more
remote interfaces where real, live users are interacting and cooperating with
one another. Building a WebRTC app without thinking carefully about the
user interface would be like building a bicycle without the seat and handlebars.
Ouch, right? So don’t be surprised to spend some time—perhaps more than
you’d expect—working with HTML and CSS, too.
You will be doing WebRTC development within the friendly confines of your
local network for most of the book, but a chapter at the end will walk you
through the necessary requirements and steps for deploying your real-time
applications to the web, and testing them out.
So what’s not covered? In a phrase, this book does not cover issues of scale
or millisecond-obsessed WebRTC optimization. While there are a growing
number of server-, platform-, and system-based implementations of WebRTC,
as well as numerous WebRTC-based communication platforms as a service
(CPaaS), those are all beyond the scope of this book. That means you won’t
find coverage here of scaling apps up to handle dozens or thousands of con-
current users supported by server-side technologies like selective forwarding
units (SFUs) or multi-conference units (MCUs). Although you will work with
a small server that provides a signaling channel, that is the extent of the
server-side content in this book.
And while you’ll learn about some fundamentals of streaming-media CODECs
and optimization, this book does not go deep on those topics. Nor does it
encourage session description protocol (SDP) “munging” to coerce browsers
to use a particular CODEC, or fiddling with RTCRtpTransceiver objects. Native
browser code for those matters is better tuned and tested than app-based
adjustments likely ever will be.
However, the core principles of WebRTC that we’ll look at in depth—working
with a signaling channel, establishing peer connections, adding and managing
media streams and data channels—will have you well prepared to tackle
WebRTC implementations and third-party services wherever you might
encounter them.
report erratum • discuss
What’s Covered (And What’s Not) • xiii
How This Book Is Organized
This book is organized in a set of sequential chapters. It’s meant to be read
more or less front to back. If that feels overly prescriptive and stifling, or if
you’ve just got a rebellious streak that you like to take out on tech books, try
anyway to make it through at least the first four chapters before you jump
around to the later ones whose topics most interest you.
In Chapter 1, you’ll learn how to set up a development environment that will
play nicely with WebRTC. You’ll also learn how to get the most out of the
starter and example code that accompanies the book.
With your development environment set up and tested out, Chapter 2 dives
right into the only necessary server component of WebRTC apps: a signaling
channel. As part of working with the signaling channel, you’ll start to build
the basic interface of your first WebRTC app, which provides peer-to-peer
video calls.
Chapter 3 is where you’ll really hit your stride working directly with WebRTC’s
APIs, all of which orbit around the RTCPeerConnection interface. By the end of
this chapter, you’ll be streaming video between two connected peers which—to
start—will be two browser windows on your desktop.
Streaming real-time user video and audio is WebRTC’s most famous feature.
But that’s not its only feature. Over the course of Chapter 4 and Chapter 5,
you’ll go from streaming basic data to streaming more complex data—
including JSON as well as images and other binary files. WebRTC provides
a powerful and flexible low-level interface for streaming arbitrary application
data between two peers, all in real time. You’ll learn to command that interface,
and abstract away subtle differences found in browsers with incomplete
WebRTC implementations. You’ll even bring what you learn full circle by
safely implementing user audio to complete the silent streaming video you’ll
work with at first.
Buckle your seatbelt when you get to Chapter 6. Connecting two peers is
one thing. But how about connecting three or more peers? Chapter 6 will
have you establishing WebRTC calls using a mesh-network topography to
enable multiple peers to join the same call simultaneously. You’ll also experi-
ence the theoretical and practical upper limits on the number of peers who
can join a call on a mesh network, depending on what your app does and the
amount of bandwidth and processing power it consumes.
In Chapter 7, you’ll work more in depth with the MediaDevices interface on the
Media Capture and Streams API to do things like help users determine what
Preface • xiv
report erratum • discuss
mics and cameras they have available, and handle edge cases in your logic
when either there are no devices available, or users deny permission to access
them. You’ll learn to do some minor media-stream optimization, too, with the
aid of the built-in, real-time statistics that WebRTC implementations provide
in the browser.
Closing out the body of the book, in Chapter 8, you’ll learn how to deploy
WebRTC applications to production. You’ll find a concrete deployment
example, but you’ll also learn how to adjust that example to suit your own
needs and preferences.
And finally, you will find an appendix at the end of the book that will show
you the necessary fixes for making your WebRTC applications work with
legacy browsers that don’t support the WebRTC APIs necessary for perfect
negotiation.
Online Resources
You can download the source code for studying and working alongside the
examples in the book from pragprog.com.2
If you spot an error or even just
come across something that is blocking your path forward in the book, con-
sider this my personal invitation to you to join and post to the book’s forum
on DevTalk.3
If you would like to contact me directly, I am available on Mastodon at
@stolley@hachyderm.io.4
I also blog about WebRTC and other web topics at
https://stolley.dev/
All right. Enough with the formalities that we classy preface readers enjoy
while thoughtfully adjusting our monocles from the comfort of a high-back
leather chair. Let’s pop out the monocle, pull open a laptop, and get down to
it: it’s time to set up a development environment that will be your trusty
companion on your exciting, monocle-free journey with WebRTC.
2. https://pragprog.com/titles/ksrtc/
3. https://devtalk.com/books/programming-webrtc/errata
4. https://hachyderm.io/@stolley
report erratum • discuss
Online Resources • xv
CHAPTER 1
PreparingaWebRTC
DevelopmentEnvironment
Exciting new technologies often require developers to level up the sophistica-
tion of their development environments. WebRTC is no exception. Almost
everything you’ll be doing with WebRTC happens in and between browsers.
While you’ll be writing HTML, CSS, and JavaScript just as you would for any
other web application, there are some important things to set up to smooth
your way through the rest of the book and your work building real-time web
applications.
In this chapter, you’ll install Node.js if you haven’t already. You’ll also learn
where to get yourself a copy of the code that accompanies this book, and
you’ll take a brief tour of the code’s organization so you can find what you
need, when you need it. You’ll then generate and make use of your own self-
signed certificates for serving HTTPS in development. HTTPS is necessary to
fully and reliably access many newfangled, highfalutin Web APIs—including
WebRTC, even in development.
You’ll choose a WebRTC-ready development browser (spoiler: Chrome or Firefox),
fire up the server that’s packed in with the book’s code to serve your in-progress
work or the completed examples, and heroically machete your way through the
dire security warnings that your browser will throw at you over your self-signed
certificates.
It’ll be a little bit of work, but once you’ve set this all up for yourself, you
shouldn’t have to think about any of it again.
All of the setup here should work without much fuss or drama on Unix-like
operating systems, including macOS.
report erratum • discuss
Install Windows Subsystem for Linux
If you’re developing on Windows, you’ll need to install and use
Windows Subsystem for Linux.1
Installing Node.js
The small server I’ve written to support your local WebRTC development relies
on Node.js, a wildly popular JavaScript runtime that you might already be
familiar with and have installed. Let’s figure out if you’ve got a copy of Node.js
already, and install one if you don’t.
If you’re not sure if you have Node.js, run which node on your command line.
You’ll see output showing the path to your Node.js installation, if you have
one (now might be a good time to update it, if you haven’t in a while).
If you see no output or know for a fact you aren’t running Node.js yet, no
problem: there are a few different ways to install it. You can find, download,
and run a Node.js installer for your operating system of choice from nodejs.org.2
Alternatively, if your operating system has a package manager available, such
Homebrew for MacOS3
or your native package manager for Linux, you can
install Node.js that way.
However you opt to install Node.js, it’s generally a good idea to be running
the latest LTS version.4
As a sanity check, once you’ve installed Node.js, you can run which node and
which npm on your command line, which should report the locations where
Node.js and its own package manager, npm, were installed. That’s simply
confirmation that you’ve successfully installed Node.js, and also that your
command line knows where to find it.
Downloading the Supporting Code
and Installing Dependencies
Once you’ve set up Node.js, you should download the code for this book from
pragprog.com.5
Once you’ve downloaded and decompressed the ZIP file,6
move
1. https://learn.microsoft.com/en-us/windows/wsl/install
2. https://nodejs.org/en/download/
3. https://brew.sh/
4. https://nodejs.org/en/about/previous-releases
5. https://pragprog.com/titles/ksrtc/programming-webrtc/
6. https://media.pragprog.com/titles/ksrtc/code/ksrtc-code.zip
Chapter 1. Preparing a WebRTC Development Environment • 2
report erratum • discuss
the unzipped code/ directory somewhere you can conveniently access from the
command line. You might also want to rename the directory to something
more recognizable than code/.
Having done that, use your command line to navigate to the directory you’ve
set up. Once you’re there, you need to install the dependencies for the server
that you’ll rely on as you work through the book. Run this command:
$ npm install
A bunch of output will fill the terminal screen, but installation should only
take a few minutes at most. Probably less.
If you see warnings about deprecated packages, you can try running npm audit
fix or the more aggressive npm audit fix --force to try and resolve matters. But for
doing WebRTC development, it’s okay to ignore such warnings altogether.
Finding Your Way Around the Code Directory
I’ve prepared the book’s accompanying code to include the examples that I’ve
written and a separate starter directory for you to follow along in the book
and experiment on your own.
Here is a brief look at what you’ll find in the code directory and where:
• The book’s completed examples are each in their own subdirectory under
demos/. You’ll see references to those files throughout the book.
• There is a www/ directory for you to work in as you make your journey
through the book. If you get stuck, you can always compare your work
against the files in the demos/ directory. Don’t forget to mutter and curse
about me under your breath, which you’ll find therapeutic.
As you work through the book, you’ll often find yourself in those two directo-
ries. But the accompanying code includes some additional files and directories
that you might be curious about:
• The deploy/ directory contains a standalone WebRTC app that you’ll use
in Chapter 8, Deploying WebRTC Apps to Production, on page 197.
• The server.js file contains a basic web server using the ExpressJS frame-
work. This file includes the basic signaling channels that are discussed
later in the book.
• The scripts/ directory contains a server startup script, start-server, which
(surprise!) starts the server, but you can simply run npm start on your
command line to fire up the server.
report erratum • discuss
Downloading the Supporting Code and Installing Dependencies • 3
• For further study and your future experiments, you will find starter scripts
for establishing peer connections in the _starter/ directory. p2p.js contains
the logic necessary to establish a connection between two peers, and
multi.js is enhanced with logic to establish a connection among three or
more peers.
Serving HTTPS in Development
In order to develop WebRTC applications, it’s necessary to configure your
development environment to serve HTTPS. Browsers disallow access to a
number of APIs, including those that grant access to your microphone and
camera, unless you’re serving HTTPS—even in development.
To serve HTTPS in development, you’ll need to create and use your own self-
signed certificates. It doesn’t take too much work to generate self-signed cer-
tificates, but you can still impress your friends that you managed to serve
HTTPS over localhost.
Although self-signed certificates are not suited for use on the open web, they
are perfectly acceptable for testing within the familiar comfort of your local
network. The browsers you test your work on will nevertheless protest
mightily, and they’ll do their best to scare you away from using self-signed
certificates. And when that doesn’t work, they’ll try to make you feel bad about
yourself. But don’t worry, and don’t feel bad: we’ll diffuse their unwarranted
scare-and-shame tactics at the end of this chapter.
Generating Self-Signed Certificates
You’ll need access to openssl to generate your own certificate and key files on
your operating system. MacOS and virtually all Unix-like operating systems
and Linux distributions ship with openssl. If you’re a Windows user, you might
need to install openssl yourself. The OpenSSL wiki maintains a list of download-
able binaries.7
If you run Git Bash on Windows,8
it already includes openssl.exe,
which you can also use.
Before you generate the certificate files, you’ll need to create an easy-to-
remember place for them to live. I recommend creating a Certs directory in
your home directory:
$ mkdir ~/Certs
7. https://wiki.openssl.org/index.php/Binaries
8. https://gitforwindows.org/
Chapter 1. Preparing a WebRTC Development Environment • 4
report erratum • discuss
The book’s supporting code you downloaded includes a script you can run
to generate your certificate files. You’ll need to change into the directory where
you’re storing the book’s code in order to do this. The base command is npm
run ssl-keys, and it needs two arguments: the path to the directory you created
(keydir) and the number of days before your self-signed certificates expire
(numdays).
For example, to create self-signed certificate files in ~/Certs that won’t expire
for about five years (1825 days), you’d run this command in the book’s code
directory:
$ npm run ssl-keys --keydir="$HOME/Certs" --numdays=1825
Whatever values you choose, use the $HOME variable instead of the tilde, ~,
and ensure there are no spaces around your equals signs.
If you’re the suspicious type, you can examine the ssl-keys script in the pack-
age.json file before you run it. It’s based on a command suggested by Let’s
Encrypt,9
which is ordinarily in the business of offering free certificates that
can be used on world-facing websites. If you’d prefer, you can head over to
Let’s Encrypt’s original post10
and copy from there into your command line
instead of using the npm script. You’ll need to change into your certificates
directory before running the Let’s Encrypt command to generate the certificate
and key files. By default, openssl creates certificates that expire in one year. If
you want to go longer than that without having to generate new ones, add
-days followed by some number of days to the Let’s Encrypt script, like -days
1825 to create five-year certificates.
However you generate your keys, once you hit Return, it will only take a
moment to generate the key and certificate files in the directory you’ve chosen,
like ~/Certs. Creating those files is an important first step to serving HTTPS.
But you’ll also need to make sure that any scripts and servers you run can
find your certificate files with as little fuss as possible. Let’s set that up next.
Storing the Certificate File Locations in Environment Variables
You’ll need to export two environment variables from your command line’s
startup scripts. Those variables will point to the location of your self-signed
certificate files. Your startup scripts will be in a file in your home directory
called .bashrc or .bash_profile, if you’re a bash user, .zshrc if you’re a zsh user, or
possibly even a file called .profile.
9. https://letsencrypt.org/
10. https://letsencrypt.org/docs/certificates-for-localhost/
report erratum • discuss
Serving HTTPS in Development • 5
Whichever startup file your command line uses, open it in your favorite text
editor, and add the following lines:
# SSL Keys
export LOCALHOST_SSL_CERT="$HOME/Certs/localhost.crt"
export LOCALHOST_SSL_KEY="$HOME/Certs/localhost.key"
Be sure not to put any spaces around the equals signs, =. And don’t forget
to adjust the path if you’ve saved your keys somewhere different from ~/Certs
or named them something other than localhost.crt and localhost.key. Once you’ve
saved your startup file, reload it in your terminal using the source command.
You’ll need to reference the name of the file your command line uses. In this
example, the file is called .zshrc:
$ source ~/.zshrc
As a quick sanity check, you can confirm that your command line knows
about these variables by using echo to output their values. To do this, prefix
the variable names with a dollar sign:
$ echo $LOCALHOST_SSL_CERT
If everything has gone according to plan, you’ll see the certificate location you
specified output by your command-line shell for LOCALHOST_SSL_CERT. On MacOS,
for example, that will look something like /Users/username/Certs/localhost.crt. You
can check the LOCALHOST_SSL_KEY value the same way, if you’d like.
Choosing a Development Browser
You’ll have the best possible development experience running the latest version
of either Chrome or Firefox. I personally prefer Firefox Developer Edition,11
but the choice is yours.
Whichever browser you choose, you’ll need to open its developer console and
disable caching, so you always load the latest version of your CSS and Java-
Script as you work. Firefox and Chrome both have the Disable Cache option
under the Network tab of the developer pane. If you’re like me, you’ll opt to
pop the developer console into its own window to maximize your available
screen space. You’ll be building responsive interfaces that take advantage of
the entire viewport. And you know the kinds of divas interfaces can be: steal
their spotlight in any way and they’ll make your life miserable and spread
salacious rumors about you to their friends.
11. https://www.mozilla.org/en-US/firefox/developer/
Chapter 1. Preparing a WebRTC Development Environment • 6
report erratum • discuss
What About Safari?
Safari has historically lagged in its WebRTC implementation, but as of April 2022’s
release of Safari 15.4, it is also up to snuff. If you opt to use Safari as your develop-
ment browser, or if you want to test out your work on an iPhone or iPad, please be
sure you’re running at least Safari 15.4.
Incomplete WebRTC implementations in older versions of Safari and other browsers
(Firefox prior to version 80, and Chrome prior to version 75) will give you a series of
raging headaches. Consult Appendix 1, Connection Negotiation in Legacy Browsers,
on page 225 to learn about the fallbacks you’ll need to add to your code for the sake
of older browsers, for as long as they remain in use (if history is a guide, that will be
awhile).
Starting and Stopping the Server
I’ve tried to make it as painless as possible for you to serve both your own
work and the completed demos over localhost. To serve your own files as you
work on them, run npm run start or simply npm start on the command line. Your
files are more important, so they get the more convenient commands.
Remember to Install Dependencies
If you haven’t yet run npm install, be sure to do so before starting
the server, or if you encounter errors about missing modules.
To serve the book’s completed examples, you’ll need to run the slightly more
verbose command npm run start:demos. No space on either side of the colon.
Whenever you start the server, you’ll see output like this in your terminal
window:
signaling-server: ** Serving from the www/ directory. **
signaling-server:
signaling-server: App available in your browser at:
signaling-server:
signaling-server: -> https://127.0.0.1:3000/
signaling-server: -> https://192.168.1.6:3000/
signaling-server:
signaling-server: Hold CTRL + C to stop the server.
signaling-server:
signaling-server: +0ms
report erratum • discuss
Starting and Stopping the Server • 7
Each time you start the server, it will tell you which directory you’re currently
serving from (again: www/ with your work, demos/ with the completed examples)
and at least two addresses for reaching the server from your development
browser of choice. Whenever you need to stop the server, hold down CTRL + C.
The first time you start the server, your operating system might notify you
about a firewall restriction of some kind. Because you’ll eventually be hitting
this server from other devices connected to your local network, instruct your
OS to allow incoming connections.
With the server still running, you can open your browser to https://localhost:3000/.
Of course localhost is a shortcut for 127.0.0.1, which you can also use if you get a
thrill out of typing numbers and dots: https://127.0.0.1:3000/. Don’t worry for now
about the second IP address you see. It will almost certainly be different from
192.168.1.6, but eventually you will be able to use whatever that second address
is to test your app using other devices connected to your local network.
One big gotcha: you absolutely must type out the full https:// protocol portion
of these URLs. If you leave the protocol off, your browser will try to establish
a connection over http://. The server isn’t actually listening for HTTP, but HTTPS.
Your browser doesn’t know that, though, so it’ll dismissively inform you that
it’s unable to connect. And you’ll lose a whole part of the day tracking down
that missing s, which is made even more difficult to spot in browsers that
hide the http:// protocol string on HTTP URLs.
Once you enter the exact HTTPS-serving address, you’ll immediately hit a
snag. Your browser, which demanded that you type out https:// in the address
bar, now wants you to know that it has a big problem with your self-signed
certificates. Instead of seeing the page the server is serving, you’ll get a
security warning.
Getting Past Browser Security Warnings
After you point your browser to any of the server’s local https:// URLs, the
browser viewport will fill with dire warnings, bad omens, and tales of an
ancient curse. Firefox Developer Edition, for example, will present the screen
on page 9.
On the open web, these security warnings are a good thing. But in develop-
ment, they’re just overly dramatic, pearl-clutching pains in the neck.
The good news is that you’ll likely see this warning only the first time you hit
your local address, and you will have to take these steps only once, too. To
Chapter 1. Preparing a WebRTC Development Environment • 8
report erratum • discuss
get Firefox to chillax and let you get on with your work, click the Advanced…
button, and then the Accept the Risk and Continue button in the box that
appears. On Chrome, you’ll likewise click Advanced and then the “Proceed to
localhost (unsafe)” link.
Safari makes you jump through a different series of hoops to accept self-
signed certificates: when confronted by Safari’s warning screen, click on Show
Details, then the “visit this website” hyperlink. That’s all you have to do on
iOS, thankfully. But on MacOS, you’ll then be greeted by a popup asking,
“Are you sure you want to visit this website on a connection that is not pri-
vate?” Click “Visit Website” and then you’ll see another popup: “You are
making changes to your Certificate Trust Settings.” Click Use Password…
and then enter the password you use to log into your Mac.
report erratum • discuss
Starting and Stopping the Server • 9
Once you’ve gone through those steps, you’ll see an overview page I’ve written
for you. On the demos side, the page includes links to each of the demos. On
the working side, from www/, you’ll see some instructions and encouragement.
Next Steps
Excellent. Having viewed one or both of those pages over HTTPS in your
development browser of choice, you can be confident that you’ve successfully
prepared your WebRTC development environment. You’ve installed Node.js,
and you’ve located, downloaded, and used the command line to find this
book’s accompanying source code and install its dependencies. You’ve also
generated your own self-signed certificate files for serving HTTPS in develop-
ment. And finally, you’ve convinced your browser, after some haggling over
security, to accept your self-signed certificate.
In the next chapter, we’re going to get right to work with the server you
installed and fired up in this chapter. You’ll be working with its signaling-
channel component—which we haven’t seen yet—while also constructing a
user interface for your first WebRTC app. As we’ll see, WebRTC meshes
tightly with user interfaces. So we will want to make sure the code we write
for the interface is as well crafted and expertly engineered as everything we’ll
be writing alongside WebRTC’s browser APIs. Let’s get going!
Chapter 1. Preparing a WebRTC Development Environment • 10
report erratum • discuss
CHAPTER 2
WorkingwithaSignalingChannel
In this chapter, you’re going to build the foundations of a video-calling app
that will use WebRTC for connecting two peers who can stream video—and
eventually also audio—to each other, in real time.
You’ll start by building an interface using semantic HTML and CSS, including
a little flexbox for layout. Some of that work will strike you as being very
precise and detailed. But the goal is to build a lean, accessible interface that
also displays responsively across all types of devices. With the interface built,
you’ll then do the necessary work to wire it up with JavaScript for handling
routine events, like clicks, that happen in the browser. Those will eventually
hook into the signaling channel and other WebRTC logic. So it’s necessary to
build the interface first.
With the interface built, we’ll take a look at the peer-to-peer architecture of
WebRTC and how it differs from the more familiar client-server web architec-
ture of HTTP and HTTPS.
From there, we’ll take a sightseeing tour of a crucial piece of technology for
establishing peer connections over WebRTC: a signaling channel, which will
take the form of a small server that you’ve already downloaded with the book’s
companion code. With a better understanding of the signaling channel in
hand, you’ll then write some skeletal, foundational code for simultaneously
connecting multiple pairs of peers over your app.
Preparing a Basic Peer-to-Peer Interface
Let’s begin with a basic process for building something new for the web:
establishing a workable UI concept, structuring it in semantic HTML, and
styling the HTML with just enough CSS to make it responsive across the full
report erratum • discuss
range of web-enabled devices. That will pave the way for much more graceful
and efficient work with JavaScript for stitching the app together.
The UI work here is going to be pretty precise, probably way more than what
you’ve come to expect from books on specialized topics in web development.
The advantage to engaging at least some front-end design in your development
work is that when you’re selling some new API or idea to someone—a team
lead, a manager, a client—you have something to show that actually looks
good and helps to sell the idea a little more effectively than prefacing your
work with “Imagine this doesn’t look like garbage.”
But it’s also never too early to consider the obligations we as developers have
to end users: front-end development involves precise work on interfaces that
real users will have to interact with. Precise UI design is also the cornerstone
of accessibility, which is as essential for real-time communication technologies
as it is for anything else developed for the web. For that reason, it’s nothing
less than a professional obligation to keep users in mind when building
something explicitly for them—no matter how deep down the rabbit hole the
underlying API takes us. With WebRTC, that rabbit hole is deep.
And because the underlying WebRTC API is deep and complex, you’ll also see
that we’re going to spend more than a little bit of time working through some
core fundamentals of JavaScript, including code organization and the
behavior of callback functions. The purpose of that work is twofold: first, as
JavaScript continues to advance as a language, it can be harder to keep all
developers on the same page with its newer features, especially as they relate
to JavaScript’s mainstay features. And second, because of the design of the
WebRTC API, I think you’ll find that you work more effectively within WebRTC’s
dizzying number of event-driven methods and callback functions if the con-
nections between them and vanilla JavaScript are made more explicit. That
will be especially true if your routine, day-to-day interactions with JavaScript
are mediated through a JavaScript framework with a higher-level API than
plain old vanilla JavaScript has to offer.
Designing Peer-to-Peer UI Patterns
Before you start to write a line of code for your video-call app, think for a
moment about the kinds of interfaces that are common in peer-to-peer apps
you’ve used. Their interfaces are generally modeled on one of two patterns—
a caller pattern or a joiner pattern. A caller pattern is asymmetric: certain
peer-to-peer apps, like FaceTime or Skype, and even the telephone, depend
on one peer calling and the other answering. The FaceTime interface, for
example, looks the same for two peers inside and outside of a call. But while
Chapter 2. Working with a Signaling Channel • 12
report erratum • discuss
a call is being placed, the interface is different for both the caller and
answerer. The interfaces return to a symmetric state once the person being
called decides either to answer, causing both interfaces to show the call, or
to decline (or simply ignore), in which case both interfaces return to their
states before the call was placed.
Other peer-to-peer apps, like Google Meet or Zoom, use a symmetric joiner
pattern: instead of setting up a call between a caller and an answerer, sym-
metrically patterned apps treat a call as something that already exists for
users to join whenever they’re ready. The interface’s state depends only on
what any one user is doing: preparing to join a call, participating in a call
after joining, or leaving the call.
WebRTC-backed interfaces can be constructed using either pattern, caller or
joiner. But the joiner pattern simplifies the interface, because there’s no need
to create a screen to handle an incoming call, or wire up a bunch of buttons
to answer or decline. Instead, one button is all that’s needed—Join Call—and
it’s presented to each peer. In the joiner pattern, we don’t even have to think
in terms of someone calling someone else. From the perspective of the peers
on the call, no one cares who joined first. As the designers of the app, we
don’t have to care, either.
Adding a Header and Button
With a joiner pattern in mind, you can start building the HTML and CSS to
structure and style the app. No need to worry about special HTML, CSS, or
JavaScript for a caller or an answerer: everyone on the call is a joiner, so
everyone gets the same code.
Open the www/basic-p2p/ directory and find its index.html file, which already links
to the CSS and JavaScript files you’ll use. Start off by writing a basic header
that has a first-level heading along with a button element for joining the call.
You can write this inside the <main> element you’ll find in the HTML file. The
button will enable users to both join and leave the call, but you should set it
up initially as a join button. When the app first loads, users will need to join
the call:
demos/basic-p2p/index.html
<main id="interface">
<header id="header" role="banner">
<h1>Welcome</h1>
<button class="join" type="button" id="call-button">Join Call</button>
</header>
</main>
report erratum • discuss
Preparing a Basic Peer-to-Peer Interface • 13
The type="button" attribute might look redundant, but it’s good practice to
include it for any button element without a browser-provided behavior: <button>
defaults to type="submit" for submitting form data, in the absence of an explicit
type attribute. But this button is not submitting a form.
The unique ID call-button is deliberately generic. It will save a lot of work we
would otherwise have to do in JavaScript to swap separate join and leave
buttons. The class join will work as a nice styling hook in CSS and, later, for
JavaScript to determine the state of the button. The button’s Join Call text
should be an unambiguous cue to users. In a bit, a little JavaScript will
change the button’s class to leave and its text to Leave Call once the Join Call
button has been clicked.
Adding Video Elements: Self and Peer
With a basic heading and button in place, the only other HTML this basic
app needs is for handling video: one element for a user’s own video stream,
which we will refer to as self, and one element for the remote user’s video
stream, which we’ll refer to as peer.
One of the many fun aspects of working with peer-to-peer streaming media
is that you get to write some things in your markup that are usually big no-
nos. One such thing is setting up multiple video elements to play simultane-
ously (one for self, one for the peer), with some attributes that might surprise
you if you’re familiar with the <video> element introduced in HTML5. If you’re
not familiar, that’s okay, too. We’ll walk through them.
Setting up the Self Video
This is how to set up the video element for the self video, which for convenience
takes an ID of self:
demos/basic-p2p/index.html
<video id="self"
autoplay
muted
playsinline
poster="img/placeholder.png">
</video>
That <video> element sets a number of important attributes. Let’s briefly
explore each one’s purpose in the context of live-streaming video.
Chapter 2. Working with a Signaling Channel • 14
report erratum • discuss
The autoplay Attribute
Ordinarily the autoplay attribute is frowned upon. Users generally expect control
over the playback of video and especially the accompanying audio. Browser
makers have helped to enforce user control by disregarding the autoplay
attribute until a user has interacted with a page in some way first. But for
streaming video, autoplay is strictly necessary: without it, the browser will only
show the very first frame of a streaming video. And because users will have
to click the Join Call button you built above, they will have interacted with
the page before any media begins to stream—all but guaranteeing that the
browser will respect the autoplay attribute.
The muted Attribute
In the next chapter on page 37, for same-machine testing purposes, we’ll
exclude audio from the streaming tracks entirely. But to ensure that future
audio-enabled streams don’t cause hellacious feedback, the self video takes
the muted attribute. That’s different from muting your mic so no one else can
hear you—a topic we will get to in a later chapter. All this attribute does is
disable audio on the self video.
The playsinline Attribute
The final Boolean attribute to include is playsinline. This attribute instructs
mobile devices in particular not to launch a full-screen presentation of the
video once the stream starts, which is the default behavior in Safari on iOS
and other mobile browsers. While full-screen video might sound desirable, it
will obscure anything else on the page, including any user-interface compo-
nents and the self video. With playsinline set, the peer video will play wherever
on the page you place it with CSS.
The poster Attribute
And although not strictly necessary, a placeholder image can be referenced
from the poster attribute. The image will display until the video stream starts,
which is a graceful way to prepare users to expect to see video streams on
the page. It’s helpful for testing purposes too: without a poster or some
explicit dimensions and colors set in CSS, video streams would appear out
of apparent nothingness—or not appear at all—if there’s something wrong
with media permissions or the peer connection that you’ll set up in Setting
Up the Peer Connection, on page 42.
report erratum • discuss
Adding Video Elements: Self and Peer • 15
Setting up the Peer Video
The markup for the peer video is almost identical to the self video, except that
it takes an ID of peer and omits the muted attribute:
demos/basic-p2p/index.html
<video id="peer"
autoplay
playsinline
poster="img/placeholder.png">
</video>
You might have noticed that there is neither an src attribute nor any inner
<source> elements for either video. Their omission is intentional. There’s no
file involved in streaming video. In the next chapter, you’ll use JavaScript to
set up the streaming source for each video element, using the srcObject property,
in the displayStream() function on page 41.
Finally, let’s wrap both <video> elements in an <article> element with an id of
videos. It should open with a second-level heading to label the streaming videos
for accessibility purposes. We’ll give the heading a class of preserve-access that
we can refer to from the CSS in a moment. Putting it all together, your HTML
file should look something like this:
demos/basic-p2p/index.html
<main id="interface">
<header id="header" role="banner">
<h1>Welcome</h1>
<button class="join" type="button" id="call-button">Join Call</button>
</header>
<article id="videos">
➤
<h2 class="preserve-access">Streaming Videos</h2>
➤
<video id="self"
autoplay
muted
playsinline
poster="img/placeholder.png">
</video>
<video id="peer"
autoplay
playsinline
poster="img/placeholder.png">
</video>
</article>
➤
</main>
That’s it: a heading, a button, and two video elements—all wrapped up in a neat
package of semantic sectioning elements. Let’s style everything up in CSS.
Chapter 2. Working with a Signaling Channel • 16
report erratum • discuss
Styling the Core App Elements
With the HTML in place, you can turn your attention to writing some CSS to
present a workable interface. I always begin my work with Eric Meyer’s Reset
CSS.1
There’s no need to replicate that here, but you will see a minified version
of it in the starter screen.css file for this app.
It’s useful to begin by defining an app’s basic typographic properties, usually
on the html selector:
demos/basic-p2p/css/screen.css
html {
font-family: "Lucida Grande", Arial, sans-serif;
font-size: 18px;
font-weight: bold;
line-height: 22px;
}
That text setting might strike you as a bit large and bulky, especially because
these are also meant to be mobile-first styles. But that’s deliberate: think
about your face’s position relative to your screen when you’re on a video call.
Most of us move back from the screen so that the camera can capture at least
our entire heads. And if your head is a prize-winning pumpkin like mine, you
might have to move back. With users’ eyes further from the screen, it’s a more
accessible choice to err on the side of an oversized, easily visible UI—especially
for controls.
Let’s also add some foundational layout styles that set box-sizing to the more
intuitive border-box value, and add some padding around the interface and
header elements:
demos/basic-p2p/css/screen.css
/* Layout */
* {
box-sizing: border-box;
}
#interface {
padding: 22px;
}
#header {
margin-bottom: 11px;
}
#header > h1 {
margin-bottom: 11px;
}
1. https://meyerweb.com/eric/tools/css/reset/
report erratum • discuss
Styling the Core App Elements • 17
Because the “Streaming Videos” heading is meant to help low-vision users
navigate the page’s structures, let’s use an accessible technique to hide the
heading from sighted users while keeping it available in the accessibility tree.
We will reuse this class on additional elements in later chapters:
demos/basic-p2p/css/screen.css
.preserve-access {
position: absolute;
left: -20000px;
}
Styling the Button Element
The <button> element, the only interactive UI on the page, is generally easier
to style than other form elements. You can set up a basic look for it on the
button element selector, opening with a bunch of font styles to inherit the look
of all text on the page, as well as setting the cursor to display as a pointer:
demos/basic-p2p/css/screen.css
button {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
cursor: pointer;
/* Box Styles */
display: block;
border: 0;
border-radius: 3px;
padding: 11px;
}
For better control over the button’s place in the layout, it’s worth setting it
to display as a block. You can also remove the default border that browsers
draw, given the button rounded corners with a small border-radius. A little
bit of margin and padding—derived from the page’s 22px line-height value—
provide some room around the text and some margins to offset the element.
Nothing fancy. (And don’t worry—if your familiarity with responsive design
has you feeling a gnawing guilt for using pixel units, you’re welcome to make
those adjustments yourself. Or give yourself permission to work with pixel
units while you’re still learning WebRTC.)
The button’s HTML currently has only the join class, but you can still set up
some very basic colors on both the join and leave classes for the button:
Chapter 2. Working with a Signaling Channel • 18
report erratum • discuss
demos/basic-p2p/css/screen.css
.join {
background-color: green;
color: white;
}
.leave {
background-color: #CA0;
color: black;
}
Finally, let’s set a width on the #call-button selector:
demos/basic-p2p/css/screen.css
#call-button {
width: 143px; /* 6.5 typographic grid lines */
margin-right: 11px;
}
The 143px width value is tailored to comfortably fit the text for Join Call and
Leave Call, while also being derived from the page’s 22px line height. All the
width does is ensure the button won’t shift the layout around when the but-
ton’s text changes from Join Call to Leave Call. That doesn’t matter much
when the button is on its own line, but as a small responsive touch, let’s add
a media query to display the contents of the <header id="header"> as flex items,
all on the same line:
demos/basic-p2p/css/screen.css
@media screen and (min-width: 500px) {
#header {
display: flex;
flex-direction: row-reverse;
align-items: baseline;
justify-content: flex-end;
}
#header > * {
flex: 0 0 auto;
}
#header > h1 {
margin-bottom: 0;
}
}
If you’re not familiar with flexbox and its properties, what’s happening here
is the page header is set to display as a flexbox with display: flex. Aligning the
flex items—the first-level heading and the button—to the baseline keeps
the text of each on the same invisible line.
report erratum • discuss
Styling the Core App Elements • 19
Then, to move the join button so it sits to the left of the heading, flex-direction:
row-reverse flips the order of the header’s items, while justify-content: flex-end, on a
row-reversed flexbox, will keep the flex items aligned to the left of the flexbox.
The child selector #header > * sets the behavior of the flex items (an <h1> and
<button>, in this case). The shorthand flex sets the grow and shrink values to
zero, meaning that neither element will grow or shrink from its auto width.
For the first-level heading, that will be the width of its text. For the button,
that will be the 143px value of the width property set on the #call-button selector.
The result is that on viewports 500 pixels and wider, the button and heading
sit on the same line as in the figure on page 20. That will reduce how far down
the viewport the header pushes the video elements. And speaking of the video
elements, let’s style them next.
Styling the Video Elements
As embedded content, the <video> element displays inline by default, just like
the <img/> tag does. Ethan Marcotte made famous the pairing of display: block
and max-width: 100% for responsive images,2
and we can do the same for videos.
Setting the max-width property ensures the video element will be no wider than
either its parent element’s width or, in the case of this small app, the viewport:
demos/basic-p2p/css/screen.css
/* Video Elements */
video {
background-color: #DDD;
display: block;
max-width: 100%;
}
2. https://alistapart.com/article/fluid-images/
Chapter 2. Working with a Signaling Channel • 20
report erratum • discuss
You can set the background color on video elements to a very light shade of
gray, which will show the exact boundaries of the video elements and work
nicely with the transparent smiley face PNG set on the video element’s poster
attribute.
The peer video can display as is, but a few adjustments to the self video will
make it obvious which video is which. That’s helpful if you have just a single
camera on your computer. The same streaming video image will appear in
both video elements when you’re testing out your app:
demos/basic-p2p/css/screen.css
#self {
width: 50%;
max-width: 320px;
margin-bottom: 11px;
}
That reduces the self video’s width to 50 percent, and puts a half line-height of
space between it and the peer video that sits below.
With all of that CSS in place, you can reload the page in your browser to see
the button, heading, and video placeholders:
report erratum • discuss
Styling the Core App Elements • 21
Adding Functionality to the Call Button in JavaScript
That’s all the structure and styling your app needs. Now it’s time to cozy up
to JavaScript. We’ll start by focusing on the call button’s functionality.
The first thing we’ll do in the main.js JavaScript file is invoke strict mode. That’s
as simple as writing strict mode as a string at the top of the file:
demos/basic-p2p/js/main.js
'use strict';
Invoking strict mode is good practice. Strict mode will often reveal errors and
inconsistencies in your JavaScript that the browser would otherwise suppress
or ignore. A strict browser turns out to be a real asset when you’re trying to
debug and track down errors in your JavaScript. If you’re interested, you can
read more about strict mode at MDN.3
Now onto the call button, which we’ll handle as a one-off line of JavaScript
in the “User Interface Setup” portion of the JavaScript file. You can use the
querySelector() method on the document object to select the call button from
the HTML. If you’ve not used querySelector() before, know that it accepts a string
containing the same selector syntax that you’d write in CSS:4
document.querySelector('#call-button');
With the #call-button element selected, you can then call the addEventListener()
method to respond to click events on the button. Start small and report in
the console that the button has been clicked:
document.querySelector('#call-button')
.addEventListener('click', function(event) {
console.log('Call button clicked!');
});
Reload your page in the browser and open the JavaScript console. You should
see “Call button clicked!” appear in the console.
Writing Named Functions as Callbacks
While the querySelector method takes a single argument—the CSS selector #call-
button—the addEventListener method takes two required arguments. The first
argument is the name of the event, 'click', and the second argument is an
anonymous callback function, also known as a listener. The function is
3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
4. https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
Chapter 2. Working with a Signaling Channel • 22
report erratum • discuss
anonymous because it doesn’t have a name: the function keyword just defines
the function in place. I like callback functions to have names and their own
place to live in a JavaScript file. In this case, there is a “User-Interface
Functions and Callbacks” area in the main.js file. So the first little improvement
is to create a named function and pass its name in as the second argument
to addEventListener():
/**
* User-Interface Setup
*/
document.querySelector('#call-button')
.addEventListener('click', handleCallButton);
/**
* User-Interface Functions and Callbacks
*/
function handleCallButton(event) {
console.log('Call button clicked! Named callback function active!');
}
With the handleCallButton function passed in as a reference, the code looks a
little cleaner: we can simply glance at the event, click, and the descriptive name
of the function to be called in response to the event. Even better, the nitty-
gritty details of handleCallButton are in their own spot a little further down in the
JavaScript file. Maintaining those kinds of organizational habits will be even
more important once the file includes more signaling and WebRTC code,
which is almost entirely driven by events and their callbacks.
Reload your app in the browser and click the button once again. The console
should show the longer message announcing that the named callback function
is active.
If you saw the message in the console before you clicked, it’s possible that
you might have called the function by mistake, rather than passing it in by
reference:
document.querySelector('#call-button')
.addEventListener('click', handleCallButton()); // Oops!
If you included the opening and closing parentheses, (), on handleCallButton,
what gets passed into addEventListener is not the reference to the callback
function, but the result of the callback function after it runs prematurely. If
that happened, make sure that you’re passing in the name of the function
only: handleCallButton, without parentheses. Then refresh the app in your
browser and try again.
report erratum • discuss
Adding Functionality to the Call Button in JavaScript • 23
Working with Data Returned to Callback Functions
The addEventListener() method, like many methods that accept callback functions,
passes along a chunk of data to the callback when its event fires. With
addEventListener(), it’s basically convention to pass that data into the callback
as either event or simply e. Choose whichever you prefer, but note that the
code you’ll see throughout this book uses e for errors and spells out event for
all events. The target property on event is a DOM object representing the element
that was clicked. Because it’s a DOM object, much like we’d get from using
document.querySelector(), we can do DOM things with it, like determine its id:
function handleCallButton(event) {
console.log('Button with ID', event.target.id, 'clicked!');
}
Refresh and click the button again, and watch for “Button with ID call-button
clicked!” to appear in your console.
We can do even more with event.target to make the call button fully functional:
1. Change the button’s class from join to leave, and vice versa
2. Change the button’s text from “Join Call” to “Leave Call,” and vice versa
Let’s set that up. To make the code more readable, hang onto the event.target
value in a local variable called call_button:
demos/basic-p2p/js/main.js
function handleCallButton(event) {
const call_button = event.target;
if (call_button.className === 'join') {
console.log('Joining the call...');
call_button.className = 'leave';
call_button.innerText = 'Leave Call';
} else {
console.log('Leaving the call...');
call_button.className = 'join';
call_button.innerText = 'Join Call';
}
}
Refresh your app once more. Now you can click the button again and again.
Its text will change and, thanks to CSS that you wrote earlier, so will its
appearance. The console will also log “Joining the call…” or “Leaving the
call…,” depending on what state the button is in.
Excellent. All of the foundational HTML, CSS, and JavaScript for the video-
call app’s interface is now in place. Up next, we need to start adding in logic
for the signaling channel. But before we get to that, let’s take a code break
Chapter 2. Working with a Signaling Channel • 24
report erratum • discuss
and look a little bit at WebRTC as implemented in the browser—and learn
why a signaling channel is necessary for WebRTC.
Positioning WebRTC as a Front-End Technology
With an imposing name like WebRTC: Real-Time Communication in Browsers,
the WebRTC specification sure sounds like a newfangled server-based tech-
nology.5
As web developers, especially front-end developers, we’ve simply
never seen browsers do much else than talk to a server, whether to make
requests for static HTML pages, process data submitted from a form, or
asynchronously request resources with the Fetch API. The traditional web
has always been a 100 percent server-backed technology. Front-end developers
are left to mush their faces against the windowpane to the server room, where
all the cool new toys often are.
They’re not wrong to feel that way: the web has always relied on the client-
server architecture of HTTP, where the client part is usually synonymous with
browser. (Web-standards documents refer to user agents—which are clients
or browsers, too.) Client-server architecture means that even something as
basic as a text-based chat application requires relaying each chat message
from a browser, through a server, and over to the receiving browser. WebSockets
have enabled persistent connections and push-like behavior, so chat messages
appear to come across instantaneously from one user to another. But even
with WebSockets, the basic architecture of the web remains unchanged,
including the server as the central hub.
Not so with WebRTC. As a genuine peer-to-peer technology, the architecture
of WebRTC as implemented in web browsers just about eliminates the need
for an intermediary server. (True, there are server, OS, and even smart-device
implementations of WebRTC, but they represent different use cases beyond
the scope of this book.) Once a connection is established, WebRTC in the
browser enables any two peers to directly exchange data, including streaming
media, over an encrypted low-latency connection without their data ever
touching a web server.
Previewing Peer-to-Peer Connection Negotiation
Of course, there’s one little hitch: WebRTC is peer-to-peer only once a connec-
tion is established. RTC peer connections take a little bit of work to establish,
as you’ll see, and usually with the aid of a server that provides a signaling
channel.
5. https://www.w3.org/TR/webrtc/
report erratum • discuss
Positioning WebRTC as a Front-End Technology • 25
As web developers, we’re used to HTTP’s request-response connection pattern:
if you have a domain name or even just an IP address, you enter it in the
address bar of the browser and boom—connection established and resource
or error message delivered in response to the request. End of story. You’ve
been doing that already with the little local web server for previewing your
work on the video-call app’s interface. Subsequent requests to the same
website work the same way as the first request, but typically without a per-
sistent connection between browser and server unless you’re dealing with
something fancy like WebSockets or a web server with keepalive functionality.
Establishing a connection over WebRTC is more involved than request-
response. The two connecting peers have to negotiate the terms of the connec-
tion before it can be established. Instead of request-response, peer connections
are negotiated by an offer-answer pattern over a signaling channel.
The offer-answer structure that establishes a peer connection is metaphori-
cally no different from a conversation between two friends making plans to
meet for coffee. Unless they share a remarkably strong psychic connection,
the two friends won’t show up the same day and time at the exact same coffee
shop. They must first negotiate the details of the coffee date, using some kind
of a signaling channel: email, phone, text, or something conventional like
that. Even a remarkably strong psychic connection functions as a signaling
channel, when it comes right down to it.
But let’s stick with a more conventional example. One friend initiates the
coffee date by opening a signaling channel: sending a text, making a phone
call, or yelling up the street. The initiating friend provides proposed details
on a plan to meet up, and the other friend responds. The friends go back and
forth, offering and answering over their shared signaling channel, until they
reach an agreement to meet at a specific time and place. Regardless of the
signaling channel used (text, phone, email), their conversation goes something
like this:
Friend A: How about we meet for coffee next Tuesday at 10, at The Red Eye?
Friend B: I could do Tuesday, but not until 11.
Friend A: That works, but I have another meeting at 12:30 on the other side of
town, so let’s meet at Common Grounds instead.
Friend B: Okay! See you on Tuesday at 11 at Common Grounds.
Friend A: Sounds good!
Again: all of that negotiation takes place over a signaling channel, which is
independent of the destination coffee shop. The coffee shop itself cannot serve
Chapter 2. Working with a Signaling Channel • 26
report erratum • discuss
as a signaling channel, because even the question of which coffee shop may
have to be negotiated—as was the case in the little exchange above.
Establishing a peer-to-peer connection between two browsers works much
the same way. At least one browser will make an offer to connect, and the
other browser will return some kind of response. The process continues like
that over the signaling channel, like friends setting a coffee date over text,
until the browsers agree on how to open the peer connection.
Using a Lightweight Signaling Channel
Browsers lack a signaling channel that enables a connection to be negotiated.
The editors of the WebRTC specification have gone so far as to avoid requiring
any specific signaling technology whatsoever: you have to bring your own. So
to prepare the way to establish a WebRTC connection, we need first to select
and set up a signaling channel.
A server-based signaling channel is the most convenient option. Even a very
basic peer-to-peer application, like the one you’re writing right now, requires
both browsers to visit a URL pointing at some server on the web to download
the HTML, CSS, and JavaScript necessary to establish and support the call.
With a web server already in the mix, any server-side setup for passing a
message from one browser to another would suffice.
Technically speaking, a server-based signaling channel isn’t strictly necessary.
Two peers connecting over WebRTC could, in theory, email or even handwrite
and make no-contact delivery of their browsers’ offers and answers to set up
the peer connection—just like the two friends above could have set their coffee
date using semaphore, tin cans on a string, a dead drop, or a whole bunch
of other improbable signaling channels. But a server is going to provide the
greatest flexibility and much lower latency. It would be possible, for example,
to write your own signaling channel using WebSockets, in any server-side
language you choose: PHP, Python, Ruby, and so on.
To prevent you from getting sidetracked by all of that work, I have written a
very small server in ExpressJS6
that includes a signaling channel based on
Socket.IO.7
It’s included in the book’s sample code you already downloaded.
The signaling channel works out of the box. While you might find value in
experimenting with it, you won’t be writing much server-side code in this
book. Our focus is in the browser, because that’s where the real action is.
6. https://expressjs.com/
7. https://socket.io/
report erratum • discuss
Using a Lightweight Signaling Channel • 27
But before we get to work in the browser, you will better grasp the code you’re
about to write for using the signaling channel if we take a quick walking tour
of the signaling channel itself. The channel is very little and very dumb: all
it does is manage a few events and shuttle messages back and forth. It doesn’t
know about WebRTC or anything else that we might be doing. To the greatest
extent possible, it’s better to keep knowledge of WebRTC in and between
browsers. The less your signaling channel does, the easier it will be to move
your app to a different signaling channel in the future.
The server’s signaling channel component is fewer than a dozen lines, all of
which can be reproduced here. As you’ve seen, sometimes I walk through
setting up a piece of code, line by line. Other times, I will do a dramatic reveal
of the whole thing before talking through it with you. Like here:
server.js
const namespaces = io.of(/^/[0-9]{7}$/);
namespaces.on('connect', function(socket) {
const namespace = socket.nsp;
socket.broadcast.emit('connected peer');
socket.on('signal', function(data) {
socket.broadcast.emit('signal', data);
});
socket.on('disconnect', function() {
namespace.emit('disconnected peer');
});
});
Two features of the signaling channel set the stage for the code we’ll write in
the browser: a set of events and a precise seven-digit namespace. Let’s look
at the events first.
Exploring the Signaling Channel’s Events
The signaling channel handles seven events: three that it listens for—connect,
signal, and disconnect—and four that it emits—connected peer, signal, and disconnected
peer, plus a connect event that Socket.IO emits automatically.
When a client connects, the signaling channel will broadcast the connected peer
event to other connected clients. When a client sends a signal, the signaling
channel will rebroadcast the signal event and its data, essentially acting as
a repeater. And finally, when a client disconnects, the signaling channel will
emit a disconnected peer event.
Chapter 2. Working with a Signaling Channel • 28
report erratum • discuss
That captures everything a basic signaling channel needs to do: listen for
connections, listen for and repeat signals, and listen for disconnections. That’s
it. The code we write in the browser to establish a peer connection will trigger
or respond to each of those events.
Namespacing the Signaling Channel
The video-call app you’re building is meant to scale: it will allow multiple
different independent pairs of peers to connect to each other simultaneously.
That is no different from how Zoom or Google Meet works. When you use
Zoom, you and the person you want to talk to must share (over some other
signaling channel!) a unique URL like https://fake-example.zoom.us/j/72072139453.
You’ll also be building unique URLs similar to that.
The opening lines of the signaling channel in server.js use a regular expression
to verify the structure of a namespace, which is similar to a meeting code
in Zoom. With a shared namespace, one pair of peers can negotiate their
connection over the namespace /0000001, while another pair can connect
simultaneously over /0000002. Their signals will never cross.
Those are very easy to guess patterns, of course, so we’ll write a function in
the browser to test or generate a random number that matches the name-
space’s expected seven-digit pattern on the server. We’ll make the namespace
easy for users to share by attaching it as a hash on the URL, something like
https://localhost/basic-p2p/#1234567. (Later in the book, we’ll use the server to gen-
erate namespaces using URL paths rather than hashes.)
At the very bottom of the main.js file, you will find a section for utility functions.
Here I’ve written a function called prepareNamespace() that takes two arguments.
The hash argument will work with an existing hash, likely as reported by the
browser from window.location.hash. The second argument, set_location, is a Boolean
value (true or false) for setting the prepared namespace on window.location.hash.
Look through the whole thing, and then let’s walk through it together.
demos/basic-p2p/js/main.js
/**
* Utility Functions
*/
function prepareNamespace(hash, set_location) {
let ns = hash.replace(/^#/, ''); // remove # from the hash
if (/^[0-9]{7}$/.test(ns)) {
console.log('Checked existing namespace', ns);
return ns;
}
report erratum • discuss
Using a Lightweight Signaling Channel • 29
ns = Math.random().toString().substring(2, 9);
console.log('Created new namespace', ns);
if (set_location) window.location.hash = ns;
return ns;
}
The local variable ns uses the replace() string method with a regular expression
and empty string to remove the # (octothorp) that window.location.hash always
returns. With the octothorp removed, the function can check the namespace’s
value against another regular expression pattern: /^[0-9]{7}$/. That is almost
identical to the signaling channel’s pattern you already saw in server.js (its
pattern must also check for the slash that Socket.IO prepends to namespaces).
Demystifying Regular Expressions
If you’ve never worked with regular expressions before, or if the phrase regular
expressions makes your palms sweat and your chest tighten, please try to
relax. Their basic purpose is pretty easy to summarize: regular expressions
describe patterns.
Let’s examine the regular-expression pattern /^[0-9]{7}$/ from the inside out.
A pattern that matches a sequence of seven digits, 0 through 9, looks like
this: [0-9]{7}. [0-9] represents all numbers in the range zero to nine. The seven
in curly braces, {7}, means that we want the numbers in the range to appear
seven times in a row. Easy enough, right? You’d think. The problem is that
any string of text that includes seven digits in a row—no matter what other
text the string includes—would still satisfy a regular expression pattern
specifying seven sequential digits.
To make sure the namespace is only ever exactly seven digits and therefore
prevent potentially malicious characters from ever reaching the signaling
channel, the pattern opens with a caret: ^. The caret is a regular-expression
symbol that marks the very beginning of a one-line string. Similarly, a dollar
sign $ marks the very end of the string. Without the dollar sign, the namespace
0011222-EVIL-BUSINESS-HERE! would still match, because it opens with seven digits.
That kind of thing is probably why most people find themselves so confused
and angered by regular expressions: they are incredibly, infuriatingly literal.
Hey, you said you wanted seven digits–I found you seven digits! Ask regular
expressions for a haircut, and they’ll cut exactly one of your hairs. They are
the dad jokes of computer programming.
Back to the pattern at hand: bookending the regular expression are slashes,
/, which demarcate regular expressions in JavaScript, just like quotation
marks or backticks demarcate strings. Put it all together, and this little creep
Chapter 2. Working with a Signaling Channel • 30
report erratum • discuss
should look a tad less imposing: /^[0-9]{7}$/. Seven digits in a row, and seven
digits only. No more, no less—and nothing else. That’s the pattern for our
namespace.
Making Use of the Namespace
And now back to the prepareNamespace() function definition itself: With the reg-
ular expression in place, we call the test() method on it and pass along the
namespace we have on hand, ns. If the test passes, we return the octothorp-
free namespace, ns.
But if the test fails because either the hash doesn’t match our pattern or there’s
no hash at all, the function generates a new random hash in a complex one-liner:
demos/basic-p2p/js/main.js
ns = Math.random().toString().substring(2, 9);
That generates a random number and converts it to a string. The substring()
method gets called to remove the first two characters: numbers generated by
Math.random() always begin with 0. The second argument to substring(), 9, ensures
that we get a seven-digit number, thanks to the first two characters being
discarded by the first argument, 2.
If set_location is true, the new namespace gets set on the URL by assigning it to
window.location.hash.
Finally, the function returns the value of ns, which is the brand-new, randomly
generated hash.
With the prepareNamespace() function definition in place, you can put it to work
at the top of your file and assign its output–the returned namespace–to a
namespace variable:
demos/basic-p2p/js/main.js
const namespace = prepareNamespace(window.location.hash, true);
Let’s do something user-facing with the namespace variable. In the user-interface
area of the JavaScript file, add another one-liner that sets the h1 text to wel-
come users to a correctly namespaced room:
demos/basic-p2p/js/main.js
/**
* User-Interface Setup
*/
document.querySelector('#header h1')
➤
.innerText = 'Welcome to Room #' + namespace;
➤
document.querySelector('#call-button')
.addEventListener('click', handleCallButton);
report erratum • discuss
Using a Lightweight Signaling Channel • 31
Reload the page at https://localhost:3000/basic-p2p/. You should see two things: a
random, seven-digit hash appended to the URL, something like https://local-
host:3000/basic-p2p/#4134610, and that same hash repeated in the text of the page’s
first-level heading, like this:
Those adjustments are basically cosmetic, changing only the appearance of
the URL and page. Now let’s use the namespace variable to connect to the sig-
naling channel.
Connecting to the Signaling Channel
There are a few preliminary steps you’ll need to take to connect to the signaling
channel. The first is to return to the index.html file and add another <script> tag
right above the one that loads the main.js file. Point it to the JavaScript file
/socket.io/socket.io.js that Socket.IO automatically serves:
demos/basic-p2p/index.html
<script src="/socket.io/socket.io.js"></script>
➤
<script src="js/main.js"></script>
</body>
</html>
The main.js file depends on the contents of the socket.io.js file, which is the
Socket.IO client, so be sure to include the <script> tag that points at main.js
second in the HTML.
Chapter 2. Working with a Signaling Channel • 32
report erratum • discuss
Back in the main.js file itself, in the “Signaling-Channel Setup” area up top,
let’s connect to the signaling channel and attach some code to the connect
event that will output a message to the browser console if the connection is
successful. You can declare an sc variable to hold onto the namespaced sig-
naling channel as returned by Socket.IO’s io object’s connect method:
/**
* Signaling-Channel Setup
*/
const namespace = prepareNamespace(window.location.hash, true);
const sc = io.connect('/' + namespace);
➤
➤
sc.on('connect', function() {
➤
console.log('Successfully connected to the signaling channel!');
➤
});
➤
If your browser’s JavaScript console isn’t already open, open it. Reload the
page and you should see the success message logged in the console: “Success-
fully connected to the signaling channel!”
Right now, anyone using your app will automatically connect to the signaling
channel by loading the page in the browser. That’s not the behavior we want,
though: the signaling channel’s connect event will eventually start the process
of establishing the WebRTC call. To give users better control, then, let’s rewrite
the code so users don’t connect to the signaling channel until they click the
Join Call button that we went to all the trouble of setting up.
Connecting to the Signaling Channel Manually
Remember how you set up a handleCallButton() function in Writing Named
Functions as Callbacks, on page 22? Let’s call two more functions from
within it: joinCall() and leaveCall(). The definitions for those functions can go right
below the handleCallButton function definition:
demos/basic-p2p/js/main.js
/**
* User-Interface Functions and Callbacks
*/
function handleCallButton(event) {
const call_button = event.target;
if (call_button.className === 'join') {
console.log('Joining the call...');
call_button.className = 'leave';
call_button.innerText = 'Leave Call';
joinCall();
➤
} else {
console.log('Leaving the call...');
report erratum • discuss
Connecting to the Signaling Channel • 33
call_button.className = 'join';
call_button.innerText = 'Join Call';
leaveCall();
➤
}
}
function joinCall() {
sc.open();
}
function leaveCall() {
sc.close();
}
There will be more to add to both the joinCall and leaveCall functions. But to get
started, they are responsible only for opening and closing the Socket.IO sig-
naling channel, which provides its own open() and close() methods. We just
need to call them on the sc object.
Now that your JavaScript is set to manually open and close the connection
to the signaling server, you need to modify how the signaling channel is con-
figured by passing in an options object that sets autoConnect to false:
demos/basic-p2p/js/main.js
const sc = io.connect('/' + namespace, { autoConnect: false });
Reload the page in your browser again. You shouldn’t see the “Successfully
connected to the signaling server!” in the JavaScript console until you click
the Join Call button. If you click the button a few more times, joining and
leaving the call, you’ll see the success message each time you click Join Call.
Nice!
Because the signaling channel is powered by Socket.IO, it has an active prop-
erty that returns true when the signaling server is connected and false when
it’s not. If you like, hit the Join Call button and then type sc.active in your
browser’s JavaScript console and hit Return. It should report true. Hit the
Leave Call button, type sc.active again, and it should report false. That’s an easy
way to verify that your button really is opening and closing the signaling
channel.
Preparing Placeholders for the Remaining Signaling Callbacks
One more task, and then we’ll have everything in place for writing the
remaining signaling-channel code and WebRTC connection logic in the next
chapter’s coverage of Building Connection Logic to the “Perfect Negotiation” Pat-
tern, on page 51. Find the “Signaling-Channel Functions and Callbacks” area
of the JavaScript file. Let’s write a wrapper function, registerScCallbacks() that
Chapter 2. Working with a Signaling Channel • 34
report erratum • discuss
Random documents with unrelated
content Scribd suggests to you:
[45] Espinosa, Antq. y Grand. de Seville, L. 2, c. 3.
[46] Conde, P. 1, c. 14.
[47] Conde, p. 1. Cronica del Moro Rasis.—Cron. gen. España, por Alonzo el
Sabio, p. 3, c. 1.
[48] Conde, pt. 1. c. 15.
[49] Conde, pt. 1, c. 15.
[50] Conde, pt. 1, c. 16.
[51] Conde, pt. 1, c. 17.
[52] Algarbe, or Algarbia, in Arabic signifies the west, as Axarkia is the east,
Algufia the north, and Aquibla the south. This will serve to explain some of
the geographical names on the peninsula which are of Arabian origin.
[53] Faxardo, Corona Gothica, T. 1, p. 492.—Joan. Mar. de Reb. Hisp. L. 6, c.
27.
[54] Conde, pt. 1, c. 17.
[55] Chron. gen. de Alonzo el Sabio, p. 3. Joan Mar. de Reb. Hisp. lib. 6, c.
27. Conde, pt. 1, c. 19.
[56] Abarca, Anales de Aragon. Ante regno, § 2.
[57] El Moro Rasis, La Destruycion de España. Rojas, Hist. Toledo, pt. 2, L.
4, cl.
[58] El Moro Rasis, Destruycion de España, pt. 2, c. 101.
[59] Morales, Cronicon de España, L. 13, c. 2.
[60] Judicio Domini actum est, ut ipsius montis pars se a fundamentis
evolvens, sexaginta tria millia caldeorum stupenter in fulmina projecit, atque
eos omnes opressit. Ubi usque nunc ipse fluvius dum tempore hyemali
alveum suum implet, ripasque dissoluit, signa armorum et ossa eorum
evidentissime ostendit.—Sebastianus Salmanticensis Episc.
[61] La Destruycion de España, part 3.
[62] Sandoval, p. 301.
[63] It does not appear that Count Fernan Gonzalez kept his promise of
founding a church and monastery on the site of the hermitage. The latter
edifice remained to after ages. “It stands,” says Sandoval, “on a precipice
overhanging the river Arlanza, insomuch that it inspires dread to look below.
It is extremely ancient; large enough to hold a hundred persons. Within the
chapel is an opening like a chasm, leading down to a cavern larger than the
church, formed in the solid rock, with a small window which overlooks the
river. It was here the Christians used to conceal themselves.”
As a corroboration of the adventure of the Count of Castile, Sandoval
assures us that in his day the oak still existed to which Don Fernan Gonzalez
tied his horse, when he alighted to scramble up the hill in pursuit of the boar.
The worthy Fray Agapida, however, needed no corroboration of the kind,
swallowing the whole story with the ready credence of a pious monk. The
action here recorded was known by the name of the battle of the Ford of
Cascajares.
Sandoval gives a different account of the fate of the hermits. He says that
Almanzor, in a rage at their prognostics, overthrew their chapel, and, without
alighting from his horse, ordered the three monks to be beheaded in his
presence. “This martyrdom,” he adds, “is represented in an ancient painting
of the chapel which still exists.”
[64] Sandoval. The Five Bishops. Mariana, lib. 8, c. 5, p. 367. Cron. Gen. de
España, part 3, c. 18, fol. 53.
[65] Cron. Gen. de España, ut supra.
[66] Cron. Gen. de España.
[67] Mariana, lib. 8, c. 5, p. 367.
[68] Sandoval, p. 313.
[69] In the Cronica General de España, this imprisonment is said to have
been by King Sancho the Fat; but the cautious Agapida goes according to his
favorite Sandoval in attributing it to King Ramiro, and in so doing he is
supported by the Chronicle of Bleda, L. 3, c. 19.
[70] Exactly the same kind of miracle is recorded as happening in the same
place to a cavalier of the name of Don Fernan Antolenez, in the service of the
Count Garcia Fernandez. Fray Antonio Agapida has no doubt that the same
miracle did actually happen to both cavaliers; “for in those days,” says he,
“there was such a demand for miracles that the same had frequently to be
repeated;” witness the repeated appearance of Santiago in precisely the
same manner, to save Christian armies from imminent danger of defeat, and
achieve wonderful victories over the infidels, as we find recorded throughout
the Spanish chronicles.
[71] Cronica de Alonzo el Sabio, pt. 3 c. 19.
[72] Sandoval, p. 334.
[73] Cronica Gotica, por Don Alonzo Nuñez de Castro, p. 17.
[74] Cronica General de España, pt. 3, p. 370.
[75] Cron. Gen. de España, pt. 4, fol. 373.
[76] Cron. Gen. de España, pt. 4, c. ii.
[77] The hiatus, here noted by the author, has evidently arisen from the loss
of a leaf of his manuscript. The printed line which precedes the parenthesis
concludes page 32 of the manuscript; the line which follows it begins page
34. The intermediate page is wanting. I presume the author did not become
conscious of his loss until he had resorted to his manuscript for revision, and
that he could not depend upon his memory to supply what was wanting
without a fresh resort to authorities not at hand. Hence a postponement and
ultimate omission. The missing leaf would scarce have filled half a page of
print, and, it would seem from the context, must have related the invasion of
Andalusia by Fernando and the ravages committed by his armies.—Ed.
[78] Cron. Gen. de España, pt. 4. Bleda, lib. 4, c. 10.
[79] Cronica del Rey Santo, cap. 13.
[80] Notas para la Vida del Santo Rey, p. 554.
[81] Some chronicles, through mistake, make it Pezuelo near Ciudal Real, in
the mountains on the confines of Granada.
[82] Conde, tom. iii. c. 5.
[83] Notas para la Vida, etc., p. 562.
[84] Notas para la Vida del Santo Rey, p. 572.
[85] Rodriguez, Memorias del Santo Rey, c. lviii.
[86] Cronica del Rey Don Fernando, c. XIII.
[87] Zuniga, Annales de Sevilla, L. 1.
[88] Jacob Paranes, Lib. de los Maestros de St. Iago. Corona Gothica, T. 3, §
xiii. Zuniga, Annales de Sevilla.
[89] Corona Gothica, T. 3, § viii.
[90] Cronica Gotica, L. 3, § 13. Cronica General, pt. 4. Cronica de Santo
Rey, c. 55.
[91] Cronica General, pt. 4, p. 338.
[92] Cronica General de España, pt. 4. Cronica del Rey Fernando el Santo,
c. 60. Corona Gothica, T. 3, p. 126.
[93] Cronica General, pt. 4, 341.
[94] Cronica General, pt. 4. Corona Gothica, T. 3, § 16.
[95] Cronica General, pt. 4. Cronica del Rey Santo. Corona Gothica, T. 3, §
16.
[96] Cronica General, pt. 4, p. 424.
[97] Mariana, L. 13, c. 7.
[98] In Castile, whenever the kings entered any place where there was a
synagogue, the Jews assembled in council and paid to the Monteros, or bull-
fighters, twelve maravedis each, to guard them, that they should receive no
harm from the the Christians; being held in such contempt and odium, that it
was necessary they should be under the safeguard of the king, not to be
injured or insulted.[A]
[A] Zuniga, Annales de Sevilla.
[99] Pablo de Espinosa, Grandesas de Sevilla, fol. 146. Cronica del Santo
Rey, c. 78. Corona Gothica, T. 3, p. 166.
[100] Argote de Molina, Nobleza de Andaluzia, L. 1, c. 21. Tomas Bocio,
Signales de la Iglesia, L. 20. Don Rodrigo Sanchez, Bishop of Palencia, pt. 3,
c. 40.
[101] Pablo de Espinosa, fol. 146.
Transcriber's note
Original spelling was kept, but variant spellings were made consistent when a
predominant usage was found.
Obvious printer errors have been silently corrected.
The following changes were also made:
Page 29: “cheek” → “check”
Page 31: “potents” → “portents”
Page 459: “señoria” → “señorio”
Page 516,
note 100: “Argoti” → “Argote”
Page 521: “pundoner” → “pundonor”
Blank pages have been skipped.
Footnotes have been renumbered and moved to the end of the book.
The text of chapter headings and of Table of Contents entries have been made
consistent.
All chapters end with ornated illustrations, even when they were not present in
the printed book.
*** END OF THE PROJECT GUTENBERG EBOOK SPANISH PAPERS
***
Updated editions will replace the previous one—the old editions will
be renamed.
Creating the works from print editions not protected by U.S.
copyright law means that no one owns a United States copyright in
these works, so the Foundation (and you!) can copy and distribute it
in the United States without permission and without paying
copyright royalties. Special rules, set forth in the General Terms of
Use part of this license, apply to copying and distributing Project
Gutenberg™ electronic works to protect the PROJECT GUTENBERG™
concept and trademark. Project Gutenberg is a registered trademark,
and may not be used if you charge for an eBook, except by following
the terms of the trademark license, including paying royalties for use
of the Project Gutenberg trademark. If you do not charge anything
for copies of this eBook, complying with the trademark license is
very easy. You may use this eBook for nearly any purpose such as
creation of derivative works, reports, performances and research.
Project Gutenberg eBooks may be modified and printed and given
away—you may do practically ANYTHING in the United States with
eBooks not protected by U.S. copyright law. Redistribution is subject
to the trademark license, especially commercial redistribution.
START: FULL LICENSE
THE FULL PROJECT GUTENBERG LICENSE
PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK
To protect the Project Gutenberg™ mission of promoting the free
distribution of electronic works, by using or distributing this work (or
any other work associated in any way with the phrase “Project
Gutenberg”), you agree to comply with all the terms of the Full
Project Gutenberg™ License available with this file or online at
www.gutenberg.org/license.
Section 1. General Terms of Use and
Redistributing Project Gutenberg™
electronic works
1.A. By reading or using any part of this Project Gutenberg™
electronic work, you indicate that you have read, understand, agree
to and accept all the terms of this license and intellectual property
(trademark/copyright) agreement. If you do not agree to abide by all
the terms of this agreement, you must cease using and return or
destroy all copies of Project Gutenberg™ electronic works in your
possession. If you paid a fee for obtaining a copy of or access to a
Project Gutenberg™ electronic work and you do not agree to be
bound by the terms of this agreement, you may obtain a refund
from the person or entity to whom you paid the fee as set forth in
paragraph 1.E.8.
1.B. “Project Gutenberg” is a registered trademark. It may only be
used on or associated in any way with an electronic work by people
who agree to be bound by the terms of this agreement. There are a
few things that you can do with most Project Gutenberg™ electronic
works even without complying with the full terms of this agreement.
See paragraph 1.C below. There are a lot of things you can do with
Project Gutenberg™ electronic works if you follow the terms of this
agreement and help preserve free future access to Project
Gutenberg™ electronic works. See paragraph 1.E below.
1.C. The Project Gutenberg Literary Archive Foundation (“the
Foundation” or PGLAF), owns a compilation copyright in the
collection of Project Gutenberg™ electronic works. Nearly all the
individual works in the collection are in the public domain in the
United States. If an individual work is unprotected by copyright law
in the United States and you are located in the United States, we do
not claim a right to prevent you from copying, distributing,
performing, displaying or creating derivative works based on the
work as long as all references to Project Gutenberg are removed. Of
course, we hope that you will support the Project Gutenberg™
mission of promoting free access to electronic works by freely
sharing Project Gutenberg™ works in compliance with the terms of
this agreement for keeping the Project Gutenberg™ name associated
with the work. You can easily comply with the terms of this
agreement by keeping this work in the same format with its attached
full Project Gutenberg™ License when you share it without charge
with others.
1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside the
United States, check the laws of your country in addition to the
terms of this agreement before downloading, copying, displaying,
performing, distributing or creating derivative works based on this
work or any other Project Gutenberg™ work. The Foundation makes
no representations concerning the copyright status of any work in
any country other than the United States.
1.E. Unless you have removed all references to Project Gutenberg:
1.E.1. The following sentence, with active links to, or other
immediate access to, the full Project Gutenberg™ License must
appear prominently whenever any copy of a Project Gutenberg™
work (any work on which the phrase “Project Gutenberg” appears,
or with which the phrase “Project Gutenberg” is associated) is
accessed, displayed, performed, viewed, copied or distributed:
This eBook is for the use of anyone anywhere in the United
States and most other parts of the world at no cost and with
almost no restrictions whatsoever. You may copy it, give it away
or re-use it under the terms of the Project Gutenberg License
included with this eBook or online at www.gutenberg.org. If you
are not located in the United States, you will have to check the
laws of the country where you are located before using this
eBook.
1.E.2. If an individual Project Gutenberg™ electronic work is derived
from texts not protected by U.S. copyright law (does not contain a
notice indicating that it is posted with permission of the copyright
holder), the work can be copied and distributed to anyone in the
United States without paying any fees or charges. If you are
redistributing or providing access to a work with the phrase “Project
Gutenberg” associated with or appearing on the work, you must
comply either with the requirements of paragraphs 1.E.1 through
1.E.7 or obtain permission for the use of the work and the Project
Gutenberg™ trademark as set forth in paragraphs 1.E.8 or 1.E.9.
1.E.3. If an individual Project Gutenberg™ electronic work is posted
with the permission of the copyright holder, your use and distribution
must comply with both paragraphs 1.E.1 through 1.E.7 and any
additional terms imposed by the copyright holder. Additional terms
will be linked to the Project Gutenberg™ License for all works posted
with the permission of the copyright holder found at the beginning
of this work.
1.E.4. Do not unlink or detach or remove the full Project
Gutenberg™ License terms from this work, or any files containing a
part of this work or any other work associated with Project
Gutenberg™.
1.E.5. Do not copy, display, perform, distribute or redistribute this
electronic work, or any part of this electronic work, without
prominently displaying the sentence set forth in paragraph 1.E.1
with active links or immediate access to the full terms of the Project
Gutenberg™ License.
1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if you
provide access to or distribute copies of a Project Gutenberg™ work
in a format other than “Plain Vanilla ASCII” or other format used in
the official version posted on the official Project Gutenberg™ website
(www.gutenberg.org), you must, at no additional cost, fee or
expense to the user, provide a copy, a means of exporting a copy, or
a means of obtaining a copy upon request, of the work in its original
“Plain Vanilla ASCII” or other form. Any alternate format must
include the full Project Gutenberg™ License as specified in
paragraph 1.E.1.
1.E.7. Do not charge a fee for access to, viewing, displaying,
performing, copying or distributing any Project Gutenberg™ works
unless you comply with paragraph 1.E.8 or 1.E.9.
1.E.8. You may charge a reasonable fee for copies of or providing
access to or distributing Project Gutenberg™ electronic works
provided that:
• You pay a royalty fee of 20% of the gross profits you derive
from the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
about donations to the Project Gutenberg Literary Archive
Foundation.”
• You provide a full refund of any money paid by a user who
notifies you in writing (or by e-mail) within 30 days of receipt
that s/he does not agree to the terms of the full Project
Gutenberg™ License. You must require such a user to return or
destroy all copies of the works possessed in a physical medium
and discontinue all use of and all access to other copies of
Project Gutenberg™ works.
• You provide, in accordance with paragraph 1.F.3, a full refund of
any money paid for a work or a replacement copy, if a defect in
the electronic work is discovered and reported to you within 90
days of receipt of the work.
• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.
1.E.9. If you wish to charge a fee or distribute a Project Gutenberg™
electronic work or group of works on different terms than are set
forth in this agreement, you must obtain permission in writing from
the Project Gutenberg Literary Archive Foundation, the manager of
the Project Gutenberg™ trademark. Contact the Foundation as set
forth in Section 3 below.
1.F.
1.F.1. Project Gutenberg volunteers and employees expend
considerable effort to identify, do copyright research on, transcribe
and proofread works not protected by U.S. copyright law in creating
the Project Gutenberg™ collection. Despite these efforts, Project
Gutenberg™ electronic works, and the medium on which they may
be stored, may contain “Defects,” such as, but not limited to,
incomplete, inaccurate or corrupt data, transcription errors, a
copyright or other intellectual property infringement, a defective or
damaged disk or other medium, a computer virus, or computer
codes that damage or cannot be read by your equipment.
1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for
the “Right of Replacement or Refund” described in paragraph 1.F.3,
the Project Gutenberg Literary Archive Foundation, the owner of the
Project Gutenberg™ trademark, and any other party distributing a
Project Gutenberg™ electronic work under this agreement, disclaim
all liability to you for damages, costs and expenses, including legal
fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR
NEGLIGENCE, STRICT LIABILITY, BREACH OF WARRANTY OR
BREACH OF CONTRACT EXCEPT THOSE PROVIDED IN PARAGRAPH
1.F.3. YOU AGREE THAT THE FOUNDATION, THE TRADEMARK
OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL
NOT BE LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT,
CONSEQUENTIAL, PUNITIVE OR INCIDENTAL DAMAGES EVEN IF
YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH DAMAGE.
1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you
discover a defect in this electronic work within 90 days of receiving
it, you can receive a refund of the money (if any) you paid for it by
sending a written explanation to the person you received the work
from. If you received the work on a physical medium, you must
return the medium with your written explanation. The person or
entity that provided you with the defective work may elect to provide
a replacement copy in lieu of a refund. If you received the work
electronically, the person or entity providing it to you may choose to
give you a second opportunity to receive the work electronically in
lieu of a refund. If the second copy is also defective, you may
demand a refund in writing without further opportunities to fix the
problem.
1.F.4. Except for the limited right of replacement or refund set forth
in paragraph 1.F.3, this work is provided to you ‘AS-IS’, WITH NO
OTHER WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR ANY PURPOSE.
1.F.5. Some states do not allow disclaimers of certain implied
warranties or the exclusion or limitation of certain types of damages.
If any disclaimer or limitation set forth in this agreement violates the
law of the state applicable to this agreement, the agreement shall be
interpreted to make the maximum disclaimer or limitation permitted
by the applicable state law. The invalidity or unenforceability of any
provision of this agreement shall not void the remaining provisions.
1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation,
the trademark owner, any agent or employee of the Foundation,
anyone providing copies of Project Gutenberg™ electronic works in
accordance with this agreement, and any volunteers associated with
the production, promotion and distribution of Project Gutenberg™
electronic works, harmless from all liability, costs and expenses,
including legal fees, that arise directly or indirectly from any of the
following which you do or cause to occur: (a) distribution of this or
any Project Gutenberg™ work, (b) alteration, modification, or
additions or deletions to any Project Gutenberg™ work, and (c) any
Defect you cause.
Section 2. Information about the Mission
of Project Gutenberg™
Project Gutenberg™ is synonymous with the free distribution of
electronic works in formats readable by the widest variety of
computers including obsolete, old, middle-aged and new computers.
It exists because of the efforts of hundreds of volunteers and
donations from people in all walks of life.
Volunteers and financial support to provide volunteers with the
assistance they need are critical to reaching Project Gutenberg™’s
goals and ensuring that the Project Gutenberg™ collection will
remain freely available for generations to come. In 2001, the Project
Gutenberg Literary Archive Foundation was created to provide a
secure and permanent future for Project Gutenberg™ and future
generations. To learn more about the Project Gutenberg Literary
Archive Foundation and how your efforts and donations can help,
see Sections 3 and 4 and the Foundation information page at
www.gutenberg.org.
Section 3. Information about the Project
Gutenberg Literary Archive Foundation
The Project Gutenberg Literary Archive Foundation is a non-profit
501(c)(3) educational corporation organized under the laws of the
state of Mississippi and granted tax exempt status by the Internal
Revenue Service. The Foundation’s EIN or federal tax identification
number is 64-6221541. Contributions to the Project Gutenberg
Literary Archive Foundation are tax deductible to the full extent
permitted by U.S. federal laws and your state’s laws.
The Foundation’s business office is located at 809 North 1500 West,
Salt Lake City, UT 84116, (801) 596-1887. Email contact links and up
to date contact information can be found at the Foundation’s website
and official page at www.gutenberg.org/contact
Section 4. Information about Donations to
the Project Gutenberg Literary Archive
Foundation
Project Gutenberg™ depends upon and cannot survive without
widespread public support and donations to carry out its mission of
increasing the number of public domain and licensed works that can
be freely distributed in machine-readable form accessible by the
widest array of equipment including outdated equipment. Many
small donations ($1 to $5,000) are particularly important to
maintaining tax exempt status with the IRS.
The Foundation is committed to complying with the laws regulating
charities and charitable donations in all 50 states of the United
States. Compliance requirements are not uniform and it takes a
considerable effort, much paperwork and many fees to meet and
keep up with these requirements. We do not solicit donations in
locations where we have not received written confirmation of
compliance. To SEND DONATIONS or determine the status of
compliance for any particular state visit www.gutenberg.org/donate.
While we cannot and do not solicit contributions from states where
we have not met the solicitation requirements, we know of no
prohibition against accepting unsolicited donations from donors in
such states who approach us with offers to donate.
International donations are gratefully accepted, but we cannot make
any statements concerning tax treatment of donations received from
outside the United States. U.S. laws alone swamp our small staff.
Please check the Project Gutenberg web pages for current donation
methods and addresses. Donations are accepted in a number of
other ways including checks, online payments and credit card
donations. To donate, please visit: www.gutenberg.org/donate.
Section 5. General Information About
Project Gutenberg™ electronic works
Professor Michael S. Hart was the originator of the Project
Gutenberg™ concept of a library of electronic works that could be
freely shared with anyone. For forty years, he produced and
distributed Project Gutenberg™ eBooks with only a loose network of
volunteer support.
Project Gutenberg™ eBooks are often created from several printed
editions, all of which are confirmed as not protected by copyright in
the U.S. unless a copyright notice is included. Thus, we do not
necessarily keep eBooks in compliance with any particular paper
edition.
Most people start at our website which has the main PG search
facility: www.gutenberg.org.
This website includes information about Project Gutenberg™,
including how to make donations to the Project Gutenberg Literary
Archive Foundation, how to help produce our new eBooks, and how
to subscribe to our email newsletter to hear about new eBooks.
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com

More Related Content

PDF
Programming WebRTC Build Real Time Streaming Applications for the Web 1st Edi...
PDF
Programming WebRTC Build Real Time Streaming Applications for the Web 1st Edi...
PDF
Realtime Communication With Webrtc Peertopeer In The Browser 1st Edition Salv...
PPTX
WebRTC Seminar Report
PDF
Kamailio World 2017: Getting Real with WebRTC
PDF
WebRTC for non-telco people
PPTX
Deploying WebRTC successfully – A web developer perspective
PDF
8 pre launch steps to go with the web rtc based application development
Programming WebRTC Build Real Time Streaming Applications for the Web 1st Edi...
Programming WebRTC Build Real Time Streaming Applications for the Web 1st Edi...
Realtime Communication With Webrtc Peertopeer In The Browser 1st Edition Salv...
WebRTC Seminar Report
Kamailio World 2017: Getting Real with WebRTC
WebRTC for non-telco people
Deploying WebRTC successfully – A web developer perspective
8 pre launch steps to go with the web rtc based application development

Similar to Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley (20)

PDF
Boosting business with WebRTC - ClueCon 2017
PDF
Workshop web rtc implementation details
PDF
FOSDEM 2020: How can we make WebRTC Easier?
PDF
Spring Boot for WebRTC Signaling Servers: A Comprehensive Guide
PPTX
DeveloperWeek 2015 - WebRTC - Where to start and how to scale
PDF
Grow your business with webrtc app development
PDF
WebRTC on Mobile Devices: Challenges and Opportunities
PDF
WebRTC Webinar & Q&A - W3C WebRTC JS API Test Platform & Updates from W3C Lis...
PDF
WebRTC: Why and How?
PDF
Web rtc standards live session #13 - The Browser-Standards Gap
PDF
Introduction to WebRTC
PDF
Putting WebRTC to Work: Using the Standard in the Real World
PDF
A jQuery for WebRTC
PPTX
The Enterprise wants WebRTC -- and it needs Middleware to get it! (IIT RTC Co...
PDF
DevCon 5 (December 2013) - WebRTC & WebSockets
PDF
WebRTC, Whats in it for me?
PDF
Astricon WebRTC Update
PPTX
WebRTC and Web Design
PDF
Let's Get Real (time): Server-Sent Events, WebSockets and WebRTC for the soul
PDF
WebRTC for Telco: Informa's WebRTC Global Summit Preconference
Boosting business with WebRTC - ClueCon 2017
Workshop web rtc implementation details
FOSDEM 2020: How can we make WebRTC Easier?
Spring Boot for WebRTC Signaling Servers: A Comprehensive Guide
DeveloperWeek 2015 - WebRTC - Where to start and how to scale
Grow your business with webrtc app development
WebRTC on Mobile Devices: Challenges and Opportunities
WebRTC Webinar & Q&A - W3C WebRTC JS API Test Platform & Updates from W3C Lis...
WebRTC: Why and How?
Web rtc standards live session #13 - The Browser-Standards Gap
Introduction to WebRTC
Putting WebRTC to Work: Using the Standard in the Real World
A jQuery for WebRTC
The Enterprise wants WebRTC -- and it needs Middleware to get it! (IIT RTC Co...
DevCon 5 (December 2013) - WebRTC & WebSockets
WebRTC, Whats in it for me?
Astricon WebRTC Update
WebRTC and Web Design
Let's Get Real (time): Server-Sent Events, WebSockets and WebRTC for the soul
WebRTC for Telco: Informa's WebRTC Global Summit Preconference
Ad

Recently uploaded (20)

PPTX
202450812 BayCHI UCSC-SV 20250812 v17.pptx
PPTX
GDM (1) (1).pptx small presentation for students
PDF
Complications of Minimal Access Surgery at WLH
PDF
RMMM.pdf make it easy to upload and study
PPTX
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
PDF
O5-L3 Freight Transport Ops (International) V1.pdf
PPTX
Lesson notes of climatology university.
PDF
Supply Chain Operations Speaking Notes -ICLT Program
PPTX
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
PPTX
Cell Types and Its function , kingdom of life
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PPTX
Cell Structure & Organelles in detailed.
PDF
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
PDF
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
PDF
Anesthesia in Laparoscopic Surgery in India
PDF
VCE English Exam - Section C Student Revision Booklet
PPTX
Presentation on HIE in infants and its manifestations
PDF
Computing-Curriculum for Schools in Ghana
PDF
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
PDF
STATICS OF THE RIGID BODIES Hibbelers.pdf
202450812 BayCHI UCSC-SV 20250812 v17.pptx
GDM (1) (1).pptx small presentation for students
Complications of Minimal Access Surgery at WLH
RMMM.pdf make it easy to upload and study
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
O5-L3 Freight Transport Ops (International) V1.pdf
Lesson notes of climatology university.
Supply Chain Operations Speaking Notes -ICLT Program
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
Cell Types and Its function , kingdom of life
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
Cell Structure & Organelles in detailed.
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
Anesthesia in Laparoscopic Surgery in India
VCE English Exam - Section C Student Revision Booklet
Presentation on HIE in infants and its manifestations
Computing-Curriculum for Schools in Ghana
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
STATICS OF THE RIGID BODIES Hibbelers.pdf
Ad

Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley

  • 1. Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley download https://ebookbell.com/product/programming-webrtc-build-realtime- streaming-applications-for-the-web-1st-edition-karl- stolley-59015078 Explore and download more ebooks at ebookbell.com
  • 2. Here are some recommended products that we believe you will be interested in. You can click the link to download. Programming Webrtc Build Realtime Streaming Applications For The Web 1st Edition Karl Stolley https://ebookbell.com/product/programming-webrtc-build-realtime- streaming-applications-for-the-web-1st-edition-karl-stolley-58962332 Programming Webrtc Karl Stolley https://ebookbell.com/product/programming-webrtc-karl-stolley-58962336 Programming Webrtc 1st Edition Karl Stolley https://ebookbell.com/product/programming-webrtc-1st-edition-karl- stolley-58968442 Programming Languages And Systems 31st European Symposium On Programming Esop 2022 Held As Part Of The European Joint Conferences On Theory And Practice Of Software Etaps 2022 Munich Germany April 27 2022 Proceedings Ilya Sergey https://ebookbell.com/product/programming-languages-and-systems-31st- european-symposium-on-programming-esop-2022-held-as-part-of-the- european-joint-conferences-on-theory-and-practice-of-software- etaps-2022-munich-germany-april-27-2022-proceedings-ilya- sergey-44887738
  • 3. Programming 101 Learn To Code Using The Processing Programming Language 2nd Edition 2nd Jeanine Meyer https://ebookbell.com/product/programming-101-learn-to-code-using-the- processing-programming-language-2nd-edition-2nd-jeanine-meyer-46238180 Programming 101 The How And Why Of Programming Revealed Using The Processing Programming Language Jeanine Meyer https://ebookbell.com/product/programming-101-the-how-and-why-of- programming-revealed-using-the-processing-programming-language- jeanine-meyer-46318424 Programming And Gui Fundamentals Tcltk For Electronic Design Automation Suman Lata Tripathi https://ebookbell.com/product/programming-and-gui-fundamentals-tcltk- for-electronic-design-automation-suman-lata-tripathi-46318712 Programming With Openscad A Beginners Guide To Coding 3dprintable Objects 1st Edition Justin Gohde https://ebookbell.com/product/programming-with-openscad-a-beginners- guide-to-coding-3dprintable-objects-1st-edition-justin-gohde-46410140 Programming In Two Semesters Using Python And Java Quentin Charatan https://ebookbell.com/product/programming-in-two-semesters-using- python-and-java-quentin-charatan-46494972
  • 7. Early praise for Programming WebRTC Programming WebRTC is an exceptional book that teaches WebRTC theory through practical application of the specification. Through an iterative approach to imple- menting a WebRTC application, Programming WebRTC somehow makes an ex- tremely difficult and nuanced topic approachable and easy to understand. It is a must-read book on the topic. ➤ Stephen Watzman Software Engineer Karl has a passion for WebRTC that shows in this book, and combining that with his experience as an educator and a technologist, he’s crafted a book that is very accessible and informative. WebRTC development has a lot of nuances, but Karl approaches the topic in a way that is both reader-friendly and technically comprehensive. I highly recommend it to anyone new to WebRTC! ➤ Arin Sime CEO/Founder, WebRTC.ventures Dr. Karl Stolley, a developer, technical author, and prominent WebRTC expert, has consistently addressed the field’s challenges and advancements in conferences, including the IIT RTC, and other academic settings. His insights highlight the dynamic nature of WebRTC implementations, yet underscore their current robust- ness through practical examples often employed in his teaching. ➤ Alberto Gonzalez Trastoy Software Consultant and CTO, WebRTC.ventures
  • 8. This book expertly blends technical depth with humor, making WebRTC accessible. Even complex topics like signaling channels are tackled with wit, reminding us that a well-designed interface beats the “Imagine this doesn’t look like garbage” approach any day. ➤ Paul Freiberger co-author, Fire in the Valley
  • 9. Programming WebRTC Build Real-Time Streaming Applications for the Web Karl Stolley The Pragmatic Bookshelf Dallas, Texas
  • 10. For our complete catalog of hands-on, practical, and Pragmatic content for software developers, please visit https://pragprog.com. Contact support@pragprog.com for sales, volume licensing, and support. For international rights, please contact rights@pragprog.com. The team that produced this book includes: Dave Thomas Publisher: Janet Furlow COO: Susannah Davidson Executive Editor: Michael Swaine Development Editor: Vanya Wryter Copy Editor: Potomac Indexing, LLC Indexing: Gilson Graphics Layout: Copyright © 2024 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. When we are aware that a term used in this book is claimed as a trademark, the designation is printed with an initial capital letter or in all capitals. ThePragmaticStarterKit,ThePragmaticProgrammer,PragmaticProgramming,PragmaticBookshelf, PragProg and the linking g device are trademarks of The Pragmatic Programmers, LLC. Every precaution was taken in the preparation of this book. However, the publisher assumes no responsibility for errors or omissions, or for damages that may result from the use of information (including program listings) contained herein. ISBN-13: 978-1-68050-903-8 Encoded using recycled binary digits. Book version: P1.0—July 2024
  • 11. Contents Acknowledgments . . . . . . . . . . . ix Preface . . . . . . . . . . . . . . xi 1. Preparing a WebRTC Development Environment . . . . 1 Installing Node.js 2 Downloading the Supporting Code and Installing Dependencies 2 Serving HTTPS in Development 4 Choosing a Development Browser 6 Starting and Stopping the Server 7 2. Working with a Signaling Channel . . . . . . . 11 Preparing a Basic Peer-to-Peer Interface 11 Adding Video Elements: Self and Peer 14 Styling the Core App Elements 17 Adding Functionality to the Call Button in JavaScript 22 Positioning WebRTC as a Front-End Technology 25 Using a Lightweight Signaling Channel 27 Connecting to the Signaling Channel 32 3. Establishing a Peer-to-Peer Connection . . . . . . 37 Requesting User-Media Permissions 37 Setting Up the Peer Connection 42 Building Connection Logic to the “Perfect Negotiation” Pattern 51 Receiving Media Tracks 59 Testing Out Your First Peer-to-Peer App 60 4. Handling Data Channels . . . . . . . . . . 63 Adding Basic Visual Effects to User Videos 64 Determining Peer-Connection States 68 Applying Filters Remotely with Data Channels 71
  • 12. Uniquely Identifying Data Channels 73 Adding a Text-Chat Feature 75 Adding Logic to Handle Chat Events 81 Setting Up the Text-Chat Data Channel 83 Building a Message Queue 86 5. Streaming Complex Data . . . . . . . . . . 93 Structuring Chat Messages in JSON 93 Adding Mic and Camera Toggles 98 Refining the Initial Properties on Self and Peer 99 Building A/V Toggles 105 Sharing Features over Data Channels 112 Sending Images over the Chat 117 Sending and Receiving Binary Data 125 6. Managing Multipeer Connections . . . . . . . 133 Learning from a Failed Peer-to-Peer Call 134 Working with a Multipeer-Ready Signaling Channel 136 Revising the Signaling Logic on the Client 139 Generating Video Structures on the Fly 145 Initializing Peers as Needed 150 Fleshing out the Skeletal Signaling Callbacks 152 Working with Peer IDs in the handleScSignal() Callback 156 Restructuring WebRTC Callbacks with Closures 159 Sharing Features over Multipeer Data Channels 165 7. Managing User Media . . . . . . . . . . 173 Determining Device Availability 174 Detecting Device Changes 182 Removing User Media for Remote Peers 187 Programmatically Recognizing Denied Media Permissions 190 Setting and Applying Media Constraints Objects 193 8. Deploying WebRTC Apps to Production . . . . . . 197 Configuring a WebRTC App for Public Deployment 198 Configuring a Server to Host Your WebRTC App 202 Deploying Your App with Git 207 Monitoring Your App with PM2 210 Configuring Nginx for Reverse Proxies 212 Setting Up Your Own STUN/TURN Server 214 Contents • vi
  • 13. A1. Connection Negotiation in Legacy Browsers . . . . 225 Implementing Backward-Compatible Fixes 225 Bibliography . . . . . . . . . . . . 231 Index . . . . . . . . . . . . . . 233 Contents • vii
  • 14. Acknowledgments I wrote this book over the span of two very different careers and three jobs, with a global pandemic thrown in for good measure. Screenshots found throughout the book document my various hair lengths and unkempt beard for posterity. Thank you to my past and current employers, supervisors, and coworkers who in ways big and small graciously accommodated and supported my work on this: Ray Trygstad, Nina Kuruvilla & Kwindla Kramer, and Alya Abbott & Tim Abbott. Thank you to the students in my WebRTC classes who read and worked through the earliest iterations of this book, providing useful feedback that shaped what this work would ultimately become: Hareem Akram, Jon Andoni Baranda, Yelitza Castro, Chia-Chi Chang, Julaam J. Diop, Michael P. Kaczowka, David Singer, Rida Tariq, and Naveed Zahid. Thank you to my professional colleagues and friends who offered encourage- ment, interest, and support for this project: Nancy DeJoy, Molly Holzschlag, Carrie Malone, Jayne Mast, Kate McLaughlin, Adria Neapolitan, Brian Okken, Arin Sime, Alberto Gonzalez Trastoy, and Brian Watts. Thank you to my technical reviewers: Martin Deen, Tsahi Levent-Levi, and Kimberlee Johnson. Additional thanks to Dasha Day Hisholer for also reviewing this work while it was struggling to become a proposal. Special thanks to Aman Agrawal for his careful review of the completed book. I am solely to blame for any errors and shortcomings that remain. Thank you to the team at or closely orbiting Pragmatic Programmers: Tammy Coron, Tim Mitra, Erica Sadun, and especially Margaret Eldridge who, among other things, shepherded this work in its proposal form to a head-spinningly quick acceptance. report erratum • discuss
  • 15. A special thank you to my editor, Michael Swaine, for his encouragement, bone-dry wit, and saint-like patience in helping me see this book through to completion. Working with him was the best writing experience of my career. And the deepest thank you to my family: to my dog, Hank, for his countless hours of unflagging dog assistance and posing for screenshots in front of a laptop. And to Amy, my incredible wife of almost 20 years, for her patience, support, and boundless love: you are the very best one. Acknowledgments • x report erratum • discuss
  • 16. Preface WebRTC—or Web Real-Time Communication—is a standardized API exposed in all modern web browsers. The World Wide Web Consortium accepted the WebRTC specification as a full recommendation after a long decade of devel- opment.1 Couple a complete, stable specification with browser support for WebRTC ranging from rock-solid to serviceable, and you find yourself in a perfect environment for developing and deploying real-time web applications in the browser. Like any Web API, WebRTC doesn’t enjoy a perfectly spec-aligned implemen- tation in any browser. But this book will start you on your journey to devel- oping real-time streaming applications, all according to the certainty of a stable specification. You’ll also learn to write elegant, backward-compatible code to get your WebRTC apps working across the widest possible range of recent and modern browsers. Desktop and mobile devices, too. Support for WebRTC is everywhere. Your WebRTC Journey You’ll start your journey by getting straight to work on building a basic WebRTC application to support peer-to-peer video calling. Chapter by chapter, you’ll refine that app and its core logic to then spin up additional WebRTC- powered apps that will have your users sharing all manner of data with one another, all in real time. This book treats WebRTC as a part of the Web Platform. No third-party libraries or heavy downloads are required for you to make your way through this book, or for your users to use the WebRTC apps you build: you’ll be writing and strengthening your knowledge of modern HTML, CSS, and Java- Script to get the most out of browser-native WebRTC APIs. 1. https://www.w3.org/TR/webrtc/ report erratum • discuss
  • 17. From the outside, WebRTC is pretty daunting. Okay, really daunting. I’m proud of you just for peeking inside this book. But don’t put it down just because WebRTC will challenge you and twist your brain around in some profound ways. That’s both expected and totally okay. It means you’re on the path. We’ll walk it together. Things look better and more manageable from the inside. This book will get you on the path to where you want to go with WebRTC right away. You won’t find any throwaway code or opaque, puzzling examples here. We’re going to be developing real WebRTC applications together from the outset. And it will be from those real, functioning applications that we will tease out how WebRTC works. By the end of your journey, you will have all the foundational skills and knowledge you need to build your own wildly imaginative real-time applications. And because work on the WebRTC speci- fication continues, you will also learn how to stay on top of the latest changes and discussions. Who Should Read This Book? This book is aimed at intermediate and advanced web designers and developers looking to explore and implement real-time communication features in new or existing web applications. Whether you consider yourself a designer or a developer, WebRTC is one of those rare Web APIs where design and development converge head on: not just conceptually, but in the actual code you’ll be writing. WebRTC is a front- end technology that requires only the teeniest, tiniest server-side component, which I have provided for you in the codebase that accompanies this book. Almost all of your work will be run and rendered directly in the browser. You should have at least some knowledge of JavaScript, along with HTML and CSS. I’ll do my best to fill in any gaps that might arise for you and point out additional books and resources that you might find useful. You’ll find yourself working with other Web APIs in this book—not just WebRTC. Many of those will equip you with added knowledge to enhance your work on all kinds of web applications, whether or not they include a real-time component. What’s Covered (And What’s Not) This book covers WebRTC’s APIs as natively implemented in recent and modern web browsers. You’ll be working with those APIs directly in vanilla JavaScript to build your knowledge and command of WebRTC independent of any third-party libraries. The promise of WebRTC has always been to provide Preface • xii report erratum • discuss
  • 18. real-time communication right in the browser without requiring users to download special plugins or add-ons. Your users’ browsers already have everything needed to power your real-time app. All you have to do is build it! But to build a stunningly accessible and usable real-time application requires more than just a strong JavaScript foundation. It is an all-too-common mistake to think of WebRTC as just another conduit for media streams and application data. Because of its real-time component, WebRTC knits together two or more remote interfaces where real, live users are interacting and cooperating with one another. Building a WebRTC app without thinking carefully about the user interface would be like building a bicycle without the seat and handlebars. Ouch, right? So don’t be surprised to spend some time—perhaps more than you’d expect—working with HTML and CSS, too. You will be doing WebRTC development within the friendly confines of your local network for most of the book, but a chapter at the end will walk you through the necessary requirements and steps for deploying your real-time applications to the web, and testing them out. So what’s not covered? In a phrase, this book does not cover issues of scale or millisecond-obsessed WebRTC optimization. While there are a growing number of server-, platform-, and system-based implementations of WebRTC, as well as numerous WebRTC-based communication platforms as a service (CPaaS), those are all beyond the scope of this book. That means you won’t find coverage here of scaling apps up to handle dozens or thousands of con- current users supported by server-side technologies like selective forwarding units (SFUs) or multi-conference units (MCUs). Although you will work with a small server that provides a signaling channel, that is the extent of the server-side content in this book. And while you’ll learn about some fundamentals of streaming-media CODECs and optimization, this book does not go deep on those topics. Nor does it encourage session description protocol (SDP) “munging” to coerce browsers to use a particular CODEC, or fiddling with RTCRtpTransceiver objects. Native browser code for those matters is better tuned and tested than app-based adjustments likely ever will be. However, the core principles of WebRTC that we’ll look at in depth—working with a signaling channel, establishing peer connections, adding and managing media streams and data channels—will have you well prepared to tackle WebRTC implementations and third-party services wherever you might encounter them. report erratum • discuss What’s Covered (And What’s Not) • xiii
  • 19. How This Book Is Organized This book is organized in a set of sequential chapters. It’s meant to be read more or less front to back. If that feels overly prescriptive and stifling, or if you’ve just got a rebellious streak that you like to take out on tech books, try anyway to make it through at least the first four chapters before you jump around to the later ones whose topics most interest you. In Chapter 1, you’ll learn how to set up a development environment that will play nicely with WebRTC. You’ll also learn how to get the most out of the starter and example code that accompanies the book. With your development environment set up and tested out, Chapter 2 dives right into the only necessary server component of WebRTC apps: a signaling channel. As part of working with the signaling channel, you’ll start to build the basic interface of your first WebRTC app, which provides peer-to-peer video calls. Chapter 3 is where you’ll really hit your stride working directly with WebRTC’s APIs, all of which orbit around the RTCPeerConnection interface. By the end of this chapter, you’ll be streaming video between two connected peers which—to start—will be two browser windows on your desktop. Streaming real-time user video and audio is WebRTC’s most famous feature. But that’s not its only feature. Over the course of Chapter 4 and Chapter 5, you’ll go from streaming basic data to streaming more complex data— including JSON as well as images and other binary files. WebRTC provides a powerful and flexible low-level interface for streaming arbitrary application data between two peers, all in real time. You’ll learn to command that interface, and abstract away subtle differences found in browsers with incomplete WebRTC implementations. You’ll even bring what you learn full circle by safely implementing user audio to complete the silent streaming video you’ll work with at first. Buckle your seatbelt when you get to Chapter 6. Connecting two peers is one thing. But how about connecting three or more peers? Chapter 6 will have you establishing WebRTC calls using a mesh-network topography to enable multiple peers to join the same call simultaneously. You’ll also experi- ence the theoretical and practical upper limits on the number of peers who can join a call on a mesh network, depending on what your app does and the amount of bandwidth and processing power it consumes. In Chapter 7, you’ll work more in depth with the MediaDevices interface on the Media Capture and Streams API to do things like help users determine what Preface • xiv report erratum • discuss
  • 20. mics and cameras they have available, and handle edge cases in your logic when either there are no devices available, or users deny permission to access them. You’ll learn to do some minor media-stream optimization, too, with the aid of the built-in, real-time statistics that WebRTC implementations provide in the browser. Closing out the body of the book, in Chapter 8, you’ll learn how to deploy WebRTC applications to production. You’ll find a concrete deployment example, but you’ll also learn how to adjust that example to suit your own needs and preferences. And finally, you will find an appendix at the end of the book that will show you the necessary fixes for making your WebRTC applications work with legacy browsers that don’t support the WebRTC APIs necessary for perfect negotiation. Online Resources You can download the source code for studying and working alongside the examples in the book from pragprog.com.2 If you spot an error or even just come across something that is blocking your path forward in the book, con- sider this my personal invitation to you to join and post to the book’s forum on DevTalk.3 If you would like to contact me directly, I am available on Mastodon at @stolley@hachyderm.io.4 I also blog about WebRTC and other web topics at https://stolley.dev/ All right. Enough with the formalities that we classy preface readers enjoy while thoughtfully adjusting our monocles from the comfort of a high-back leather chair. Let’s pop out the monocle, pull open a laptop, and get down to it: it’s time to set up a development environment that will be your trusty companion on your exciting, monocle-free journey with WebRTC. 2. https://pragprog.com/titles/ksrtc/ 3. https://devtalk.com/books/programming-webrtc/errata 4. https://hachyderm.io/@stolley report erratum • discuss Online Resources • xv
  • 21. CHAPTER 1 PreparingaWebRTC DevelopmentEnvironment Exciting new technologies often require developers to level up the sophistica- tion of their development environments. WebRTC is no exception. Almost everything you’ll be doing with WebRTC happens in and between browsers. While you’ll be writing HTML, CSS, and JavaScript just as you would for any other web application, there are some important things to set up to smooth your way through the rest of the book and your work building real-time web applications. In this chapter, you’ll install Node.js if you haven’t already. You’ll also learn where to get yourself a copy of the code that accompanies this book, and you’ll take a brief tour of the code’s organization so you can find what you need, when you need it. You’ll then generate and make use of your own self- signed certificates for serving HTTPS in development. HTTPS is necessary to fully and reliably access many newfangled, highfalutin Web APIs—including WebRTC, even in development. You’ll choose a WebRTC-ready development browser (spoiler: Chrome or Firefox), fire up the server that’s packed in with the book’s code to serve your in-progress work or the completed examples, and heroically machete your way through the dire security warnings that your browser will throw at you over your self-signed certificates. It’ll be a little bit of work, but once you’ve set this all up for yourself, you shouldn’t have to think about any of it again. All of the setup here should work without much fuss or drama on Unix-like operating systems, including macOS. report erratum • discuss
  • 22. Install Windows Subsystem for Linux If you’re developing on Windows, you’ll need to install and use Windows Subsystem for Linux.1 Installing Node.js The small server I’ve written to support your local WebRTC development relies on Node.js, a wildly popular JavaScript runtime that you might already be familiar with and have installed. Let’s figure out if you’ve got a copy of Node.js already, and install one if you don’t. If you’re not sure if you have Node.js, run which node on your command line. You’ll see output showing the path to your Node.js installation, if you have one (now might be a good time to update it, if you haven’t in a while). If you see no output or know for a fact you aren’t running Node.js yet, no problem: there are a few different ways to install it. You can find, download, and run a Node.js installer for your operating system of choice from nodejs.org.2 Alternatively, if your operating system has a package manager available, such Homebrew for MacOS3 or your native package manager for Linux, you can install Node.js that way. However you opt to install Node.js, it’s generally a good idea to be running the latest LTS version.4 As a sanity check, once you’ve installed Node.js, you can run which node and which npm on your command line, which should report the locations where Node.js and its own package manager, npm, were installed. That’s simply confirmation that you’ve successfully installed Node.js, and also that your command line knows where to find it. Downloading the Supporting Code and Installing Dependencies Once you’ve set up Node.js, you should download the code for this book from pragprog.com.5 Once you’ve downloaded and decompressed the ZIP file,6 move 1. https://learn.microsoft.com/en-us/windows/wsl/install 2. https://nodejs.org/en/download/ 3. https://brew.sh/ 4. https://nodejs.org/en/about/previous-releases 5. https://pragprog.com/titles/ksrtc/programming-webrtc/ 6. https://media.pragprog.com/titles/ksrtc/code/ksrtc-code.zip Chapter 1. Preparing a WebRTC Development Environment • 2 report erratum • discuss
  • 23. the unzipped code/ directory somewhere you can conveniently access from the command line. You might also want to rename the directory to something more recognizable than code/. Having done that, use your command line to navigate to the directory you’ve set up. Once you’re there, you need to install the dependencies for the server that you’ll rely on as you work through the book. Run this command: $ npm install A bunch of output will fill the terminal screen, but installation should only take a few minutes at most. Probably less. If you see warnings about deprecated packages, you can try running npm audit fix or the more aggressive npm audit fix --force to try and resolve matters. But for doing WebRTC development, it’s okay to ignore such warnings altogether. Finding Your Way Around the Code Directory I’ve prepared the book’s accompanying code to include the examples that I’ve written and a separate starter directory for you to follow along in the book and experiment on your own. Here is a brief look at what you’ll find in the code directory and where: • The book’s completed examples are each in their own subdirectory under demos/. You’ll see references to those files throughout the book. • There is a www/ directory for you to work in as you make your journey through the book. If you get stuck, you can always compare your work against the files in the demos/ directory. Don’t forget to mutter and curse about me under your breath, which you’ll find therapeutic. As you work through the book, you’ll often find yourself in those two directo- ries. But the accompanying code includes some additional files and directories that you might be curious about: • The deploy/ directory contains a standalone WebRTC app that you’ll use in Chapter 8, Deploying WebRTC Apps to Production, on page 197. • The server.js file contains a basic web server using the ExpressJS frame- work. This file includes the basic signaling channels that are discussed later in the book. • The scripts/ directory contains a server startup script, start-server, which (surprise!) starts the server, but you can simply run npm start on your command line to fire up the server. report erratum • discuss Downloading the Supporting Code and Installing Dependencies • 3
  • 24. • For further study and your future experiments, you will find starter scripts for establishing peer connections in the _starter/ directory. p2p.js contains the logic necessary to establish a connection between two peers, and multi.js is enhanced with logic to establish a connection among three or more peers. Serving HTTPS in Development In order to develop WebRTC applications, it’s necessary to configure your development environment to serve HTTPS. Browsers disallow access to a number of APIs, including those that grant access to your microphone and camera, unless you’re serving HTTPS—even in development. To serve HTTPS in development, you’ll need to create and use your own self- signed certificates. It doesn’t take too much work to generate self-signed cer- tificates, but you can still impress your friends that you managed to serve HTTPS over localhost. Although self-signed certificates are not suited for use on the open web, they are perfectly acceptable for testing within the familiar comfort of your local network. The browsers you test your work on will nevertheless protest mightily, and they’ll do their best to scare you away from using self-signed certificates. And when that doesn’t work, they’ll try to make you feel bad about yourself. But don’t worry, and don’t feel bad: we’ll diffuse their unwarranted scare-and-shame tactics at the end of this chapter. Generating Self-Signed Certificates You’ll need access to openssl to generate your own certificate and key files on your operating system. MacOS and virtually all Unix-like operating systems and Linux distributions ship with openssl. If you’re a Windows user, you might need to install openssl yourself. The OpenSSL wiki maintains a list of download- able binaries.7 If you run Git Bash on Windows,8 it already includes openssl.exe, which you can also use. Before you generate the certificate files, you’ll need to create an easy-to- remember place for them to live. I recommend creating a Certs directory in your home directory: $ mkdir ~/Certs 7. https://wiki.openssl.org/index.php/Binaries 8. https://gitforwindows.org/ Chapter 1. Preparing a WebRTC Development Environment • 4 report erratum • discuss
  • 25. The book’s supporting code you downloaded includes a script you can run to generate your certificate files. You’ll need to change into the directory where you’re storing the book’s code in order to do this. The base command is npm run ssl-keys, and it needs two arguments: the path to the directory you created (keydir) and the number of days before your self-signed certificates expire (numdays). For example, to create self-signed certificate files in ~/Certs that won’t expire for about five years (1825 days), you’d run this command in the book’s code directory: $ npm run ssl-keys --keydir="$HOME/Certs" --numdays=1825 Whatever values you choose, use the $HOME variable instead of the tilde, ~, and ensure there are no spaces around your equals signs. If you’re the suspicious type, you can examine the ssl-keys script in the pack- age.json file before you run it. It’s based on a command suggested by Let’s Encrypt,9 which is ordinarily in the business of offering free certificates that can be used on world-facing websites. If you’d prefer, you can head over to Let’s Encrypt’s original post10 and copy from there into your command line instead of using the npm script. You’ll need to change into your certificates directory before running the Let’s Encrypt command to generate the certificate and key files. By default, openssl creates certificates that expire in one year. If you want to go longer than that without having to generate new ones, add -days followed by some number of days to the Let’s Encrypt script, like -days 1825 to create five-year certificates. However you generate your keys, once you hit Return, it will only take a moment to generate the key and certificate files in the directory you’ve chosen, like ~/Certs. Creating those files is an important first step to serving HTTPS. But you’ll also need to make sure that any scripts and servers you run can find your certificate files with as little fuss as possible. Let’s set that up next. Storing the Certificate File Locations in Environment Variables You’ll need to export two environment variables from your command line’s startup scripts. Those variables will point to the location of your self-signed certificate files. Your startup scripts will be in a file in your home directory called .bashrc or .bash_profile, if you’re a bash user, .zshrc if you’re a zsh user, or possibly even a file called .profile. 9. https://letsencrypt.org/ 10. https://letsencrypt.org/docs/certificates-for-localhost/ report erratum • discuss Serving HTTPS in Development • 5
  • 26. Whichever startup file your command line uses, open it in your favorite text editor, and add the following lines: # SSL Keys export LOCALHOST_SSL_CERT="$HOME/Certs/localhost.crt" export LOCALHOST_SSL_KEY="$HOME/Certs/localhost.key" Be sure not to put any spaces around the equals signs, =. And don’t forget to adjust the path if you’ve saved your keys somewhere different from ~/Certs or named them something other than localhost.crt and localhost.key. Once you’ve saved your startup file, reload it in your terminal using the source command. You’ll need to reference the name of the file your command line uses. In this example, the file is called .zshrc: $ source ~/.zshrc As a quick sanity check, you can confirm that your command line knows about these variables by using echo to output their values. To do this, prefix the variable names with a dollar sign: $ echo $LOCALHOST_SSL_CERT If everything has gone according to plan, you’ll see the certificate location you specified output by your command-line shell for LOCALHOST_SSL_CERT. On MacOS, for example, that will look something like /Users/username/Certs/localhost.crt. You can check the LOCALHOST_SSL_KEY value the same way, if you’d like. Choosing a Development Browser You’ll have the best possible development experience running the latest version of either Chrome or Firefox. I personally prefer Firefox Developer Edition,11 but the choice is yours. Whichever browser you choose, you’ll need to open its developer console and disable caching, so you always load the latest version of your CSS and Java- Script as you work. Firefox and Chrome both have the Disable Cache option under the Network tab of the developer pane. If you’re like me, you’ll opt to pop the developer console into its own window to maximize your available screen space. You’ll be building responsive interfaces that take advantage of the entire viewport. And you know the kinds of divas interfaces can be: steal their spotlight in any way and they’ll make your life miserable and spread salacious rumors about you to their friends. 11. https://www.mozilla.org/en-US/firefox/developer/ Chapter 1. Preparing a WebRTC Development Environment • 6 report erratum • discuss
  • 27. What About Safari? Safari has historically lagged in its WebRTC implementation, but as of April 2022’s release of Safari 15.4, it is also up to snuff. If you opt to use Safari as your develop- ment browser, or if you want to test out your work on an iPhone or iPad, please be sure you’re running at least Safari 15.4. Incomplete WebRTC implementations in older versions of Safari and other browsers (Firefox prior to version 80, and Chrome prior to version 75) will give you a series of raging headaches. Consult Appendix 1, Connection Negotiation in Legacy Browsers, on page 225 to learn about the fallbacks you’ll need to add to your code for the sake of older browsers, for as long as they remain in use (if history is a guide, that will be awhile). Starting and Stopping the Server I’ve tried to make it as painless as possible for you to serve both your own work and the completed demos over localhost. To serve your own files as you work on them, run npm run start or simply npm start on the command line. Your files are more important, so they get the more convenient commands. Remember to Install Dependencies If you haven’t yet run npm install, be sure to do so before starting the server, or if you encounter errors about missing modules. To serve the book’s completed examples, you’ll need to run the slightly more verbose command npm run start:demos. No space on either side of the colon. Whenever you start the server, you’ll see output like this in your terminal window: signaling-server: ** Serving from the www/ directory. ** signaling-server: signaling-server: App available in your browser at: signaling-server: signaling-server: -> https://127.0.0.1:3000/ signaling-server: -> https://192.168.1.6:3000/ signaling-server: signaling-server: Hold CTRL + C to stop the server. signaling-server: signaling-server: +0ms report erratum • discuss Starting and Stopping the Server • 7
  • 28. Each time you start the server, it will tell you which directory you’re currently serving from (again: www/ with your work, demos/ with the completed examples) and at least two addresses for reaching the server from your development browser of choice. Whenever you need to stop the server, hold down CTRL + C. The first time you start the server, your operating system might notify you about a firewall restriction of some kind. Because you’ll eventually be hitting this server from other devices connected to your local network, instruct your OS to allow incoming connections. With the server still running, you can open your browser to https://localhost:3000/. Of course localhost is a shortcut for 127.0.0.1, which you can also use if you get a thrill out of typing numbers and dots: https://127.0.0.1:3000/. Don’t worry for now about the second IP address you see. It will almost certainly be different from 192.168.1.6, but eventually you will be able to use whatever that second address is to test your app using other devices connected to your local network. One big gotcha: you absolutely must type out the full https:// protocol portion of these URLs. If you leave the protocol off, your browser will try to establish a connection over http://. The server isn’t actually listening for HTTP, but HTTPS. Your browser doesn’t know that, though, so it’ll dismissively inform you that it’s unable to connect. And you’ll lose a whole part of the day tracking down that missing s, which is made even more difficult to spot in browsers that hide the http:// protocol string on HTTP URLs. Once you enter the exact HTTPS-serving address, you’ll immediately hit a snag. Your browser, which demanded that you type out https:// in the address bar, now wants you to know that it has a big problem with your self-signed certificates. Instead of seeing the page the server is serving, you’ll get a security warning. Getting Past Browser Security Warnings After you point your browser to any of the server’s local https:// URLs, the browser viewport will fill with dire warnings, bad omens, and tales of an ancient curse. Firefox Developer Edition, for example, will present the screen on page 9. On the open web, these security warnings are a good thing. But in develop- ment, they’re just overly dramatic, pearl-clutching pains in the neck. The good news is that you’ll likely see this warning only the first time you hit your local address, and you will have to take these steps only once, too. To Chapter 1. Preparing a WebRTC Development Environment • 8 report erratum • discuss
  • 29. get Firefox to chillax and let you get on with your work, click the Advanced… button, and then the Accept the Risk and Continue button in the box that appears. On Chrome, you’ll likewise click Advanced and then the “Proceed to localhost (unsafe)” link. Safari makes you jump through a different series of hoops to accept self- signed certificates: when confronted by Safari’s warning screen, click on Show Details, then the “visit this website” hyperlink. That’s all you have to do on iOS, thankfully. But on MacOS, you’ll then be greeted by a popup asking, “Are you sure you want to visit this website on a connection that is not pri- vate?” Click “Visit Website” and then you’ll see another popup: “You are making changes to your Certificate Trust Settings.” Click Use Password… and then enter the password you use to log into your Mac. report erratum • discuss Starting and Stopping the Server • 9
  • 30. Once you’ve gone through those steps, you’ll see an overview page I’ve written for you. On the demos side, the page includes links to each of the demos. On the working side, from www/, you’ll see some instructions and encouragement. Next Steps Excellent. Having viewed one or both of those pages over HTTPS in your development browser of choice, you can be confident that you’ve successfully prepared your WebRTC development environment. You’ve installed Node.js, and you’ve located, downloaded, and used the command line to find this book’s accompanying source code and install its dependencies. You’ve also generated your own self-signed certificate files for serving HTTPS in develop- ment. And finally, you’ve convinced your browser, after some haggling over security, to accept your self-signed certificate. In the next chapter, we’re going to get right to work with the server you installed and fired up in this chapter. You’ll be working with its signaling- channel component—which we haven’t seen yet—while also constructing a user interface for your first WebRTC app. As we’ll see, WebRTC meshes tightly with user interfaces. So we will want to make sure the code we write for the interface is as well crafted and expertly engineered as everything we’ll be writing alongside WebRTC’s browser APIs. Let’s get going! Chapter 1. Preparing a WebRTC Development Environment • 10 report erratum • discuss
  • 31. CHAPTER 2 WorkingwithaSignalingChannel In this chapter, you’re going to build the foundations of a video-calling app that will use WebRTC for connecting two peers who can stream video—and eventually also audio—to each other, in real time. You’ll start by building an interface using semantic HTML and CSS, including a little flexbox for layout. Some of that work will strike you as being very precise and detailed. But the goal is to build a lean, accessible interface that also displays responsively across all types of devices. With the interface built, you’ll then do the necessary work to wire it up with JavaScript for handling routine events, like clicks, that happen in the browser. Those will eventually hook into the signaling channel and other WebRTC logic. So it’s necessary to build the interface first. With the interface built, we’ll take a look at the peer-to-peer architecture of WebRTC and how it differs from the more familiar client-server web architec- ture of HTTP and HTTPS. From there, we’ll take a sightseeing tour of a crucial piece of technology for establishing peer connections over WebRTC: a signaling channel, which will take the form of a small server that you’ve already downloaded with the book’s companion code. With a better understanding of the signaling channel in hand, you’ll then write some skeletal, foundational code for simultaneously connecting multiple pairs of peers over your app. Preparing a Basic Peer-to-Peer Interface Let’s begin with a basic process for building something new for the web: establishing a workable UI concept, structuring it in semantic HTML, and styling the HTML with just enough CSS to make it responsive across the full report erratum • discuss
  • 32. range of web-enabled devices. That will pave the way for much more graceful and efficient work with JavaScript for stitching the app together. The UI work here is going to be pretty precise, probably way more than what you’ve come to expect from books on specialized topics in web development. The advantage to engaging at least some front-end design in your development work is that when you’re selling some new API or idea to someone—a team lead, a manager, a client—you have something to show that actually looks good and helps to sell the idea a little more effectively than prefacing your work with “Imagine this doesn’t look like garbage.” But it’s also never too early to consider the obligations we as developers have to end users: front-end development involves precise work on interfaces that real users will have to interact with. Precise UI design is also the cornerstone of accessibility, which is as essential for real-time communication technologies as it is for anything else developed for the web. For that reason, it’s nothing less than a professional obligation to keep users in mind when building something explicitly for them—no matter how deep down the rabbit hole the underlying API takes us. With WebRTC, that rabbit hole is deep. And because the underlying WebRTC API is deep and complex, you’ll also see that we’re going to spend more than a little bit of time working through some core fundamentals of JavaScript, including code organization and the behavior of callback functions. The purpose of that work is twofold: first, as JavaScript continues to advance as a language, it can be harder to keep all developers on the same page with its newer features, especially as they relate to JavaScript’s mainstay features. And second, because of the design of the WebRTC API, I think you’ll find that you work more effectively within WebRTC’s dizzying number of event-driven methods and callback functions if the con- nections between them and vanilla JavaScript are made more explicit. That will be especially true if your routine, day-to-day interactions with JavaScript are mediated through a JavaScript framework with a higher-level API than plain old vanilla JavaScript has to offer. Designing Peer-to-Peer UI Patterns Before you start to write a line of code for your video-call app, think for a moment about the kinds of interfaces that are common in peer-to-peer apps you’ve used. Their interfaces are generally modeled on one of two patterns— a caller pattern or a joiner pattern. A caller pattern is asymmetric: certain peer-to-peer apps, like FaceTime or Skype, and even the telephone, depend on one peer calling and the other answering. The FaceTime interface, for example, looks the same for two peers inside and outside of a call. But while Chapter 2. Working with a Signaling Channel • 12 report erratum • discuss
  • 33. a call is being placed, the interface is different for both the caller and answerer. The interfaces return to a symmetric state once the person being called decides either to answer, causing both interfaces to show the call, or to decline (or simply ignore), in which case both interfaces return to their states before the call was placed. Other peer-to-peer apps, like Google Meet or Zoom, use a symmetric joiner pattern: instead of setting up a call between a caller and an answerer, sym- metrically patterned apps treat a call as something that already exists for users to join whenever they’re ready. The interface’s state depends only on what any one user is doing: preparing to join a call, participating in a call after joining, or leaving the call. WebRTC-backed interfaces can be constructed using either pattern, caller or joiner. But the joiner pattern simplifies the interface, because there’s no need to create a screen to handle an incoming call, or wire up a bunch of buttons to answer or decline. Instead, one button is all that’s needed—Join Call—and it’s presented to each peer. In the joiner pattern, we don’t even have to think in terms of someone calling someone else. From the perspective of the peers on the call, no one cares who joined first. As the designers of the app, we don’t have to care, either. Adding a Header and Button With a joiner pattern in mind, you can start building the HTML and CSS to structure and style the app. No need to worry about special HTML, CSS, or JavaScript for a caller or an answerer: everyone on the call is a joiner, so everyone gets the same code. Open the www/basic-p2p/ directory and find its index.html file, which already links to the CSS and JavaScript files you’ll use. Start off by writing a basic header that has a first-level heading along with a button element for joining the call. You can write this inside the <main> element you’ll find in the HTML file. The button will enable users to both join and leave the call, but you should set it up initially as a join button. When the app first loads, users will need to join the call: demos/basic-p2p/index.html <main id="interface"> <header id="header" role="banner"> <h1>Welcome</h1> <button class="join" type="button" id="call-button">Join Call</button> </header> </main> report erratum • discuss Preparing a Basic Peer-to-Peer Interface • 13
  • 34. The type="button" attribute might look redundant, but it’s good practice to include it for any button element without a browser-provided behavior: <button> defaults to type="submit" for submitting form data, in the absence of an explicit type attribute. But this button is not submitting a form. The unique ID call-button is deliberately generic. It will save a lot of work we would otherwise have to do in JavaScript to swap separate join and leave buttons. The class join will work as a nice styling hook in CSS and, later, for JavaScript to determine the state of the button. The button’s Join Call text should be an unambiguous cue to users. In a bit, a little JavaScript will change the button’s class to leave and its text to Leave Call once the Join Call button has been clicked. Adding Video Elements: Self and Peer With a basic heading and button in place, the only other HTML this basic app needs is for handling video: one element for a user’s own video stream, which we will refer to as self, and one element for the remote user’s video stream, which we’ll refer to as peer. One of the many fun aspects of working with peer-to-peer streaming media is that you get to write some things in your markup that are usually big no- nos. One such thing is setting up multiple video elements to play simultane- ously (one for self, one for the peer), with some attributes that might surprise you if you’re familiar with the <video> element introduced in HTML5. If you’re not familiar, that’s okay, too. We’ll walk through them. Setting up the Self Video This is how to set up the video element for the self video, which for convenience takes an ID of self: demos/basic-p2p/index.html <video id="self" autoplay muted playsinline poster="img/placeholder.png"> </video> That <video> element sets a number of important attributes. Let’s briefly explore each one’s purpose in the context of live-streaming video. Chapter 2. Working with a Signaling Channel • 14 report erratum • discuss
  • 35. The autoplay Attribute Ordinarily the autoplay attribute is frowned upon. Users generally expect control over the playback of video and especially the accompanying audio. Browser makers have helped to enforce user control by disregarding the autoplay attribute until a user has interacted with a page in some way first. But for streaming video, autoplay is strictly necessary: without it, the browser will only show the very first frame of a streaming video. And because users will have to click the Join Call button you built above, they will have interacted with the page before any media begins to stream—all but guaranteeing that the browser will respect the autoplay attribute. The muted Attribute In the next chapter on page 37, for same-machine testing purposes, we’ll exclude audio from the streaming tracks entirely. But to ensure that future audio-enabled streams don’t cause hellacious feedback, the self video takes the muted attribute. That’s different from muting your mic so no one else can hear you—a topic we will get to in a later chapter. All this attribute does is disable audio on the self video. The playsinline Attribute The final Boolean attribute to include is playsinline. This attribute instructs mobile devices in particular not to launch a full-screen presentation of the video once the stream starts, which is the default behavior in Safari on iOS and other mobile browsers. While full-screen video might sound desirable, it will obscure anything else on the page, including any user-interface compo- nents and the self video. With playsinline set, the peer video will play wherever on the page you place it with CSS. The poster Attribute And although not strictly necessary, a placeholder image can be referenced from the poster attribute. The image will display until the video stream starts, which is a graceful way to prepare users to expect to see video streams on the page. It’s helpful for testing purposes too: without a poster or some explicit dimensions and colors set in CSS, video streams would appear out of apparent nothingness—or not appear at all—if there’s something wrong with media permissions or the peer connection that you’ll set up in Setting Up the Peer Connection, on page 42. report erratum • discuss Adding Video Elements: Self and Peer • 15
  • 36. Setting up the Peer Video The markup for the peer video is almost identical to the self video, except that it takes an ID of peer and omits the muted attribute: demos/basic-p2p/index.html <video id="peer" autoplay playsinline poster="img/placeholder.png"> </video> You might have noticed that there is neither an src attribute nor any inner <source> elements for either video. Their omission is intentional. There’s no file involved in streaming video. In the next chapter, you’ll use JavaScript to set up the streaming source for each video element, using the srcObject property, in the displayStream() function on page 41. Finally, let’s wrap both <video> elements in an <article> element with an id of videos. It should open with a second-level heading to label the streaming videos for accessibility purposes. We’ll give the heading a class of preserve-access that we can refer to from the CSS in a moment. Putting it all together, your HTML file should look something like this: demos/basic-p2p/index.html <main id="interface"> <header id="header" role="banner"> <h1>Welcome</h1> <button class="join" type="button" id="call-button">Join Call</button> </header> <article id="videos"> ➤ <h2 class="preserve-access">Streaming Videos</h2> ➤ <video id="self" autoplay muted playsinline poster="img/placeholder.png"> </video> <video id="peer" autoplay playsinline poster="img/placeholder.png"> </video> </article> ➤ </main> That’s it: a heading, a button, and two video elements—all wrapped up in a neat package of semantic sectioning elements. Let’s style everything up in CSS. Chapter 2. Working with a Signaling Channel • 16 report erratum • discuss
  • 37. Styling the Core App Elements With the HTML in place, you can turn your attention to writing some CSS to present a workable interface. I always begin my work with Eric Meyer’s Reset CSS.1 There’s no need to replicate that here, but you will see a minified version of it in the starter screen.css file for this app. It’s useful to begin by defining an app’s basic typographic properties, usually on the html selector: demos/basic-p2p/css/screen.css html { font-family: "Lucida Grande", Arial, sans-serif; font-size: 18px; font-weight: bold; line-height: 22px; } That text setting might strike you as a bit large and bulky, especially because these are also meant to be mobile-first styles. But that’s deliberate: think about your face’s position relative to your screen when you’re on a video call. Most of us move back from the screen so that the camera can capture at least our entire heads. And if your head is a prize-winning pumpkin like mine, you might have to move back. With users’ eyes further from the screen, it’s a more accessible choice to err on the side of an oversized, easily visible UI—especially for controls. Let’s also add some foundational layout styles that set box-sizing to the more intuitive border-box value, and add some padding around the interface and header elements: demos/basic-p2p/css/screen.css /* Layout */ * { box-sizing: border-box; } #interface { padding: 22px; } #header { margin-bottom: 11px; } #header > h1 { margin-bottom: 11px; } 1. https://meyerweb.com/eric/tools/css/reset/ report erratum • discuss Styling the Core App Elements • 17
  • 38. Because the “Streaming Videos” heading is meant to help low-vision users navigate the page’s structures, let’s use an accessible technique to hide the heading from sighted users while keeping it available in the accessibility tree. We will reuse this class on additional elements in later chapters: demos/basic-p2p/css/screen.css .preserve-access { position: absolute; left: -20000px; } Styling the Button Element The <button> element, the only interactive UI on the page, is generally easier to style than other form elements. You can set up a basic look for it on the button element selector, opening with a bunch of font styles to inherit the look of all text on the page, as well as setting the cursor to display as a pointer: demos/basic-p2p/css/screen.css button { font-family: inherit; font-size: inherit; font-weight: inherit; line-height: inherit; cursor: pointer; /* Box Styles */ display: block; border: 0; border-radius: 3px; padding: 11px; } For better control over the button’s place in the layout, it’s worth setting it to display as a block. You can also remove the default border that browsers draw, given the button rounded corners with a small border-radius. A little bit of margin and padding—derived from the page’s 22px line-height value— provide some room around the text and some margins to offset the element. Nothing fancy. (And don’t worry—if your familiarity with responsive design has you feeling a gnawing guilt for using pixel units, you’re welcome to make those adjustments yourself. Or give yourself permission to work with pixel units while you’re still learning WebRTC.) The button’s HTML currently has only the join class, but you can still set up some very basic colors on both the join and leave classes for the button: Chapter 2. Working with a Signaling Channel • 18 report erratum • discuss
  • 39. demos/basic-p2p/css/screen.css .join { background-color: green; color: white; } .leave { background-color: #CA0; color: black; } Finally, let’s set a width on the #call-button selector: demos/basic-p2p/css/screen.css #call-button { width: 143px; /* 6.5 typographic grid lines */ margin-right: 11px; } The 143px width value is tailored to comfortably fit the text for Join Call and Leave Call, while also being derived from the page’s 22px line height. All the width does is ensure the button won’t shift the layout around when the but- ton’s text changes from Join Call to Leave Call. That doesn’t matter much when the button is on its own line, but as a small responsive touch, let’s add a media query to display the contents of the <header id="header"> as flex items, all on the same line: demos/basic-p2p/css/screen.css @media screen and (min-width: 500px) { #header { display: flex; flex-direction: row-reverse; align-items: baseline; justify-content: flex-end; } #header > * { flex: 0 0 auto; } #header > h1 { margin-bottom: 0; } } If you’re not familiar with flexbox and its properties, what’s happening here is the page header is set to display as a flexbox with display: flex. Aligning the flex items—the first-level heading and the button—to the baseline keeps the text of each on the same invisible line. report erratum • discuss Styling the Core App Elements • 19
  • 40. Then, to move the join button so it sits to the left of the heading, flex-direction: row-reverse flips the order of the header’s items, while justify-content: flex-end, on a row-reversed flexbox, will keep the flex items aligned to the left of the flexbox. The child selector #header > * sets the behavior of the flex items (an <h1> and <button>, in this case). The shorthand flex sets the grow and shrink values to zero, meaning that neither element will grow or shrink from its auto width. For the first-level heading, that will be the width of its text. For the button, that will be the 143px value of the width property set on the #call-button selector. The result is that on viewports 500 pixels and wider, the button and heading sit on the same line as in the figure on page 20. That will reduce how far down the viewport the header pushes the video elements. And speaking of the video elements, let’s style them next. Styling the Video Elements As embedded content, the <video> element displays inline by default, just like the <img/> tag does. Ethan Marcotte made famous the pairing of display: block and max-width: 100% for responsive images,2 and we can do the same for videos. Setting the max-width property ensures the video element will be no wider than either its parent element’s width or, in the case of this small app, the viewport: demos/basic-p2p/css/screen.css /* Video Elements */ video { background-color: #DDD; display: block; max-width: 100%; } 2. https://alistapart.com/article/fluid-images/ Chapter 2. Working with a Signaling Channel • 20 report erratum • discuss
  • 41. You can set the background color on video elements to a very light shade of gray, which will show the exact boundaries of the video elements and work nicely with the transparent smiley face PNG set on the video element’s poster attribute. The peer video can display as is, but a few adjustments to the self video will make it obvious which video is which. That’s helpful if you have just a single camera on your computer. The same streaming video image will appear in both video elements when you’re testing out your app: demos/basic-p2p/css/screen.css #self { width: 50%; max-width: 320px; margin-bottom: 11px; } That reduces the self video’s width to 50 percent, and puts a half line-height of space between it and the peer video that sits below. With all of that CSS in place, you can reload the page in your browser to see the button, heading, and video placeholders: report erratum • discuss Styling the Core App Elements • 21
  • 42. Adding Functionality to the Call Button in JavaScript That’s all the structure and styling your app needs. Now it’s time to cozy up to JavaScript. We’ll start by focusing on the call button’s functionality. The first thing we’ll do in the main.js JavaScript file is invoke strict mode. That’s as simple as writing strict mode as a string at the top of the file: demos/basic-p2p/js/main.js 'use strict'; Invoking strict mode is good practice. Strict mode will often reveal errors and inconsistencies in your JavaScript that the browser would otherwise suppress or ignore. A strict browser turns out to be a real asset when you’re trying to debug and track down errors in your JavaScript. If you’re interested, you can read more about strict mode at MDN.3 Now onto the call button, which we’ll handle as a one-off line of JavaScript in the “User Interface Setup” portion of the JavaScript file. You can use the querySelector() method on the document object to select the call button from the HTML. If you’ve not used querySelector() before, know that it accepts a string containing the same selector syntax that you’d write in CSS:4 document.querySelector('#call-button'); With the #call-button element selected, you can then call the addEventListener() method to respond to click events on the button. Start small and report in the console that the button has been clicked: document.querySelector('#call-button') .addEventListener('click', function(event) { console.log('Call button clicked!'); }); Reload your page in the browser and open the JavaScript console. You should see “Call button clicked!” appear in the console. Writing Named Functions as Callbacks While the querySelector method takes a single argument—the CSS selector #call- button—the addEventListener method takes two required arguments. The first argument is the name of the event, 'click', and the second argument is an anonymous callback function, also known as a listener. The function is 3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode 4. https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector Chapter 2. Working with a Signaling Channel • 22 report erratum • discuss
  • 43. anonymous because it doesn’t have a name: the function keyword just defines the function in place. I like callback functions to have names and their own place to live in a JavaScript file. In this case, there is a “User-Interface Functions and Callbacks” area in the main.js file. So the first little improvement is to create a named function and pass its name in as the second argument to addEventListener(): /** * User-Interface Setup */ document.querySelector('#call-button') .addEventListener('click', handleCallButton); /** * User-Interface Functions and Callbacks */ function handleCallButton(event) { console.log('Call button clicked! Named callback function active!'); } With the handleCallButton function passed in as a reference, the code looks a little cleaner: we can simply glance at the event, click, and the descriptive name of the function to be called in response to the event. Even better, the nitty- gritty details of handleCallButton are in their own spot a little further down in the JavaScript file. Maintaining those kinds of organizational habits will be even more important once the file includes more signaling and WebRTC code, which is almost entirely driven by events and their callbacks. Reload your app in the browser and click the button once again. The console should show the longer message announcing that the named callback function is active. If you saw the message in the console before you clicked, it’s possible that you might have called the function by mistake, rather than passing it in by reference: document.querySelector('#call-button') .addEventListener('click', handleCallButton()); // Oops! If you included the opening and closing parentheses, (), on handleCallButton, what gets passed into addEventListener is not the reference to the callback function, but the result of the callback function after it runs prematurely. If that happened, make sure that you’re passing in the name of the function only: handleCallButton, without parentheses. Then refresh the app in your browser and try again. report erratum • discuss Adding Functionality to the Call Button in JavaScript • 23
  • 44. Working with Data Returned to Callback Functions The addEventListener() method, like many methods that accept callback functions, passes along a chunk of data to the callback when its event fires. With addEventListener(), it’s basically convention to pass that data into the callback as either event or simply e. Choose whichever you prefer, but note that the code you’ll see throughout this book uses e for errors and spells out event for all events. The target property on event is a DOM object representing the element that was clicked. Because it’s a DOM object, much like we’d get from using document.querySelector(), we can do DOM things with it, like determine its id: function handleCallButton(event) { console.log('Button with ID', event.target.id, 'clicked!'); } Refresh and click the button again, and watch for “Button with ID call-button clicked!” to appear in your console. We can do even more with event.target to make the call button fully functional: 1. Change the button’s class from join to leave, and vice versa 2. Change the button’s text from “Join Call” to “Leave Call,” and vice versa Let’s set that up. To make the code more readable, hang onto the event.target value in a local variable called call_button: demos/basic-p2p/js/main.js function handleCallButton(event) { const call_button = event.target; if (call_button.className === 'join') { console.log('Joining the call...'); call_button.className = 'leave'; call_button.innerText = 'Leave Call'; } else { console.log('Leaving the call...'); call_button.className = 'join'; call_button.innerText = 'Join Call'; } } Refresh your app once more. Now you can click the button again and again. Its text will change and, thanks to CSS that you wrote earlier, so will its appearance. The console will also log “Joining the call…” or “Leaving the call…,” depending on what state the button is in. Excellent. All of the foundational HTML, CSS, and JavaScript for the video- call app’s interface is now in place. Up next, we need to start adding in logic for the signaling channel. But before we get to that, let’s take a code break Chapter 2. Working with a Signaling Channel • 24 report erratum • discuss
  • 45. and look a little bit at WebRTC as implemented in the browser—and learn why a signaling channel is necessary for WebRTC. Positioning WebRTC as a Front-End Technology With an imposing name like WebRTC: Real-Time Communication in Browsers, the WebRTC specification sure sounds like a newfangled server-based tech- nology.5 As web developers, especially front-end developers, we’ve simply never seen browsers do much else than talk to a server, whether to make requests for static HTML pages, process data submitted from a form, or asynchronously request resources with the Fetch API. The traditional web has always been a 100 percent server-backed technology. Front-end developers are left to mush their faces against the windowpane to the server room, where all the cool new toys often are. They’re not wrong to feel that way: the web has always relied on the client- server architecture of HTTP, where the client part is usually synonymous with browser. (Web-standards documents refer to user agents—which are clients or browsers, too.) Client-server architecture means that even something as basic as a text-based chat application requires relaying each chat message from a browser, through a server, and over to the receiving browser. WebSockets have enabled persistent connections and push-like behavior, so chat messages appear to come across instantaneously from one user to another. But even with WebSockets, the basic architecture of the web remains unchanged, including the server as the central hub. Not so with WebRTC. As a genuine peer-to-peer technology, the architecture of WebRTC as implemented in web browsers just about eliminates the need for an intermediary server. (True, there are server, OS, and even smart-device implementations of WebRTC, but they represent different use cases beyond the scope of this book.) Once a connection is established, WebRTC in the browser enables any two peers to directly exchange data, including streaming media, over an encrypted low-latency connection without their data ever touching a web server. Previewing Peer-to-Peer Connection Negotiation Of course, there’s one little hitch: WebRTC is peer-to-peer only once a connec- tion is established. RTC peer connections take a little bit of work to establish, as you’ll see, and usually with the aid of a server that provides a signaling channel. 5. https://www.w3.org/TR/webrtc/ report erratum • discuss Positioning WebRTC as a Front-End Technology • 25
  • 46. As web developers, we’re used to HTTP’s request-response connection pattern: if you have a domain name or even just an IP address, you enter it in the address bar of the browser and boom—connection established and resource or error message delivered in response to the request. End of story. You’ve been doing that already with the little local web server for previewing your work on the video-call app’s interface. Subsequent requests to the same website work the same way as the first request, but typically without a per- sistent connection between browser and server unless you’re dealing with something fancy like WebSockets or a web server with keepalive functionality. Establishing a connection over WebRTC is more involved than request- response. The two connecting peers have to negotiate the terms of the connec- tion before it can be established. Instead of request-response, peer connections are negotiated by an offer-answer pattern over a signaling channel. The offer-answer structure that establishes a peer connection is metaphori- cally no different from a conversation between two friends making plans to meet for coffee. Unless they share a remarkably strong psychic connection, the two friends won’t show up the same day and time at the exact same coffee shop. They must first negotiate the details of the coffee date, using some kind of a signaling channel: email, phone, text, or something conventional like that. Even a remarkably strong psychic connection functions as a signaling channel, when it comes right down to it. But let’s stick with a more conventional example. One friend initiates the coffee date by opening a signaling channel: sending a text, making a phone call, or yelling up the street. The initiating friend provides proposed details on a plan to meet up, and the other friend responds. The friends go back and forth, offering and answering over their shared signaling channel, until they reach an agreement to meet at a specific time and place. Regardless of the signaling channel used (text, phone, email), their conversation goes something like this: Friend A: How about we meet for coffee next Tuesday at 10, at The Red Eye? Friend B: I could do Tuesday, but not until 11. Friend A: That works, but I have another meeting at 12:30 on the other side of town, so let’s meet at Common Grounds instead. Friend B: Okay! See you on Tuesday at 11 at Common Grounds. Friend A: Sounds good! Again: all of that negotiation takes place over a signaling channel, which is independent of the destination coffee shop. The coffee shop itself cannot serve Chapter 2. Working with a Signaling Channel • 26 report erratum • discuss
  • 47. as a signaling channel, because even the question of which coffee shop may have to be negotiated—as was the case in the little exchange above. Establishing a peer-to-peer connection between two browsers works much the same way. At least one browser will make an offer to connect, and the other browser will return some kind of response. The process continues like that over the signaling channel, like friends setting a coffee date over text, until the browsers agree on how to open the peer connection. Using a Lightweight Signaling Channel Browsers lack a signaling channel that enables a connection to be negotiated. The editors of the WebRTC specification have gone so far as to avoid requiring any specific signaling technology whatsoever: you have to bring your own. So to prepare the way to establish a WebRTC connection, we need first to select and set up a signaling channel. A server-based signaling channel is the most convenient option. Even a very basic peer-to-peer application, like the one you’re writing right now, requires both browsers to visit a URL pointing at some server on the web to download the HTML, CSS, and JavaScript necessary to establish and support the call. With a web server already in the mix, any server-side setup for passing a message from one browser to another would suffice. Technically speaking, a server-based signaling channel isn’t strictly necessary. Two peers connecting over WebRTC could, in theory, email or even handwrite and make no-contact delivery of their browsers’ offers and answers to set up the peer connection—just like the two friends above could have set their coffee date using semaphore, tin cans on a string, a dead drop, or a whole bunch of other improbable signaling channels. But a server is going to provide the greatest flexibility and much lower latency. It would be possible, for example, to write your own signaling channel using WebSockets, in any server-side language you choose: PHP, Python, Ruby, and so on. To prevent you from getting sidetracked by all of that work, I have written a very small server in ExpressJS6 that includes a signaling channel based on Socket.IO.7 It’s included in the book’s sample code you already downloaded. The signaling channel works out of the box. While you might find value in experimenting with it, you won’t be writing much server-side code in this book. Our focus is in the browser, because that’s where the real action is. 6. https://expressjs.com/ 7. https://socket.io/ report erratum • discuss Using a Lightweight Signaling Channel • 27
  • 48. But before we get to work in the browser, you will better grasp the code you’re about to write for using the signaling channel if we take a quick walking tour of the signaling channel itself. The channel is very little and very dumb: all it does is manage a few events and shuttle messages back and forth. It doesn’t know about WebRTC or anything else that we might be doing. To the greatest extent possible, it’s better to keep knowledge of WebRTC in and between browsers. The less your signaling channel does, the easier it will be to move your app to a different signaling channel in the future. The server’s signaling channel component is fewer than a dozen lines, all of which can be reproduced here. As you’ve seen, sometimes I walk through setting up a piece of code, line by line. Other times, I will do a dramatic reveal of the whole thing before talking through it with you. Like here: server.js const namespaces = io.of(/^/[0-9]{7}$/); namespaces.on('connect', function(socket) { const namespace = socket.nsp; socket.broadcast.emit('connected peer'); socket.on('signal', function(data) { socket.broadcast.emit('signal', data); }); socket.on('disconnect', function() { namespace.emit('disconnected peer'); }); }); Two features of the signaling channel set the stage for the code we’ll write in the browser: a set of events and a precise seven-digit namespace. Let’s look at the events first. Exploring the Signaling Channel’s Events The signaling channel handles seven events: three that it listens for—connect, signal, and disconnect—and four that it emits—connected peer, signal, and disconnected peer, plus a connect event that Socket.IO emits automatically. When a client connects, the signaling channel will broadcast the connected peer event to other connected clients. When a client sends a signal, the signaling channel will rebroadcast the signal event and its data, essentially acting as a repeater. And finally, when a client disconnects, the signaling channel will emit a disconnected peer event. Chapter 2. Working with a Signaling Channel • 28 report erratum • discuss
  • 49. That captures everything a basic signaling channel needs to do: listen for connections, listen for and repeat signals, and listen for disconnections. That’s it. The code we write in the browser to establish a peer connection will trigger or respond to each of those events. Namespacing the Signaling Channel The video-call app you’re building is meant to scale: it will allow multiple different independent pairs of peers to connect to each other simultaneously. That is no different from how Zoom or Google Meet works. When you use Zoom, you and the person you want to talk to must share (over some other signaling channel!) a unique URL like https://fake-example.zoom.us/j/72072139453. You’ll also be building unique URLs similar to that. The opening lines of the signaling channel in server.js use a regular expression to verify the structure of a namespace, which is similar to a meeting code in Zoom. With a shared namespace, one pair of peers can negotiate their connection over the namespace /0000001, while another pair can connect simultaneously over /0000002. Their signals will never cross. Those are very easy to guess patterns, of course, so we’ll write a function in the browser to test or generate a random number that matches the name- space’s expected seven-digit pattern on the server. We’ll make the namespace easy for users to share by attaching it as a hash on the URL, something like https://localhost/basic-p2p/#1234567. (Later in the book, we’ll use the server to gen- erate namespaces using URL paths rather than hashes.) At the very bottom of the main.js file, you will find a section for utility functions. Here I’ve written a function called prepareNamespace() that takes two arguments. The hash argument will work with an existing hash, likely as reported by the browser from window.location.hash. The second argument, set_location, is a Boolean value (true or false) for setting the prepared namespace on window.location.hash. Look through the whole thing, and then let’s walk through it together. demos/basic-p2p/js/main.js /** * Utility Functions */ function prepareNamespace(hash, set_location) { let ns = hash.replace(/^#/, ''); // remove # from the hash if (/^[0-9]{7}$/.test(ns)) { console.log('Checked existing namespace', ns); return ns; } report erratum • discuss Using a Lightweight Signaling Channel • 29
  • 50. ns = Math.random().toString().substring(2, 9); console.log('Created new namespace', ns); if (set_location) window.location.hash = ns; return ns; } The local variable ns uses the replace() string method with a regular expression and empty string to remove the # (octothorp) that window.location.hash always returns. With the octothorp removed, the function can check the namespace’s value against another regular expression pattern: /^[0-9]{7}$/. That is almost identical to the signaling channel’s pattern you already saw in server.js (its pattern must also check for the slash that Socket.IO prepends to namespaces). Demystifying Regular Expressions If you’ve never worked with regular expressions before, or if the phrase regular expressions makes your palms sweat and your chest tighten, please try to relax. Their basic purpose is pretty easy to summarize: regular expressions describe patterns. Let’s examine the regular-expression pattern /^[0-9]{7}$/ from the inside out. A pattern that matches a sequence of seven digits, 0 through 9, looks like this: [0-9]{7}. [0-9] represents all numbers in the range zero to nine. The seven in curly braces, {7}, means that we want the numbers in the range to appear seven times in a row. Easy enough, right? You’d think. The problem is that any string of text that includes seven digits in a row—no matter what other text the string includes—would still satisfy a regular expression pattern specifying seven sequential digits. To make sure the namespace is only ever exactly seven digits and therefore prevent potentially malicious characters from ever reaching the signaling channel, the pattern opens with a caret: ^. The caret is a regular-expression symbol that marks the very beginning of a one-line string. Similarly, a dollar sign $ marks the very end of the string. Without the dollar sign, the namespace 0011222-EVIL-BUSINESS-HERE! would still match, because it opens with seven digits. That kind of thing is probably why most people find themselves so confused and angered by regular expressions: they are incredibly, infuriatingly literal. Hey, you said you wanted seven digits–I found you seven digits! Ask regular expressions for a haircut, and they’ll cut exactly one of your hairs. They are the dad jokes of computer programming. Back to the pattern at hand: bookending the regular expression are slashes, /, which demarcate regular expressions in JavaScript, just like quotation marks or backticks demarcate strings. Put it all together, and this little creep Chapter 2. Working with a Signaling Channel • 30 report erratum • discuss
  • 51. should look a tad less imposing: /^[0-9]{7}$/. Seven digits in a row, and seven digits only. No more, no less—and nothing else. That’s the pattern for our namespace. Making Use of the Namespace And now back to the prepareNamespace() function definition itself: With the reg- ular expression in place, we call the test() method on it and pass along the namespace we have on hand, ns. If the test passes, we return the octothorp- free namespace, ns. But if the test fails because either the hash doesn’t match our pattern or there’s no hash at all, the function generates a new random hash in a complex one-liner: demos/basic-p2p/js/main.js ns = Math.random().toString().substring(2, 9); That generates a random number and converts it to a string. The substring() method gets called to remove the first two characters: numbers generated by Math.random() always begin with 0. The second argument to substring(), 9, ensures that we get a seven-digit number, thanks to the first two characters being discarded by the first argument, 2. If set_location is true, the new namespace gets set on the URL by assigning it to window.location.hash. Finally, the function returns the value of ns, which is the brand-new, randomly generated hash. With the prepareNamespace() function definition in place, you can put it to work at the top of your file and assign its output–the returned namespace–to a namespace variable: demos/basic-p2p/js/main.js const namespace = prepareNamespace(window.location.hash, true); Let’s do something user-facing with the namespace variable. In the user-interface area of the JavaScript file, add another one-liner that sets the h1 text to wel- come users to a correctly namespaced room: demos/basic-p2p/js/main.js /** * User-Interface Setup */ document.querySelector('#header h1') ➤ .innerText = 'Welcome to Room #' + namespace; ➤ document.querySelector('#call-button') .addEventListener('click', handleCallButton); report erratum • discuss Using a Lightweight Signaling Channel • 31
  • 52. Reload the page at https://localhost:3000/basic-p2p/. You should see two things: a random, seven-digit hash appended to the URL, something like https://local- host:3000/basic-p2p/#4134610, and that same hash repeated in the text of the page’s first-level heading, like this: Those adjustments are basically cosmetic, changing only the appearance of the URL and page. Now let’s use the namespace variable to connect to the sig- naling channel. Connecting to the Signaling Channel There are a few preliminary steps you’ll need to take to connect to the signaling channel. The first is to return to the index.html file and add another <script> tag right above the one that loads the main.js file. Point it to the JavaScript file /socket.io/socket.io.js that Socket.IO automatically serves: demos/basic-p2p/index.html <script src="/socket.io/socket.io.js"></script> ➤ <script src="js/main.js"></script> </body> </html> The main.js file depends on the contents of the socket.io.js file, which is the Socket.IO client, so be sure to include the <script> tag that points at main.js second in the HTML. Chapter 2. Working with a Signaling Channel • 32 report erratum • discuss
  • 53. Back in the main.js file itself, in the “Signaling-Channel Setup” area up top, let’s connect to the signaling channel and attach some code to the connect event that will output a message to the browser console if the connection is successful. You can declare an sc variable to hold onto the namespaced sig- naling channel as returned by Socket.IO’s io object’s connect method: /** * Signaling-Channel Setup */ const namespace = prepareNamespace(window.location.hash, true); const sc = io.connect('/' + namespace); ➤ ➤ sc.on('connect', function() { ➤ console.log('Successfully connected to the signaling channel!'); ➤ }); ➤ If your browser’s JavaScript console isn’t already open, open it. Reload the page and you should see the success message logged in the console: “Success- fully connected to the signaling channel!” Right now, anyone using your app will automatically connect to the signaling channel by loading the page in the browser. That’s not the behavior we want, though: the signaling channel’s connect event will eventually start the process of establishing the WebRTC call. To give users better control, then, let’s rewrite the code so users don’t connect to the signaling channel until they click the Join Call button that we went to all the trouble of setting up. Connecting to the Signaling Channel Manually Remember how you set up a handleCallButton() function in Writing Named Functions as Callbacks, on page 22? Let’s call two more functions from within it: joinCall() and leaveCall(). The definitions for those functions can go right below the handleCallButton function definition: demos/basic-p2p/js/main.js /** * User-Interface Functions and Callbacks */ function handleCallButton(event) { const call_button = event.target; if (call_button.className === 'join') { console.log('Joining the call...'); call_button.className = 'leave'; call_button.innerText = 'Leave Call'; joinCall(); ➤ } else { console.log('Leaving the call...'); report erratum • discuss Connecting to the Signaling Channel • 33
  • 54. call_button.className = 'join'; call_button.innerText = 'Join Call'; leaveCall(); ➤ } } function joinCall() { sc.open(); } function leaveCall() { sc.close(); } There will be more to add to both the joinCall and leaveCall functions. But to get started, they are responsible only for opening and closing the Socket.IO sig- naling channel, which provides its own open() and close() methods. We just need to call them on the sc object. Now that your JavaScript is set to manually open and close the connection to the signaling server, you need to modify how the signaling channel is con- figured by passing in an options object that sets autoConnect to false: demos/basic-p2p/js/main.js const sc = io.connect('/' + namespace, { autoConnect: false }); Reload the page in your browser again. You shouldn’t see the “Successfully connected to the signaling server!” in the JavaScript console until you click the Join Call button. If you click the button a few more times, joining and leaving the call, you’ll see the success message each time you click Join Call. Nice! Because the signaling channel is powered by Socket.IO, it has an active prop- erty that returns true when the signaling server is connected and false when it’s not. If you like, hit the Join Call button and then type sc.active in your browser’s JavaScript console and hit Return. It should report true. Hit the Leave Call button, type sc.active again, and it should report false. That’s an easy way to verify that your button really is opening and closing the signaling channel. Preparing Placeholders for the Remaining Signaling Callbacks One more task, and then we’ll have everything in place for writing the remaining signaling-channel code and WebRTC connection logic in the next chapter’s coverage of Building Connection Logic to the “Perfect Negotiation” Pat- tern, on page 51. Find the “Signaling-Channel Functions and Callbacks” area of the JavaScript file. Let’s write a wrapper function, registerScCallbacks() that Chapter 2. Working with a Signaling Channel • 34 report erratum • discuss
  • 55. Random documents with unrelated content Scribd suggests to you:
  • 56. [45] Espinosa, Antq. y Grand. de Seville, L. 2, c. 3. [46] Conde, P. 1, c. 14. [47] Conde, p. 1. Cronica del Moro Rasis.—Cron. gen. España, por Alonzo el Sabio, p. 3, c. 1. [48] Conde, pt. 1. c. 15. [49] Conde, pt. 1, c. 15. [50] Conde, pt. 1, c. 16. [51] Conde, pt. 1, c. 17. [52] Algarbe, or Algarbia, in Arabic signifies the west, as Axarkia is the east, Algufia the north, and Aquibla the south. This will serve to explain some of the geographical names on the peninsula which are of Arabian origin. [53] Faxardo, Corona Gothica, T. 1, p. 492.—Joan. Mar. de Reb. Hisp. L. 6, c. 27. [54] Conde, pt. 1, c. 17. [55] Chron. gen. de Alonzo el Sabio, p. 3. Joan Mar. de Reb. Hisp. lib. 6, c. 27. Conde, pt. 1, c. 19. [56] Abarca, Anales de Aragon. Ante regno, § 2. [57] El Moro Rasis, La Destruycion de España. Rojas, Hist. Toledo, pt. 2, L. 4, cl. [58] El Moro Rasis, Destruycion de España, pt. 2, c. 101. [59] Morales, Cronicon de España, L. 13, c. 2. [60] Judicio Domini actum est, ut ipsius montis pars se a fundamentis evolvens, sexaginta tria millia caldeorum stupenter in fulmina projecit, atque eos omnes opressit. Ubi usque nunc ipse fluvius dum tempore hyemali alveum suum implet, ripasque dissoluit, signa armorum et ossa eorum evidentissime ostendit.—Sebastianus Salmanticensis Episc. [61] La Destruycion de España, part 3. [62] Sandoval, p. 301.
  • 57. [63] It does not appear that Count Fernan Gonzalez kept his promise of founding a church and monastery on the site of the hermitage. The latter edifice remained to after ages. “It stands,” says Sandoval, “on a precipice overhanging the river Arlanza, insomuch that it inspires dread to look below. It is extremely ancient; large enough to hold a hundred persons. Within the chapel is an opening like a chasm, leading down to a cavern larger than the church, formed in the solid rock, with a small window which overlooks the river. It was here the Christians used to conceal themselves.” As a corroboration of the adventure of the Count of Castile, Sandoval assures us that in his day the oak still existed to which Don Fernan Gonzalez tied his horse, when he alighted to scramble up the hill in pursuit of the boar. The worthy Fray Agapida, however, needed no corroboration of the kind, swallowing the whole story with the ready credence of a pious monk. The action here recorded was known by the name of the battle of the Ford of Cascajares. Sandoval gives a different account of the fate of the hermits. He says that Almanzor, in a rage at their prognostics, overthrew their chapel, and, without alighting from his horse, ordered the three monks to be beheaded in his presence. “This martyrdom,” he adds, “is represented in an ancient painting of the chapel which still exists.” [64] Sandoval. The Five Bishops. Mariana, lib. 8, c. 5, p. 367. Cron. Gen. de España, part 3, c. 18, fol. 53. [65] Cron. Gen. de España, ut supra. [66] Cron. Gen. de España. [67] Mariana, lib. 8, c. 5, p. 367. [68] Sandoval, p. 313. [69] In the Cronica General de España, this imprisonment is said to have been by King Sancho the Fat; but the cautious Agapida goes according to his favorite Sandoval in attributing it to King Ramiro, and in so doing he is supported by the Chronicle of Bleda, L. 3, c. 19. [70] Exactly the same kind of miracle is recorded as happening in the same place to a cavalier of the name of Don Fernan Antolenez, in the service of the Count Garcia Fernandez. Fray Antonio Agapida has no doubt that the same miracle did actually happen to both cavaliers; “for in those days,” says he, “there was such a demand for miracles that the same had frequently to be repeated;” witness the repeated appearance of Santiago in precisely the same manner, to save Christian armies from imminent danger of defeat, and
  • 58. achieve wonderful victories over the infidels, as we find recorded throughout the Spanish chronicles. [71] Cronica de Alonzo el Sabio, pt. 3 c. 19. [72] Sandoval, p. 334. [73] Cronica Gotica, por Don Alonzo Nuñez de Castro, p. 17. [74] Cronica General de España, pt. 3, p. 370. [75] Cron. Gen. de España, pt. 4, fol. 373. [76] Cron. Gen. de España, pt. 4, c. ii. [77] The hiatus, here noted by the author, has evidently arisen from the loss of a leaf of his manuscript. The printed line which precedes the parenthesis concludes page 32 of the manuscript; the line which follows it begins page 34. The intermediate page is wanting. I presume the author did not become conscious of his loss until he had resorted to his manuscript for revision, and that he could not depend upon his memory to supply what was wanting without a fresh resort to authorities not at hand. Hence a postponement and ultimate omission. The missing leaf would scarce have filled half a page of print, and, it would seem from the context, must have related the invasion of Andalusia by Fernando and the ravages committed by his armies.—Ed. [78] Cron. Gen. de España, pt. 4. Bleda, lib. 4, c. 10. [79] Cronica del Rey Santo, cap. 13. [80] Notas para la Vida del Santo Rey, p. 554. [81] Some chronicles, through mistake, make it Pezuelo near Ciudal Real, in the mountains on the confines of Granada. [82] Conde, tom. iii. c. 5. [83] Notas para la Vida, etc., p. 562. [84] Notas para la Vida del Santo Rey, p. 572. [85] Rodriguez, Memorias del Santo Rey, c. lviii. [86] Cronica del Rey Don Fernando, c. XIII. [87] Zuniga, Annales de Sevilla, L. 1.
  • 59. [88] Jacob Paranes, Lib. de los Maestros de St. Iago. Corona Gothica, T. 3, § xiii. Zuniga, Annales de Sevilla. [89] Corona Gothica, T. 3, § viii. [90] Cronica Gotica, L. 3, § 13. Cronica General, pt. 4. Cronica de Santo Rey, c. 55. [91] Cronica General, pt. 4, p. 338. [92] Cronica General de España, pt. 4. Cronica del Rey Fernando el Santo, c. 60. Corona Gothica, T. 3, p. 126. [93] Cronica General, pt. 4, 341. [94] Cronica General, pt. 4. Corona Gothica, T. 3, § 16. [95] Cronica General, pt. 4. Cronica del Rey Santo. Corona Gothica, T. 3, § 16. [96] Cronica General, pt. 4, p. 424. [97] Mariana, L. 13, c. 7. [98] In Castile, whenever the kings entered any place where there was a synagogue, the Jews assembled in council and paid to the Monteros, or bull- fighters, twelve maravedis each, to guard them, that they should receive no harm from the the Christians; being held in such contempt and odium, that it was necessary they should be under the safeguard of the king, not to be injured or insulted.[A] [A] Zuniga, Annales de Sevilla. [99] Pablo de Espinosa, Grandesas de Sevilla, fol. 146. Cronica del Santo Rey, c. 78. Corona Gothica, T. 3, p. 166. [100] Argote de Molina, Nobleza de Andaluzia, L. 1, c. 21. Tomas Bocio, Signales de la Iglesia, L. 20. Don Rodrigo Sanchez, Bishop of Palencia, pt. 3, c. 40. [101] Pablo de Espinosa, fol. 146.
  • 60. Transcriber's note Original spelling was kept, but variant spellings were made consistent when a predominant usage was found. Obvious printer errors have been silently corrected. The following changes were also made: Page 29: “cheek” → “check” Page 31: “potents” → “portents” Page 459: “señoria” → “señorio” Page 516, note 100: “Argoti” → “Argote” Page 521: “pundoner” → “pundonor” Blank pages have been skipped. Footnotes have been renumbered and moved to the end of the book. The text of chapter headings and of Table of Contents entries have been made consistent. All chapters end with ornated illustrations, even when they were not present in the printed book.
  • 61. *** END OF THE PROJECT GUTENBERG EBOOK SPANISH PAPERS *** Updated editions will replace the previous one—the old editions will be renamed. Creating the works from print editions not protected by U.S. copyright law means that no one owns a United States copyright in these works, so the Foundation (and you!) can copy and distribute it in the United States without permission and without paying copyright royalties. Special rules, set forth in the General Terms of Use part of this license, apply to copying and distributing Project Gutenberg™ electronic works to protect the PROJECT GUTENBERG™ concept and trademark. Project Gutenberg is a registered trademark, and may not be used if you charge for an eBook, except by following the terms of the trademark license, including paying royalties for use of the Project Gutenberg trademark. If you do not charge anything for copies of this eBook, complying with the trademark license is very easy. You may use this eBook for nearly any purpose such as creation of derivative works, reports, performances and research. Project Gutenberg eBooks may be modified and printed and given away—you may do practically ANYTHING in the United States with eBooks not protected by U.S. copyright law. Redistribution is subject to the trademark license, especially commercial redistribution. START: FULL LICENSE
  • 62. THE FULL PROJECT GUTENBERG LICENSE
  • 63. PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK To protect the Project Gutenberg™ mission of promoting the free distribution of electronic works, by using or distributing this work (or any other work associated in any way with the phrase “Project Gutenberg”), you agree to comply with all the terms of the Full Project Gutenberg™ License available with this file or online at www.gutenberg.org/license. Section 1. General Terms of Use and Redistributing Project Gutenberg™ electronic works 1.A. By reading or using any part of this Project Gutenberg™ electronic work, you indicate that you have read, understand, agree to and accept all the terms of this license and intellectual property (trademark/copyright) agreement. If you do not agree to abide by all the terms of this agreement, you must cease using and return or destroy all copies of Project Gutenberg™ electronic works in your possession. If you paid a fee for obtaining a copy of or access to a Project Gutenberg™ electronic work and you do not agree to be bound by the terms of this agreement, you may obtain a refund from the person or entity to whom you paid the fee as set forth in paragraph 1.E.8. 1.B. “Project Gutenberg” is a registered trademark. It may only be used on or associated in any way with an electronic work by people who agree to be bound by the terms of this agreement. There are a few things that you can do with most Project Gutenberg™ electronic works even without complying with the full terms of this agreement. See paragraph 1.C below. There are a lot of things you can do with Project Gutenberg™ electronic works if you follow the terms of this agreement and help preserve free future access to Project Gutenberg™ electronic works. See paragraph 1.E below.
  • 64. 1.C. The Project Gutenberg Literary Archive Foundation (“the Foundation” or PGLAF), owns a compilation copyright in the collection of Project Gutenberg™ electronic works. Nearly all the individual works in the collection are in the public domain in the United States. If an individual work is unprotected by copyright law in the United States and you are located in the United States, we do not claim a right to prevent you from copying, distributing, performing, displaying or creating derivative works based on the work as long as all references to Project Gutenberg are removed. Of course, we hope that you will support the Project Gutenberg™ mission of promoting free access to electronic works by freely sharing Project Gutenberg™ works in compliance with the terms of this agreement for keeping the Project Gutenberg™ name associated with the work. You can easily comply with the terms of this agreement by keeping this work in the same format with its attached full Project Gutenberg™ License when you share it without charge with others. 1.D. The copyright laws of the place where you are located also govern what you can do with this work. Copyright laws in most countries are in a constant state of change. If you are outside the United States, check the laws of your country in addition to the terms of this agreement before downloading, copying, displaying, performing, distributing or creating derivative works based on this work or any other Project Gutenberg™ work. The Foundation makes no representations concerning the copyright status of any work in any country other than the United States. 1.E. Unless you have removed all references to Project Gutenberg: 1.E.1. The following sentence, with active links to, or other immediate access to, the full Project Gutenberg™ License must appear prominently whenever any copy of a Project Gutenberg™ work (any work on which the phrase “Project Gutenberg” appears, or with which the phrase “Project Gutenberg” is associated) is accessed, displayed, performed, viewed, copied or distributed:
  • 65. This eBook is for the use of anyone anywhere in the United States and most other parts of the world at no cost and with almost no restrictions whatsoever. You may copy it, give it away or re-use it under the terms of the Project Gutenberg License included with this eBook or online at www.gutenberg.org. If you are not located in the United States, you will have to check the laws of the country where you are located before using this eBook. 1.E.2. If an individual Project Gutenberg™ electronic work is derived from texts not protected by U.S. copyright law (does not contain a notice indicating that it is posted with permission of the copyright holder), the work can be copied and distributed to anyone in the United States without paying any fees or charges. If you are redistributing or providing access to a work with the phrase “Project Gutenberg” associated with or appearing on the work, you must comply either with the requirements of paragraphs 1.E.1 through 1.E.7 or obtain permission for the use of the work and the Project Gutenberg™ trademark as set forth in paragraphs 1.E.8 or 1.E.9. 1.E.3. If an individual Project Gutenberg™ electronic work is posted with the permission of the copyright holder, your use and distribution must comply with both paragraphs 1.E.1 through 1.E.7 and any additional terms imposed by the copyright holder. Additional terms will be linked to the Project Gutenberg™ License for all works posted with the permission of the copyright holder found at the beginning of this work. 1.E.4. Do not unlink or detach or remove the full Project Gutenberg™ License terms from this work, or any files containing a part of this work or any other work associated with Project Gutenberg™. 1.E.5. Do not copy, display, perform, distribute or redistribute this electronic work, or any part of this electronic work, without prominently displaying the sentence set forth in paragraph 1.E.1
  • 66. with active links or immediate access to the full terms of the Project Gutenberg™ License. 1.E.6. You may convert to and distribute this work in any binary, compressed, marked up, nonproprietary or proprietary form, including any word processing or hypertext form. However, if you provide access to or distribute copies of a Project Gutenberg™ work in a format other than “Plain Vanilla ASCII” or other format used in the official version posted on the official Project Gutenberg™ website (www.gutenberg.org), you must, at no additional cost, fee or expense to the user, provide a copy, a means of exporting a copy, or a means of obtaining a copy upon request, of the work in its original “Plain Vanilla ASCII” or other form. Any alternate format must include the full Project Gutenberg™ License as specified in paragraph 1.E.1. 1.E.7. Do not charge a fee for access to, viewing, displaying, performing, copying or distributing any Project Gutenberg™ works unless you comply with paragraph 1.E.8 or 1.E.9. 1.E.8. You may charge a reasonable fee for copies of or providing access to or distributing Project Gutenberg™ electronic works provided that: • You pay a royalty fee of 20% of the gross profits you derive from the use of Project Gutenberg™ works calculated using the method you already use to calculate your applicable taxes. The fee is owed to the owner of the Project Gutenberg™ trademark, but he has agreed to donate royalties under this paragraph to the Project Gutenberg Literary Archive Foundation. Royalty payments must be paid within 60 days following each date on which you prepare (or are legally required to prepare) your periodic tax returns. Royalty payments should be clearly marked as such and sent to the Project Gutenberg Literary Archive Foundation at the address specified in Section 4, “Information
  • 67. about donations to the Project Gutenberg Literary Archive Foundation.” • You provide a full refund of any money paid by a user who notifies you in writing (or by e-mail) within 30 days of receipt that s/he does not agree to the terms of the full Project Gutenberg™ License. You must require such a user to return or destroy all copies of the works possessed in a physical medium and discontinue all use of and all access to other copies of Project Gutenberg™ works. • You provide, in accordance with paragraph 1.F.3, a full refund of any money paid for a work or a replacement copy, if a defect in the electronic work is discovered and reported to you within 90 days of receipt of the work. • You comply with all other terms of this agreement for free distribution of Project Gutenberg™ works. 1.E.9. If you wish to charge a fee or distribute a Project Gutenberg™ electronic work or group of works on different terms than are set forth in this agreement, you must obtain permission in writing from the Project Gutenberg Literary Archive Foundation, the manager of the Project Gutenberg™ trademark. Contact the Foundation as set forth in Section 3 below. 1.F. 1.F.1. Project Gutenberg volunteers and employees expend considerable effort to identify, do copyright research on, transcribe and proofread works not protected by U.S. copyright law in creating the Project Gutenberg™ collection. Despite these efforts, Project Gutenberg™ electronic works, and the medium on which they may be stored, may contain “Defects,” such as, but not limited to, incomplete, inaccurate or corrupt data, transcription errors, a copyright or other intellectual property infringement, a defective or
  • 68. damaged disk or other medium, a computer virus, or computer codes that damage or cannot be read by your equipment. 1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the “Right of Replacement or Refund” described in paragraph 1.F.3, the Project Gutenberg Literary Archive Foundation, the owner of the Project Gutenberg™ trademark, and any other party distributing a Project Gutenberg™ electronic work under this agreement, disclaim all liability to you for damages, costs and expenses, including legal fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE PROVIDED IN PARAGRAPH 1.F.3. YOU AGREE THAT THE FOUNDATION, THE TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH DAMAGE. 1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a defect in this electronic work within 90 days of receiving it, you can receive a refund of the money (if any) you paid for it by sending a written explanation to the person you received the work from. If you received the work on a physical medium, you must return the medium with your written explanation. The person or entity that provided you with the defective work may elect to provide a replacement copy in lieu of a refund. If you received the work electronically, the person or entity providing it to you may choose to give you a second opportunity to receive the work electronically in lieu of a refund. If the second copy is also defective, you may demand a refund in writing without further opportunities to fix the problem. 1.F.4. Except for the limited right of replacement or refund set forth in paragraph 1.F.3, this work is provided to you ‘AS-IS’, WITH NO OTHER WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
  • 69. INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PURPOSE. 1.F.5. Some states do not allow disclaimers of certain implied warranties or the exclusion or limitation of certain types of damages. If any disclaimer or limitation set forth in this agreement violates the law of the state applicable to this agreement, the agreement shall be interpreted to make the maximum disclaimer or limitation permitted by the applicable state law. The invalidity or unenforceability of any provision of this agreement shall not void the remaining provisions. 1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the trademark owner, any agent or employee of the Foundation, anyone providing copies of Project Gutenberg™ electronic works in accordance with this agreement, and any volunteers associated with the production, promotion and distribution of Project Gutenberg™ electronic works, harmless from all liability, costs and expenses, including legal fees, that arise directly or indirectly from any of the following which you do or cause to occur: (a) distribution of this or any Project Gutenberg™ work, (b) alteration, modification, or additions or deletions to any Project Gutenberg™ work, and (c) any Defect you cause. Section 2. Information about the Mission of Project Gutenberg™ Project Gutenberg™ is synonymous with the free distribution of electronic works in formats readable by the widest variety of computers including obsolete, old, middle-aged and new computers. It exists because of the efforts of hundreds of volunteers and donations from people in all walks of life. Volunteers and financial support to provide volunteers with the assistance they need are critical to reaching Project Gutenberg™’s goals and ensuring that the Project Gutenberg™ collection will
  • 70. remain freely available for generations to come. In 2001, the Project Gutenberg Literary Archive Foundation was created to provide a secure and permanent future for Project Gutenberg™ and future generations. To learn more about the Project Gutenberg Literary Archive Foundation and how your efforts and donations can help, see Sections 3 and 4 and the Foundation information page at www.gutenberg.org. Section 3. Information about the Project Gutenberg Literary Archive Foundation The Project Gutenberg Literary Archive Foundation is a non-profit 501(c)(3) educational corporation organized under the laws of the state of Mississippi and granted tax exempt status by the Internal Revenue Service. The Foundation’s EIN or federal tax identification number is 64-6221541. Contributions to the Project Gutenberg Literary Archive Foundation are tax deductible to the full extent permitted by U.S. federal laws and your state’s laws. The Foundation’s business office is located at 809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887. Email contact links and up to date contact information can be found at the Foundation’s website and official page at www.gutenberg.org/contact Section 4. Information about Donations to the Project Gutenberg Literary Archive Foundation Project Gutenberg™ depends upon and cannot survive without widespread public support and donations to carry out its mission of increasing the number of public domain and licensed works that can be freely distributed in machine-readable form accessible by the widest array of equipment including outdated equipment. Many
  • 71. small donations ($1 to $5,000) are particularly important to maintaining tax exempt status with the IRS. The Foundation is committed to complying with the laws regulating charities and charitable donations in all 50 states of the United States. Compliance requirements are not uniform and it takes a considerable effort, much paperwork and many fees to meet and keep up with these requirements. We do not solicit donations in locations where we have not received written confirmation of compliance. To SEND DONATIONS or determine the status of compliance for any particular state visit www.gutenberg.org/donate. While we cannot and do not solicit contributions from states where we have not met the solicitation requirements, we know of no prohibition against accepting unsolicited donations from donors in such states who approach us with offers to donate. International donations are gratefully accepted, but we cannot make any statements concerning tax treatment of donations received from outside the United States. U.S. laws alone swamp our small staff. Please check the Project Gutenberg web pages for current donation methods and addresses. Donations are accepted in a number of other ways including checks, online payments and credit card donations. To donate, please visit: www.gutenberg.org/donate. Section 5. General Information About Project Gutenberg™ electronic works Professor Michael S. Hart was the originator of the Project Gutenberg™ concept of a library of electronic works that could be freely shared with anyone. For forty years, he produced and distributed Project Gutenberg™ eBooks with only a loose network of volunteer support.
  • 72. Project Gutenberg™ eBooks are often created from several printed editions, all of which are confirmed as not protected by copyright in the U.S. unless a copyright notice is included. Thus, we do not necessarily keep eBooks in compliance with any particular paper edition. Most people start at our website which has the main PG search facility: www.gutenberg.org. This website includes information about Project Gutenberg™, including how to make donations to the Project Gutenberg Literary Archive Foundation, how to help produce our new eBooks, and how to subscribe to our email newsletter to hear about new eBooks.
  • 73. Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com