<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jirka Hladis, Author at DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/author/jirkah/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Thu, 20 Aug 2026 21:13:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>Jirka Hladis, Author at DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Multi User Video Chat With WebRTC</title>
		<link>https://static.dmcinfo.com/blog/21795/multi-user-video-chat-with-webrtc/</link>
		
		<dc:creator><![CDATA[Jirka Hladis]]></dc:creator>
		<pubDate>Fri, 29 Mar 2019 15:04:41 +0000</pubDate>
				<category><![CDATA[Culture]]></category>
		<category><![CDATA[Web Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/21795/multi-user-video-chat-with-webrtc-2/</guid>

					<description><![CDATA[<p>At DMC, we like to keep in touch with colleagues across all of our offices. So, for a while now, we’ve had a video “portal” set up in the kitchen area of each office. We were initially using an off the shelf video conferencing service, but it was cumbersome to use and needed a manual [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/21795/multi-user-video-chat-with-webrtc/">Multi User Video Chat With WebRTC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">At DMC, we like to keep in touch with colleagues across all of our offices. So, for a while now, we’ve had a video “portal” set up in the kitchen area of each office. We were initially using an off the shelf video conferencing service, but it was cumbersome to use and needed a manual restart every few days. I thought it would be fun to see if we could build a replacement ourselves.</p>



<p class="wp-block-paragraph">I first considered setting up a dedicated IP camera in each office. But then I thought, hold on, it’s 2019, I bet there is a web-based solution out there for streaming video feeds!</p>



<p class="wp-block-paragraph">As it turns out, that solution is called <a href="https://webrtc.org/" target="_blank">WebRTC</a>, and it is the web standard for real-time peer-to-peer communication. WebRTC is an asynchronous peer-to-peer architecture, and the only need for a centralized server is for exchanging the connection set up information.</p>



<h2 class="wp-block-heading" id="h-getting-started-in-webrtc">Getting Started in WebRTC</h2>



<p class="wp-block-paragraph">Most of the available tutorials on WebRTC only show a trivial implementation of two client objects in the same browser window. After some searching, I found a <a href="https://shanetully.com/2014/09/a-dead-simple-webrtc-example/">great example</a>&nbsp;of a basic implementation of 1-to-1 video using WebRTC and a simple Node.js server to relay messages over Websockets.</p>



<p class="wp-block-paragraph">Using this example as a baseline, all I had to do is extend it to support an arbitrary number of participants. Additionally, I decided to add some CSS styling to arrange the video containers on the screen and labels to identify each participant.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/office_portal2-1.jpg" alt=""/></figure>



<p class="wp-block-paragraph">To try it out for&nbsp;yourself, install node.js if you haven&#8217;t already, <a href="https://static.dmcinfo.com/wp-content/uploads/2025/06/WebRTC.zip" target="_blank" rel="noreferrer noopener">download the full source code here</a>, unzip the file, and start the node server locally:</p>


<pre style="font-family: monospace; background-color: #333; color: #f0f2f1;"> npm install
 npm start
</pre>


<p class="wp-block-paragraph">With the server running, go to https://static.dmcinfo.com/ in Chrome. You’ll need to click through the invalid SSL certificate warning, enter a display name, and allow the page to use your camera. After that, you&#8217;ll be able to open multiple windows and see yourself multiple times. Likewise, you can enter the IP address instead of localhost, and you&#8217;ll be able to establish connections between any computers on your local network.</p>



<h2 class="wp-block-heading" id="h-setting-up-the-video-chat">Setting Up the Video Chat</h2>



<p class="wp-block-paragraph">The core functionality is all in the client-side javascript, which handles establishing and managing the connections to all the other participants. When the page is loaded, we generate a random identifier and capture a user-entered display name.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>function start() {
  localUuid = createUUID();

  // check if "&amp;displayName=xxx" is appended to URL, otherwise alert user to populate
  var urlParams = new URLSearchParams(window.location.search);
  localDisplayName = urlParams.get('displayName') || prompt('Enter your name', '');
  document.getElementById('localVideoContainer').appendChild(makeLabel(localDisplayName));</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">start</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">createUUID</span><span style="color: #D4D4D4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// check if &quot;&amp;displayName=xxx&quot; is appended to URL, otherwise alert user to populate</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">urlParams</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">URLSearchParams</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">window</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">location</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">search</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">localDisplayName</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">urlParams</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">get</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;displayName&apos;</span><span style="color: #D4D4D4">) || </span><span style="color: #DCDCAA">prompt</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Enter your name&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementById</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;localVideoContainer&apos;</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">appendChild</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">makeLabel</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">localDisplayName</span><span style="color: #D4D4D4">));</span></span></code></pre></div>



<p class="wp-block-paragraph">Next, we can specify the video and audio parameters (in this implementation, audio is disabled) and grab the local video stream.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// specify no audio for user media
  var constraints = {
    video: {
      width: {max: 320},
      height: {max: 240},
      frameRate: {max: 30},
    },
    audio: false,
  };</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// specify no audio for user media</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">constraints</span><span style="color: #D4D4D4"> = {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">video:</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">width:</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">max:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">320</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">height:</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">max:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">240</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">frameRate:</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">max:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">30</span><span style="color: #D4D4D4">},</span></span>
<span class="line"><span style="color: #D4D4D4">    },</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">audio:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  };</span></span></code></pre></div>



<p class="wp-block-paragraph">Once we successfully have the local video stream, we can set up the connection to the WebSockets server and set up callback functions. When the connection first opens, we send out a message to anyone who might already be connected, informing them that we joined the party.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// set up local video stream
  if (navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia(constraints)
      .then(stream => {
        localStream = stream;
        document.getElementById('localVideo').srcObject = stream;
      }).catch(errorHandler)

      // set up websocket and message all existing clients
      .then(() => {
        serverConnection = new WebSocket('wss://' + window.location.hostname + ':' + WS_PORT);
        serverConnection.onmessage = gotMessageFromServer;
        serverConnection.onopen = event => {
          serverConnection.send(JSON.stringify({ 'displayName': localDisplayName, 'uuid': localUuid, 'dest': 'all' }));
        }
      }).catch(errorHandler);

  } else {
    alert('Your browser does not support getUserMedia API');
  }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// set up local video stream</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">navigator</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">mediaDevices</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">getUserMedia</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">navigator</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">mediaDevices</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getUserMedia</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">constraints</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">      .</span><span style="color: #DCDCAA">then</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">stream</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">localStream</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">stream</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementById</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;localVideo&apos;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">srcObject</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">stream</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">      }).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">// set up websocket and message all existing clients</span></span>
<span class="line"><span style="color: #D4D4D4">      .</span><span style="color: #DCDCAA">then</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">serverConnection</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">WebSocket</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;wss://&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">window</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">location</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">hostname</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;:&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #4FC1FF">WS_PORT</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">serverConnection</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">onmessage</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">gotMessageFromServer</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">serverConnection</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">onopen</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">serverConnection</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">send</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">JSON</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">stringify</span><span style="color: #D4D4D4">({ </span><span style="color: #CE9178">&apos;displayName&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">localDisplayName</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;uuid&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;dest&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;all&apos;</span><span style="color: #D4D4D4"> }));</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">      }).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">alert</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Your browser does not support getUserMedia API&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">Whenever a new message is received, we check if it’s intended for us and whether it’s for setting a new peer or initiating the connection. SDP stands for Session Description Protocol.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>function gotMessageFromServer(message) {
  var signal = JSON.parse(message.data);
  var peerUuid = signal.uuid;

  // Ignore messages that are not for us or from ourselves
  if (peerUuid == localUuid || (signal.dest != localUuid &amp;&amp; signal.dest != 'all')) return;

  if (signal.displayName &amp;&amp; signal.dest == 'all') {
    // set up peer connection object for a newcomer peer
    setUpPeer(peerUuid, signal.displayName);
    serverConnection.send(JSON.stringify({ 'displayName': localDisplayName, 'uuid': localUuid, 'dest': peerUuid }));

  } else if (signal.displayName &amp;&amp; signal.dest == localUuid) {
    // initiate call if we are the newcomer peer
    setUpPeer(peerUuid, signal.displayName, true);

  } else if (signal.sdp) {
    peerConnections&#91;peerUuid&#93;.pc.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function () {
      // Only create answers in response to offers
      if (signal.sdp.type == 'offer') {
        peerConnections&#91;peerUuid&#93;.pc.createAnswer().then(description => createdDescription(description, peerUuid)).catch(errorHandler);
      }
    }).catch(errorHandler);

  } else if (signal.ice) {
    peerConnections&#91;peerUuid&#93;.pc.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
  }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">gotMessageFromServer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4"> = </span><span style="color: #4FC1FF">JSON</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">parse</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">uuid</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// Ignore messages that are not for us or from ourselves</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4"> || (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">dest</span><span style="color: #D4D4D4"> != </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">dest</span><span style="color: #D4D4D4"> != </span><span style="color: #CE9178">&apos;all&apos;</span><span style="color: #D4D4D4">)) </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">dest</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&apos;all&apos;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// set up peer connection object for a newcomer peer</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">setUpPeer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">serverConnection</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">send</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">JSON</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">stringify</span><span style="color: #D4D4D4">({ </span><span style="color: #CE9178">&apos;displayName&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">localDisplayName</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;uuid&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;dest&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4"> }));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">dest</span><span style="color: #D4D4D4"> == </span><span style="color: #9CDCFE">localUuid</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// initiate call if we are the newcomer peer</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">setUpPeer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sdp</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setRemoteDescription</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">RTCSessionDescription</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sdp</span><span style="color: #D4D4D4">)).</span><span style="color: #DCDCAA">then</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> () {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #6A9955">// Only create answers in response to offers</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">sdp</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&apos;offer&apos;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createAnswer</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">then</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">description</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">createdDescription</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">description</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">)).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      }</span></span>
<span class="line"><span style="color: #D4D4D4">    }).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ice</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">addIceCandidate</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">RTCIceCandidate</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">signal</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ice</span><span style="color: #D4D4D4">)).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">Once we have a new peer, we can add them to the peerConnections object, with the UUID as a key.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>function setUpPeer(peerUuid, displayName, initCall = false) {
  peerConnections&#91;peerUuid&#93; = { 'displayName': displayName, 'pc': new RTCPeerConnection(peerConnectionConfig) };
  peerConnections&#91;peerUuid&#93;.pc.onicecandidate = event => gotIceCandidate(event, peerUuid);
  peerConnections&#91;peerUuid&#93;.pc.ontrack = event => gotRemoteStream(event, peerUuid);
  peerConnections&#91;peerUuid&#93;.pc.oniceconnectionstatechange = event => checkPeerDisconnect(event, peerUuid);
  peerConnections&#91;peerUuid&#93;.pc.addStream(localStream);

  if (initCall) {
    peerConnections&#91;peerUuid&#93;.pc.createOffer().then(description => createdDescription(description, peerUuid)).catch(errorHandler);
  }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">setUpPeer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">initCall</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93; = { </span><span style="color: #CE9178">&apos;displayName&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;pc&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">RTCPeerConnection</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerConnectionConfig</span><span style="color: #D4D4D4">) };</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">onicecandidate</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">gotIceCandidate</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">ontrack</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">gotRemoteStream</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">oniceconnectionstatechange</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">checkPeerDisconnect</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">addStream</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">localStream</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">initCall</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createOffer</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">then</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">description</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">createdDescription</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">description</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">)).</span><span style="color: #DCDCAA">catch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorHandler</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">The rest is just handling the nitty-gritty details of WebRTC, which involves several messages back and forth before you can get the remote video stream. Once you have it, you can manipulate the DOM to add it to the page.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>function gotRemoteStream(event, peerUuid) {
  console.log(`got remote stream, peer ${peerUuid}`);
  //assign stream to new HTML video element
  var vidElement = document.createElement('video');
  vidElement.setAttribute('autoplay', '');
  vidElement.setAttribute('muted', '');
  vidElement.srcObject = event.streams&#91;0&#93;;

  var vidContainer = document.createElement('div');
  vidContainer.setAttribute('id', 'remoteVideo_' + peerUuid);
  vidContainer.setAttribute('class', 'videoContainer');
  vidContainer.appendChild(vidElement);
  vidContainer.appendChild(makeLabel(peerConnections&#91;peerUuid&#93;.displayName));

  document.getElementById('videos').appendChild(vidContainer);

  updateLayout();
}


function updateLayout() {
  // update CSS grid based on number of diplayed videos
  var rowHeight = '98vh';
  var colWidth = '98vw';

  var numVideos = Object.keys(peerConnections).length + 1; // add one to include local video

  if (numVideos > 1 &amp;&amp; numVideos &lt;= 4) { // 2x2 grid
    rowHeight = '48vh';
    colWidth = '48vw';
  } else if (numVideos > 4) { // 3x3 grid
    rowHeight = '32vh';
    colWidth = '32vw';
  }

  document.documentElement.style.setProperty(`--rowHeight`, rowHeight);
  document.documentElement.style.setProperty(`--colWidth`, colWidth);
}

function makeLabel(label) {
  var vidLabel = document.createElement('div');
  vidLabel.appendChild(document.createTextNode(label));
  vidLabel.setAttribute('class', 'videoLabel');
  return vidLabel;
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">gotRemoteStream</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">console</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">log</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">`got remote stream, peer </span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #569CD6">}</span><span style="color: #CE9178">`</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">//assign stream to new HTML video element</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">vidElement</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createElement</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;video&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidElement</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setAttribute</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;autoplay&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidElement</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setAttribute</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;muted&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidElement</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">srcObject</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">streams</span><span style="color: #D4D4D4">&#91;</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createElement</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;div&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setAttribute</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;id&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;remoteVideo_&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setAttribute</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;class&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;videoContainer&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">appendChild</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">vidElement</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">appendChild</span><span style="color: #D4D4D4">(</span><span style="color: #DCDCAA">makeLabel</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">displayName</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementById</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;videos&apos;</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">appendChild</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">vidContainer</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">updateLayout</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">updateLayout</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// update CSS grid based on number of diplayed videos</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">rowHeight</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;98vh&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">colWidth</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;98vw&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">numVideos</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Object</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">keys</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">length</span><span style="color: #D4D4D4"> + </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">; </span><span style="color: #6A9955">// add one to include local video</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">numVideos</span><span style="color: #D4D4D4"> &gt; </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4"> &amp;&amp; </span><span style="color: #9CDCFE">numVideos</span><span style="color: #D4D4D4"> &lt;= </span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">) { </span><span style="color: #6A9955">// 2x2 grid</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">rowHeight</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;48vh&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">colWidth</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;48vw&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">numVideos</span><span style="color: #D4D4D4"> &gt; </span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">) { </span><span style="color: #6A9955">// 3x3 grid</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">rowHeight</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;32vh&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">colWidth</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&apos;32vw&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">documentElement</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">style</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setProperty</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">`--rowHeight`</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">rowHeight</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">documentElement</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">style</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setProperty</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">`--colWidth`</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">colWidth</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">makeLabel</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">vidLabel</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createElement</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;div&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidLabel</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">appendChild</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createTextNode</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">vidLabel</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">setAttribute</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;class&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;videoLabel&apos;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">vidLabel</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">On the HTML side, all we need is a reference to the javascript and a container div for the local video. The remote video streams get appended to the same div by manipulating the DOM from the client-side javascript. Some CSS also takes care of arranging the videos in a grid based on the number of connected peers and displaying their name in the corner.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;html>

&lt;head>
  &lt;!-- prevent extraneous favicon requests -->
  &lt;link rel="icon" href="data:,">

  &lt;link rel="stylesheet" href="style.css">

  &lt;script src="https://webrtc.github.io/adapter/adapter-latest.js">&lt;/script>
  &lt;script src="webrtc.js">&lt;/script>
&lt;/head>

&lt;body>

  &lt;div id="videos" class="videos">

    &lt;div id="localVideoContainer" class="videoContainer">
      &lt;video id="localVideo" autoplay muted>&lt;/video>
    &lt;/div>

    &lt;!-- Remote videos get appended here -->

  &lt;/div>

  &lt;script type="text/javascript">
    start();
  &lt;/script>

&lt;/body>

&lt;/html></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">html</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">head</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">&lt;!-- prevent extraneous favicon requests --&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">link</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">rel</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;icon&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;data:,&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">link</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">rel</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;stylesheet&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">href</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;style.css&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">script</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">src</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;https://webrtc.github.io/adapter/adapter-latest.js&quot;</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">script</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">script</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">src</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;webrtc.js&quot;</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">script</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">head</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">body</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">id</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;videos&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;videos&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">div</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">id</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;localVideoContainer&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;videoContainer&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">video</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">id</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;localVideo&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">autoplay</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">muted</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">video</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">&lt;!-- Remote videos get appended here --&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">script</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;text/javascript&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">start</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">script</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">body</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">html</span><span style="color: #808080">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">We can also take care of peer connections that have dropped off by removing the video element from the DOM.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>function checkPeerDisconnect(event, peerUuid) {
  var state = peerConnections&#91;peerUuid&#93;.pc.iceConnectionState;
  console.log(`connection with peer ${peerUuid} ${state}`);
  if (state === "failed" || state === "closed" || state === "disconnected") {
    delete peerConnections&#91;peerUuid&#93;;
    document.getElementById('videos').removeChild(document.getElementById('remoteVideo_' + peerUuid));
    updateLayout();
  }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">checkPeerDisconnect</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;.</span><span style="color: #9CDCFE">pc</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">iceConnectionState</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">console</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">log</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">`connection with peer </span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #569CD6">}</span><span style="color: #CE9178"> </span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">state</span><span style="color: #569CD6">}</span><span style="color: #CE9178">`</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4"> === </span><span style="color: #CE9178">&quot;failed&quot;</span><span style="color: #D4D4D4"> || </span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4"> === </span><span style="color: #CE9178">&quot;closed&quot;</span><span style="color: #D4D4D4"> || </span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4"> === </span><span style="color: #CE9178">&quot;disconnected&quot;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">delete</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">peerConnections</span><span style="color: #D4D4D4">&#91;</span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementById</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;videos&apos;</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">removeChild</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">document</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getElementById</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;remoteVideo_&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">peerUuid</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">updateLayout</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">For the node.js server, all we need to do is serve the client-side files, and then set up a WebSocket server that re-broadcasts any messages that it receives.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// Create a server for the client html page
const handleRequest = function (request, response) {
  // Render the single client html file for any request the HTTP server receives
  console.log('request received: ' + request.url);

 if (request.url === '/webrtc.js') {
    response.writeHead(200, { 'Content-Type': 'application/javascript' });
    response.end(fs.readFileSync('client/webrtc.js'));
  } else if (request.url === '/style.css') {
    response.writeHead(200, { 'Content-Type': 'text/css' });
    response.end(fs.readFileSync('client/style.css'));
  } else {
    response.writeHead(200, { 'Content-Type': 'text/html' });
    response.end(fs.readFileSync('client/index.html'));
  }
};

const httpsServer = https.createServer(serverConfig, handleRequest);
httpsServer.listen(HTTPS_PORT);

// ----------------------------------------------------------------------------------------

// Create a server for handling websocket calls
const wss = new WebSocketServer({ server: httpsServer });

wss.on('connection', function (ws) {
  ws.on('message', function (message) {
    // Broadcast any received message to all clients
    console.log('received: %s', message);
    wss.broadcast(message);
  });

  ws.on('error', () => ws.terminate());
});

wss.broadcast = function (data) {
  this.clients.forEach(function (client) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  });
};</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Create a server for the client html page</span></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">handleRequest</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// Render the single client html file for any request the HTTP server receives</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">console</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">log</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;request received: &apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">url</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">url</span><span style="color: #D4D4D4"> === </span><span style="color: #CE9178">&apos;/webrtc.js&apos;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">writeHead</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, { </span><span style="color: #CE9178">&apos;Content-Type&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;application/javascript&apos;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">end</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">fs</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">readFileSync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;client/webrtc.js&apos;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">url</span><span style="color: #D4D4D4"> === </span><span style="color: #CE9178">&apos;/style.css&apos;</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">writeHead</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, { </span><span style="color: #CE9178">&apos;Content-Type&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;text/css&apos;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">end</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">fs</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">readFileSync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;client/style.css&apos;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">  } </span><span style="color: #C586C0">else</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">writeHead</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">200</span><span style="color: #D4D4D4">, { </span><span style="color: #CE9178">&apos;Content-Type&apos;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;text/html&apos;</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">end</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">fs</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">readFileSync</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;client/index.html&apos;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">httpsServer</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">https</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">createServer</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">serverConfig</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">handleRequest</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #9CDCFE">httpsServer</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">listen</span><span style="color: #D4D4D4">(</span><span style="color: #4FC1FF">HTTPS_PORT</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// ----------------------------------------------------------------------------------------</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Create a server for handling websocket calls</span></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">wss</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">WebSocketServer</span><span style="color: #D4D4D4">({ </span><span style="color: #9CDCFE">server:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">httpsServer</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">wss</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">on</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;connection&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">ws</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">ws</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">on</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;message&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Broadcast any received message to all clients</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">console</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">log</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;received: %s&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">wss</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">broadcast</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">ws</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">on</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;error&apos;</span><span style="color: #D4D4D4">, () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ws</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">terminate</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span>
<span class="line"><span style="color: #9CDCFE">wss</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">broadcast</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">clients</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">forEach</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">client</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">client</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">readyState</span><span style="color: #D4D4D4"> === </span><span style="color: #9CDCFE">WebSocket</span><span style="color: #D4D4D4">.</span><span style="color: #4FC1FF">OPEN</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">client</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">send</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">  });</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span></code></pre></div>



<p class="wp-block-paragraph">WebRTC requires TLS, so it does need a certificate, and you can optionally add a redirect from HTTP to HTTPS. The server can be run locally or easily deployed as a Windows service using <a href="https://github.com/coreybutler/node-windows">node-windows</a>.&nbsp;</p>



<h2 class="wp-block-heading" id="h-caveats">Caveats</h2>



<p class="wp-block-paragraph">The main downside with this implementation is that it creates a mesh network, with each participant sending a separately encoded video stream to every single other participant. If you add more than a handful of participants, you’ll notice that you&#8217;ll start maxing out your CPU and performance will degrade. For larger groups, you&#8217;ll want to look into using a centralized&nbsp;server to forward each client&#8217;s&nbsp;video feed to all its peers. <a href="https://antmedia.io/webrtc-servers/">This article</a> does a nice job of laying out the options.</p>
<p>The post <a href="https://static.dmcinfo.com/blog/21795/multi-user-video-chat-with-webrtc/">Multi User Video Chat With WebRTC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To Disassemble and Fix a Fried S7-1200 PLC</title>
		<link>https://static.dmcinfo.com/blog/22198/how-to-disassemble-and-fix-a-fried-s7-1200-plc/</link>
		
		<dc:creator><![CDATA[Jirka Hladis]]></dc:creator>
		<pubDate>Mon, 21 Jan 2019 09:52:33 +0000</pubDate>
				<category><![CDATA[Circuit Design]]></category>
		<category><![CDATA[Culture]]></category>
		<category><![CDATA[Embedded Development & Programming]]></category>
		<category><![CDATA[PLC]]></category>
		<category><![CDATA[Siemens PLC]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/22198/how-to-disassemble-and-fix-a-fried-s7-1200-plc/</guid>

					<description><![CDATA[<p>Don’t you hate it when your coworker inadvertently wires up a 24V PLC to wall power and lets out the magic smoke? Just such a&#160;mishap happened in our lab a while back and the broken PLC was sitting around in the junk bin for a while. Figuring there wasn&#8217;t anything to lose, I decided to [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/22198/how-to-disassemble-and-fix-a-fried-s7-1200-plc/">How To Disassemble and Fix a Fried S7-1200 PLC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Don’t you hate it when your coworker inadvertently wires up a 24V PLC to wall power and lets out the magic smoke? Just such a&nbsp;mishap happened in our lab a while back and the broken PLC was sitting around in the junk bin for a while. Figuring there wasn&#8217;t anything to lose,<strong> I decided to try my luck and see if it could be revived</strong>.&nbsp;</p>



<p class="wp-block-paragraph">Fortunately, the Siemens S7-1200 series PLCs are quite easy to disassemble, and&nbsp;I was able to replace the damaged components and restore it to a proper working condition.&nbsp;I definitely wouldn&#8217;t trust this refurbished PLC to run a critical system, but for testing code in our lab, it should be just fine. <strong>I would also recommend reaching out to the manufacturer if you are running into issues with a production&nbsp;PLC, keeping in mind that any warranty may be voided if you attempt to fix it yourself.</strong></p>


<hr />


<h2 id="h-opening-nbsp-the-enclosure" class="wp-block-heading">Opening&nbsp;the Enclosure</h2>



<p class="wp-block-paragraph">Using a small flat blade screwdriver to push in the side tabs, it&#8217;s easy to pull the enclosure apart and <strong>separate the power conditioning PCB from the main board</strong>.</p>


<div><iframe width="100%" height="520" src="https://www.youtube.com/embed/videoseries?list=PLWKqK_J0HJAWrpFIsrnD5tLvYvx8Nad3p&amp;autoplay=1&amp;loop=1&amp;mute=1" title="Video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>


<p class="wp-block-paragraph">&nbsp;</p>



<h2 id="h-surveying-the-damage" class="wp-block-heading">Surveying The Damage</h2>



<p class="wp-block-paragraph">I immediately noticed the <strong>burn marks </strong>coming from the positive voltage supply pin, but everything else looked okay, and there was no visible damage on the main board.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/S71200fuse.jpg" alt="PCB burn marks"/></figure>



<p class="wp-block-paragraph">The first component in the circuit is a fuse, designed to limit the damage from precisely this type of mishap. Using a <strong>multimeter </strong>to check for continuity showed that it was in fact blown, as one would expect. I went ahead and jumpered it out with a <strong>wire wrap connection</strong>.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/S71200wire_wrap.jpg" alt="wire wrap across fuse leads"/></figure>



<h2 id="h-now-what" class="wp-block-heading">Now What?</h2>



<p class="wp-block-paragraph">Unfortunately, the fix wasn&#8217;t going to be that easy. Measuring the resistance across the voltage supply terminals, I found there was now a <strong>short circuit</strong>. After checking all the components that go across the voltage bus, I found that the culprit was a small <strong>Zener diode</strong>.</p>



<p class="wp-block-paragraph">Its purpose is to suppress spikes in the supply voltage, but unsurprisingly, applying 120 VAC far exceeded its maximum power dissipation rating and caused it to short out. Simply <strong>desoldering the offending diode</strong> from one of its pads&nbsp;fixed the short, and after reassembling the PCBs&nbsp;and applying power, I was surprised that the&nbsp;PLC powered up and was fully functional. Clearly, the protection circuitry had done its job.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/S71200diode.jpg" alt="desoldered diode"/></figure>



<h2 id="h-patching-everything-up" class="wp-block-heading">Patching Everything Up</h2>



<p class="wp-block-paragraph">Even though I now had a working PLC, leaving it as-is would mean that it would most likely <strong>not survive a second jolt </strong>of high voltage, so I went ahead and <strong>replaced the fuse and Zener diode </strong>to make it almost as good as new at a cost of less than $2.</p>


<div><iframe width="50%" height="260" src="https://www.youtube.com/embed/videoseries?list=PLWKqK_J0HJAXYg5nYTHmJIVJppLRf804D&amp;autoplay=1&amp;loop=1&amp;mute=1" title="Video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe><iframe width="50%" height="260" src="https://www.youtube.com/embed/videoseries?list=PLWKqK_J0HJAUupFaOfVWaSaP214fvv186&amp;autoplay=1&amp;loop=1&amp;mute=1" title="Video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>


<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">After that, it&#8217;s easy to snap everything back together, power it up, and <strong>watch the blinkenlights turn on</strong>!</p>


<div><iframe loading="lazy" width="100%" height="520" src="https://www.youtube.com/embed/videoseries?list=PLWKqK_J0HJAWwQ8QpCsvXHAvbhkNKLjo2&amp;autoplay=1&amp;loop=1&amp;mute=1" title="Video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>


<p class="wp-block-paragraph">&nbsp;</p>



<h2 id="h-conclusion" class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">I hope you enjoyed this sneak peek under the hood of a device that we work with on a daily basis. It was nice to see that Siemens designs their PLCs to be pretty robust and easy to disassemble, unlike the typical consumer electronics devices you tend to see these days. Share your own stories of hacking PLC hardware in the comments section below!</p>


<hr />


<p class="wp-block-paragraph">Learn more about DMC&#8217;s <a href="/services/manufacturing-automation-and-intelligence/plc-programming/siemens-s7-plc-programming"><strong>Siemens S7 PLC programming expertise</strong></a> and <a href="https://static.dmcinfo.com/services/embedded-development-and-embedded-programming"><strong>embedded design expertise</strong></a>.&nbsp;</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/22198/how-to-disassemble-and-fix-a-fried-s7-1200-plc/">How To Disassemble and Fix a Fried S7-1200 PLC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To Make a Simple Time-lapse IP Web Camera Using a Raspberry Pi</title>
		<link>https://static.dmcinfo.com/blog/22333/how-to-make-a-simple-time-lapse-ip-web-camera-using-a-raspberry-pi/</link>
		
		<dc:creator><![CDATA[Jirka Hladis]]></dc:creator>
		<pubDate>Mon, 07 Jan 2019 19:32:16 +0000</pubDate>
				<category><![CDATA[Culture]]></category>
		<category><![CDATA[Scripting]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/22333/how-to-make-a-simple-time-lapse-ip-web-camera-using-a-raspberry-pi/</guid>

					<description><![CDATA[<p>When it comes to capturing time-lapse videos, there are numerous off-the-shelf solutions and libraries out there. For last year’s FedEx Day project, I decided to implement a minimalist solution on a Raspberry Pi&#160;using only the default image capture API that ships with Raspbian (raspistill), the built-in UNIX task-scheduler (cron), a video conversion utility (avconv), and [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/22333/how-to-make-a-simple-time-lapse-ip-web-camera-using-a-raspberry-pi/">How To Make a Simple Time-lapse IP Web Camera Using a Raspberry Pi</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">When it comes to capturing time-lapse videos, there are numerous off-the-shelf solutions and libraries out there. For <a href="https://static.dmcinfo.com/blog/24406/cooking-up-new-ideas-and-bbq-at-dmcs-fedex-day/">last year’s FedEx Day project</a>, I decided to implement a minimalist solution on a Raspberry Pi&nbsp;using only the default image capture API that ships with Raspbian (<a href="https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspistill.md">raspistill</a>), the built-in UNIX task-scheduler (<a href="https://en.wikipedia.org/wiki/Cron">cron</a>), a video conversion utility (<a href="https://sourceforge.net/projects/avconv/" target="_blank" rel="noreferrer noopener">avconv</a>), and a lightweight web server (<a href="https://www.lighttpd.net/" target="_blank" rel="noreferrer noopener">lighttpd</a>).</p>



<ul class="wp-block-list">
<li>Set up your Raspberry Pi with the latest version of Raspbian by <a href="https://www.raspberrypi.org/documentation/installation/installing-images/">burning the image to an SD card</a>.</li>



<li>Connect the camera and power on the Pi. Connect to your Wi-Fi or wired network, and open a terminal session, either <a href="https://www.raspberrypi.org/documentation/remote-access/ssh/">via SSH</a> or directly on the Pi.</li>



<li>(Optional) <a href="https://www.raspberrypi.com/documentation/computers/configuration.html#assign-a-static-ip-address" target="_blank" rel="noreferrer noopener">Set up a static IP address</a>. This will let you find your server in the same place every time. If you&#8217;re feeling fancy, you can even set up a DNS server to resolve a URL (e.g. myraspitimelapse.local) to this IP address.</li>



<li>Install <strong>libav</strong>, which contains avconv. This is the tool we will use to create a video from the captured stills: </li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Bash</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>sudo apt-get install libav-tools</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">sudo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">apt-get</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">install</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">libav-tools</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Install <strong>lighttpd</strong>.&nbsp;This will let you view your time-lapse video via a&nbsp;web browser: </li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Bash</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>sudo apt-get install lighttpd</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">sudo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">apt-get</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">install</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">lighttpd</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Create a basic web page containing the time-lapse&nbsp;video and save it&nbsp;in the directory where lighttpd will know&nbsp;to serve it. The most straightforward way is to use the built-in text editor nano: </li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Bash</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>sudo nano /var/www/html/index.html</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">sudo</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">nano</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">/var/www/html/index.html</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Then copy in the following HTML, which tells the browser to automatically play the video on loop (the muted attribute is required for some browsers to autoplay):</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">HTML</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;!DOCTYPE html>
&lt;html>
&lt;head>
    &lt;title>My Timelapse&lt;/title>
&lt;/head>
&lt;body>

    &lt;video autoplay loop muted>
        &lt;source src="timelapse.mp4" type="video/mpt support the video tag.
    &lt;/video>

&lt;/body>
&lt;/html></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #808080">&lt;!</span><span style="color: #569CD6">DOCTYPE</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">html</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">html</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">head</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">title</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">My Timelapse</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">title</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">head</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #808080">&lt;</span><span style="color: #569CD6">body</span><span style="color: #808080">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">video</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">autoplay</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">loop</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">muted</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">source</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">src</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;timelapse.mp4&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;video/mpt support the video tag.</span></span>
<span class="line"><span style="color: #CE9178">    </span><span style="color: #F44747">&lt;</span><span style="color: #CE9178">/video&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F44747">&lt;</span><span style="color: #CE9178">/body&gt;</span></span>
<span class="line"><span style="color: #F44747">&lt;</span><span style="color: #CE9178">/html&gt;</span></span></code></pre></div>



<ul class="wp-block-list">
<li>To create the video, we&#8217;ll use a simple shell script. Open a new file using nano as before:
 <pre style="font-family: monospace;background-color: #333;color: #f0f2f1;"> sudo nano /home/pi/timelapse/timelapse.sh</pre>
 and paste in the following script, which saves a&nbsp;new still photo and generates a video file from all the existing stills every time it runs. It takes an input argument that specifies the number of seconds for the video duration.

 
 </li>



<li>Next, change the file permissions so that the script can be executed:
 <pre style="font-family: monospace;background-color: #333;color: #f0f2f1;"> sudo chmod +x /home/pi/timelapse/timelapse.sh</pre>
 You should now be able to run your script as:

 <pre style="font-family: monospace;background-color: #333;color: #f0f2f1;"> /home/pi/timelapse/timelapse.sh 5</pre>
 and see the timelapse.mp4 file get created in the directory. Enter the Pi&#8217;s IP address into your browser, and you should be able to play the 5-second long video. For now, it won&#8217;t be too exciting, since it will just be a single frame.</li>



<li>Finally, set up the cron job that will trigger shell script. Open the cron table file:
 <pre style="font-family: monospace;background-color: #333;color: #f0f2f1;"> crontab -e</pre>
 Add the following line to the end of the file:

 <pre style="font-family: monospace;background-color: #333;color: #f0f2f1;"> 0 * * * * /home/pi/timelapse/timelapse.sh 30 &gt; /home/pi/timelapse/timelapse.log</pre>
 This runs the shell script once per minute, and the input argument of 30 means that the video will be 30 seconds long. The <strong>&gt;</strong> symbol redirects any errors to the specified log file. You can, of course, use other time intervals as well, and <a href="https://crontab.guru/">this handy website</a> helps explain the syntax, which can be a little confusing.</li>



<li>And that&#8217;s it! You can change all the parameters as needed, and you can&nbsp;even try making some tweaks to the script. For example, maybe you want the time-lapse video to show each frame for a set amount of time rather than keeping a fixed duration for the whole video, in which case you can get rid of the input argument and simply set the framerate to a constant.</li>
</ul>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-0e47273b wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="FedEx Day Timelapse 1" width="500" height="375" src="https://www.youtube.com/embed/jfp4BhVoWIA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="FedEx Day Timelapse 2" width="500" height="375" src="https://www.youtube.com/embed/V9R65scuHj0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
</div>
</div>



<p class="wp-block-paragraph">Learn more about DMC&#8217;s <a href="https://static.dmcinfo.com/services/embedded-development-and-embedded-programming"><strong>embedded design expertise</strong></a>.&nbsp;</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/22333/how-to-make-a-simple-time-lapse-ip-web-camera-using-a-raspberry-pi/">How To Make a Simple Time-lapse IP Web Camera Using a Raspberry Pi</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Connect to an Allen-Bradley SLC 500 PLC</title>
		<link>https://static.dmcinfo.com/blog/23982/how-to-connect-to-an-allen-bradley-slc-500-plc/</link>
		
		<dc:creator><![CDATA[Jirka Hladis]]></dc:creator>
		<pubDate>Thu, 24 Aug 2017 15:58:22 +0000</pubDate>
				<category><![CDATA[Allen Bradley PLC]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[PLC]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/23982/how-to-connect-to-an-allen-bradley-slc-500-plc-2/</guid>

					<description><![CDATA[<p>With most modern PLCs, all you need to start programming or troubleshooting is to plug in your laptop with a plain old ethernet cable. Older PLCs, however, tend to use a myriad of different cables and protocols. With a large number of legacy systems, the original programming cables and software are often long lost by [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/23982/how-to-connect-to-an-allen-bradley-slc-500-plc/">How to Connect to an Allen-Bradley SLC 500 PLC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">With most modern PLCs, all you need to start programming or troubleshooting is to plug in your laptop with a plain old ethernet cable. Older PLCs, however, tend to use a myriad of different cables and protocols. With a large number of legacy systems, the original programming cables and software are often long lost by the time you want to make updates or need to troubleshoot an issue.</p>

<p class="wp-block-paragraph">One of the most popular series of PLCs from the 1990s to mid-2000s is the Allen-Bradly SLC 500 series: SLC 5/01, SLC 5/02, SLC 5/03, SLC 5/04, and SLC 5/05. <strong>This blog post summarizes the different ways that you can connect to these PLCs and some of the pitfalls to look out for</strong>.</p>

<h2 class="wp-block-heading">Checking Your Settings</h2>

<p class="wp-block-paragraph">If you have access to the original PLC program (typically a .RSS file) and a copy of the RSLogix 500 programming software (contact your A-B distributor if you need one), you can open the project and check the communication settings by double-clicking on <strong>Channel Configuration</strong> under the controller section of the project tree:</p>

<figure class="wp-block-image"><img decoding="async" alt="Configuring the channel driver for SLC 500 PLC" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Channel-Config-1-and-2-1.jpg"  /></figure>

<p class="wp-block-paragraph"><em>Channel Configuration in RSLogix 500</em></p>

<p class="wp-block-paragraph">However, as is&nbsp;often the case with legacy systems, if you don&apos;t have the program handy, you can usually figure out how to connect by&nbsp;method of elimination and a bit of luck.</p>

<h2 class="wp-block-heading">Finding the Right Cables</h2>

<figure class="wp-block-image"><img decoding="async" alt="Comparison of SLC 500 PLC models" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/SLC500-Models-Final-1.jpg"  /></figure>

<p class="wp-block-paragraph"><em>SLC 500 Connectivity Comparison</em></p>

<p class="wp-block-paragraph">As you can see, each model features a slightly different set of physical ports and protocols. The two most common ways to connect are DH-485 and DF1. The SLC 5/01, 5/02, and 5/03 use DH-485 via their RS485 8-pin RJ45 jack (which is the same physical connector as Ethernet, but it won&apos;t work if you plug it directly into your computer!).</p>

<p class="wp-block-paragraph">Additionally, the 5/03, 5/04, and 5/05 have an RS232 DB9 port that <strong>MAY </strong>be configured for DH-485, but it will be set to DF1 by default and can also be configured for ASCII and Modbus RTU. You&apos;ll notice the 5/05 also has an RJ45 port, but this is for Ethernet, NOT DH-485.</p>

<p class="wp-block-paragraph"><strong>DF1</strong><br />
All you need is a regular female-to-female null modem crossover DB9 serial cable, and a USB to RS232 adapter if your computer does not have a serial port. The official A-B cable is 1747-CP3.</p>

<p class="wp-block-paragraph"><strong>DH-485</strong><br />
This protocol requires a special USB converter, the 1747-UIC, which you can order from A-B or find a number of 3rd party versions online. In addition, you will need the female-to-female null modem crossover DB9 serial cable or a straight-through RJ45 ethernet cable, depending on which port you want to connect to. Make sure you also set the selector switch to RS232 or RS 485.</p>

<p class="wp-block-paragraph"><strong>There is one potential pitfall:</strong> the default baud rate for DH485 is 19200, and this is what you will find most of the time. However, it is possible to configure it in the PLC settings to be 1200, 2400, or 9600 baud at as well.</p>

<p class="wp-block-paragraph">The 1747-UIC adapter only supports 19200 baud out of the box. If you have an original A-B unit, it is possible to change to the baud rate to 9600&nbsp;by following the steps in this&nbsp;<a href="https://rockwellautomation.custhelp.com/app/answers/detail/a_id/493842" target="_blank">Rockwell knowledgebase document</a> (requires creating a free account and logging in).</p>

<h2 class="wp-block-heading">Setting Up Drivers in RSLinx</h2>

<p class="wp-block-paragraph">You will need to set up the connection in the RSLinx software. It comes installed as part of RSLogix 500, or you can <a href="https://compatibility.rockwellautomation.com/Pages/Home.aspx" target="_blank">download RSLinx Classic Lite</a> for free from Rockwell.&nbsp;</p>

<p class="wp-block-paragraph">Once you plug in your cable and Windows installs any necessary drivers, it should show up in Device Manager as a COM port. Open RSLinx and go to <strong>Communications -&gt; Configure Drivers.</strong></p>

<p class="wp-block-paragraph"><strong>DF1</strong><br />
From the drop-down, select RS-232 DF1 devices and click Add New. Select the right COM port and click Auto-Configure. The driver should be able to figure out the correct settings.</p>

<figure class="wp-block-image"><img decoding="async" alt="Configure DF1 driver in RSLinx" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/rslinx_config_DF1-1.gif"  /></figure>

<p class="wp-block-paragraph"><strong>DH-485</strong><br />
From the drop-down, select DH485 UIC devices and click Add New. Select the right COM port and click OK.</p>

<p class="wp-block-paragraph">If everything worked, your PLC should show up, and you should be able to open&nbsp;RSLogix, go online, and upload and download programs.</p>

<h2 class="wp-block-heading">Summary of Allen-Bradley PLC Cables and Protocols</h2>

<figure class="wp-block-image"><img decoding="async" alt="Table of Allen-Bradley PLC Cables and Protocols" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/AB_PLC_Table_1-1.png"  /></figure>

<h2 class="wp-block-heading">Relevant Links</h2>

<p class="wp-block-paragraph"><a href="http://literature.rockwellautomation.com/idc/groups/literature/documents/um/1747-um011_-en-p.pdf">Here is a link to the SLC 500 user manual.</a></p>

<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/latest-thinking/blog/id/9058/connecting-to-a-legacy-ab-plc-5">Here is a walkthrough on connecting to an AB PLC-5.</a></p>

<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/latest-thinking/blog/id/87/working-with-legacy-hardware-tips-and-tricks">Here are some general tips on working with legacy hardware.</a>&nbsp;</p>

<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/plc-programming/allen-bradley-plc-programming">Learn more about DMC&apos;s Allen-Bradley PLC programming expertise</a>&nbsp;or <a href="https://static.dmcinfo.com/contact">Contact Us</a> to get started on a solution today.</p>
<p>The post <a href="https://static.dmcinfo.com/blog/23982/how-to-connect-to-an-allen-bradley-slc-500-plc/">How to Connect to an Allen-Bradley SLC 500 PLC</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
