<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexey Boyko</title>
    <description>The latest articles on DEV Community by Alexey Boyko (@alexboyko).</description>
    <link>https://dev.to/alexboyko</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F809514%2F61a168f5-f6e6-4709-8785-fae2c3d84440.jpeg</url>
      <title>DEV Community: Alexey Boyko</title>
      <link>https://dev.to/alexboyko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexboyko"/>
    <language>en</language>
    <item>
      <title>DGRM.net whiteboard / Blocks have become more compact</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Thu, 19 Mar 2026 12:53:40 +0000</pubDate>
      <link>https://dev.to/alexboyko/dgrmnet-whiteboard-blocks-have-become-more-compact-22f3</link>
      <guid>https://dev.to/alexboyko/dgrmnet-whiteboard-blocks-have-become-more-compact-22f3</guid>
      <description>&lt;p&gt;&lt;a href="http://dgrm.net/" rel="noopener noreferrer"&gt;DGRM.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzu8xu8gaevq6rgpaqwz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzu8xu8gaevq6rgpaqwz.png" alt="Now the blocks are more compact" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Previously, blocks were expanded by 2 cells, now by 1.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>product</category>
    </item>
    <item>
      <title>DGRM.net whiteboard / You can now set text alignment</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Sat, 31 Jan 2026 04:07:04 +0000</pubDate>
      <link>https://dev.to/alexboyko/dgrmnet-whiteboard-you-can-now-set-text-alignment-2b2g</link>
      <guid>https://dev.to/alexboyko/dgrmnet-whiteboard-you-can-now-set-text-alignment-2b2g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedubqm1bielsut4ajau9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedubqm1bielsut4ajau9.png" alt="Text alignment settings" width="800" height="695"&gt;&lt;/a&gt;&lt;br&gt;
You can now set text alignment. Useful for large blocks.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>saas</category>
      <category>programming</category>
    </item>
    <item>
      <title>Flowchart editor dgrm.net / Now text fits into shapes automatically</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Thu, 17 Jul 2025 14:56:52 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-dgrmnet-now-text-fits-into-shapes-automatically-1djd</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-dgrmnet-now-text-fits-into-shapes-automatically-1djd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmboxh27w7695i1sfe7jw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmboxh27w7695i1sfe7jw.gif" alt="Text fits into shapes automatically" width="458" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the text does not fit, the shape is enlarged. Works for all shapes and text.&lt;/p&gt;

&lt;p&gt;DGRM.net is the only board with text fitting into figures. Compare text fitting of competitors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flttpjkr4hv957hudbqrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flttpjkr4hv957hudbqrb.png" alt="Compare text fitting of competitors" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check it out yourself - &lt;a href="https://dgrm.net/" rel="noopener noreferrer"&gt;DGRM.net&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tooling</category>
    </item>
    <item>
      <title>JavaScript. How to Make a Blazingly Fast Multithreaded Data Grid for 1,000,000 Rows. Part 2/2: working with threads</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Mon, 05 May 2025 16:02:12 +0000</pubDate>
      <link>https://dev.to/alexboyko/javascript-how-to-make-a-blazingly-fast-multithreaded-data-grid-for-1000000-rows-part-22-3apj</link>
      <guid>https://dev.to/alexboyko/javascript-how-to-make-a-blazingly-fast-multithreaded-data-grid-for-1000000-rows-part-22-3apj</guid>
      <description>&lt;p&gt;&lt;a href="https://dgrm.net/lab/grid/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; | &lt;a href="https://github.com/AlexeyBoiko/tbl-js" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpslcxsk7b6gfi19gim7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpslcxsk7b6gfi19gim7.png" alt="Figure 1. Data Grid with 1,000,000 rows" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast Data Grid features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incredibly Fast&lt;/li&gt;
&lt;li&gt;Multithreaded&lt;/li&gt;
&lt;li&gt;Only 523 Lines of Code&lt;/li&gt;
&lt;li&gt;No Dependencies&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try scrolling and searching through 1,000,000 rows — &lt;a href="https://dgrm.net/lab/grid/" rel="noopener noreferrer"&gt;Fast Data Grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I will talk about working with threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main idea: to move the search to a separate thread. Show search results in parts as they are ready, without waiting for the search to complete
&lt;/h2&gt;

&lt;p&gt;Long calculations block the UI.&lt;/p&gt;

&lt;p&gt;Tasks in a thread are executed sequentially. Until the current task is completed, the next task will not be executed. Processing mouse clicks, scrolling — these are also tasks.&lt;/p&gt;

&lt;p&gt;Searching for 1,000,000 lines is long. While the search is in progress, the browser will not respond to user actions. The browser will “freeze”. To eliminate the freeze, you can move the search to a separate thread.&lt;/p&gt;

&lt;p&gt;Simply putting the search in a separate thread is not enough. The UI will not freeze, but the search results will also take a long time to display. You need to show the results in parts as they are ready. Fast Data Grid displays the first 100 rows at once. While you scroll, Fast Data Grid searches for the next rows in a separate thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data transfer between threads takes a long time. Only some types of data can be transferred by reference. Such types are called “Transferable objects”
&lt;/h2&gt;

&lt;p&gt;Only the main browser thread has access to the DOM. This means that only the main thread can display the search results. Therefore, the results from the search thread must be passed to the main thread.&lt;/p&gt;

&lt;p&gt;Data transfer between threads takes a long time, so transferring an array of grid rows with all the data for display is inefficient.&lt;/p&gt;

&lt;p&gt;Only some types of data can be transferred efficiently. These types of data are called “Transferable objects”. Transferable objects are not cloned when transferred between threads, but are passed by reference. Uint32Array is a “Transferable object”. We can use Uint32Array to pass grid row indices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jyj6cwo4ymb98tda39d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jyj6cwo4ymb98tda39d.png" alt="Figure 2. The main thread and the search thread store copies of the data string array. The search thread searches its copy and passes the string indices to the main thread for display using Uint32Array." width="800" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  setTimeout puts tasks at the end of the queue. With setTimeout, you can split a long operation into subtasks and not block the thread
&lt;/h2&gt;

&lt;p&gt;Searching a million lines takes a long time. If the user changes the search string, you need to interrupt the previous search as soon as possible and start a new one. There is no way to interrupt the operation. But you can break the search into subtasks. Between subtasks, check whether the search string has changed.&lt;/p&gt;

&lt;p&gt;setTimeout puts a task at the end of the queue. See Listing 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Listing 1. setTimeout puts tasks at the end of the queue. At first, the console will show 2, then 1.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Listing 2 searches over blocks of 5,000 lines. At the end of the block, setTimeout allows the thread to react to the change in newStr.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;arrForEachChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// chunkSize&lt;/span&gt;
    &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// forEachCallBack&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* search */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// chunkEndCallBack&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// let other tasks go&lt;/span&gt;


        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_str&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// stop search&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// continue search&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Listing 2. The search is performed in blocks of 5,000 lines. At the end of the block, setTimeout allows the thread to react to the change in newStr&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;arrForEachChunk is my utility, it is in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-promotion
&lt;/h2&gt;

&lt;p&gt;I am making the most beautiful flowchart editor &lt;a href="http://dgrm.net/" rel="noopener noreferrer"&gt;DGRM.net&lt;/a&gt;.&lt;br&gt;
Check it out yourself.&lt;/p&gt;

&lt;p&gt;Give it a star on &lt;a href="https://github.com/AlexeyBoiko/DgrmJS" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>JavaScript. How to Make a Blazingly Fast Multithreaded Data Grid for 1,000,000 Rows. Part 1/2: The Nuances of Working with DOM</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Sun, 01 Dec 2024 06:28:05 +0000</pubDate>
      <link>https://dev.to/alexboyko/javascript-how-to-make-a-blazingly-fast-multithreaded-data-grid-for-1000000-rows-part-12-the-3ek5</link>
      <guid>https://dev.to/alexboyko/javascript-how-to-make-a-blazingly-fast-multithreaded-data-grid-for-1000000-rows-part-12-the-3ek5</guid>
      <description>&lt;p&gt;&lt;a href="https://dgrm.net/lab/grid/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; | &lt;a href="https://github.com/AlexeyBoiko/tbl-js" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgku5ha6n8r1n76g0b7in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgku5ha6n8r1n76g0b7in.png" alt="Figure 1. Data Grid with 1,000,000 rows" width="800" height="377"&gt;&lt;/a&gt;&lt;em&gt;Figure 1. Data Grid with 1,000,000 rows&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fast Data Grid Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incredibly Fast&lt;/li&gt;
&lt;li&gt;Multithreaded&lt;/li&gt;
&lt;li&gt;Only 523 Lines of Code&lt;/li&gt;
&lt;li&gt;No Dependencies&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try scrolling and searching through 1,000,000 rows - &lt;a href="https://dgrm.net/lab/grid/" rel="noopener noreferrer"&gt;Fast Data Grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I will list the nuances of working with DOM. About multithreading in the next article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smaller the DOM, the better. Changing the contents of a DIV is faster than deleting a DIV and creating a new one.
&lt;/h2&gt;

&lt;p&gt;The browser is slow in rendering a large DOM tree. The browser won't render 1,000,000 lines of 20 px height at all - the maximum height of a DIV in Chrome is 15,000,000 px. The fewer HTML elements, the better.&lt;/p&gt;

&lt;p&gt;Fast Data Grid adds as many rows to the DOM as will fit on the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rowsCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewPortHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;rowHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;em&gt;Listing 1. Counting how many lines fit on the screen&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When new data needs to be output, the row DIVs are reused. New data is written to the same DIVs. Changing the contents of a DIV is faster than deleting a DIV and creating a new one.&lt;/p&gt;

&lt;p&gt;When scrolling, the position of the DIV rows is calculated using JavaScript.&lt;/p&gt;
&lt;h2&gt;
  
  
  Maximum DIV height is 15,000,000 px
&lt;/h2&gt;

&lt;p&gt;To make the scroll work, Fast Data Grid makes a big DIV. The scroll event is attached to this DIV. The scroll event handler calculates the position of the row DIVs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf2pzppmmh85ex46muym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf2pzppmmh85ex46muym.png" alt="Figure 2. Large DIV for scrolling" width="800" height="522"&gt;&lt;/a&gt;&lt;em&gt;Figure 2. Large DIV for scrolling&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the total height of the rows is more than 15,000,000 px, then the row DIVs should scroll faster than the big DIV. When the big DIV scrolls to the end -&amp;gt; the row DIVs should also scroll to the end.&lt;/p&gt;

&lt;p&gt;When scrolling DIV rows, a coefficient must be applied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollYKoef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="c1"&gt;// if {allRowsHeight} &amp;gt; 15 million -&amp;gt; we have to applay koef on scroll&lt;/span&gt;
    &lt;span class="c1"&gt;// if {allRowsHeight} &amp;lt;= 15 million -&amp;gt; {scrollYKoef} = 1&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allRowsHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;viewPortHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrolHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;viewPortHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollOverlayDiv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/** @param {Event &amp;amp; {target:HTMLDivElement}} evt */&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scrollYKoef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;rowsDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translateY(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;em&gt;Listing 2. Using the coefficient when scrolling&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  CSS transform translate is faster than CSS top
&lt;/h2&gt;

&lt;p&gt;When scrolling, the position is set via transform translate. CSS transform translate is faster than CSS top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- transform faster--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"transform: translateY(-16px);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- than top --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"top: -16px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;em&gt;Listing 3. CSS transform translate is faster than CSS top&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Read DOM first, then modify DOM. It's bad to read DOM after modification
&lt;/h2&gt;

&lt;p&gt;The browser displays frames on the monitor like this:&lt;br&gt;
First, JavaScript is processed, then styles are calculated, then layout, then rendering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanfl1zuuk8rkhvrofxip.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanfl1zuuk8rkhvrofxip.jpg" alt="Figure 3. Standard order of operations when outputting a frame to the monitor" width="800" height="122"&gt;&lt;/a&gt;&lt;em&gt;Figure 3. Standard order of operations when outputting a frame to the monitor&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the standard order is not violated, the browser will render the frame as quickly as possible.&lt;/p&gt;

&lt;p&gt;At the beginning of the cycle, the DOM parameters are already calculated and correspond to the parameters of the previous frame. For example, box.offsetHeight is already calculated at the beginning of the cycle. But if you change the DOM and then read the DOM -&amp;gt; the browser will have to break the standard order. It will be necessary to calculate the layout again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;super-big&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Gets the height of the box in pixels and logs it out:&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;em&gt;Listing 4. Modifying DOM before reading DOM. Bad. Leads to layout thrashing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Excessive recalculation of Layout is called “layout thrashing”.&lt;/p&gt;

&lt;p&gt;A visual demonstration of how modifying the DOM before reading slows down the browser:&lt;br&gt;
&lt;a href="https://wilsonpage.github.io/fastdom/examples/animation.html" rel="noopener noreferrer"&gt;https://wilsonpage.github.io/fastdom/examples/animation.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great article on the topic:&lt;br&gt;
&lt;a href="https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing" rel="noopener noreferrer"&gt;Avoid large, complex layouts and layout thrashing  |  Articles  |  web.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-promotion
&lt;/h2&gt;

&lt;p&gt;I make the most convenient flowchart editor &lt;a href="https://dgrm.net/" rel="noopener noreferrer"&gt;DGRM.net&lt;/a&gt;.&lt;br&gt;
It is also the most convenient service for business: Excel + business process diagrams.&lt;/p&gt;

&lt;p&gt;Give stars on &lt;a href="https://github.com/AlexeyBoiko/DgrmJS" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Diagramming Tool DGRM.net / Team Subsriptions</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Fri, 19 Jul 2024 06:44:31 +0000</pubDate>
      <link>https://dev.to/alexboyko/diagramming-tool-dgrmnet-team-subsriptions-cmd</link>
      <guid>https://dev.to/alexboyko/diagramming-tool-dgrmnet-team-subsriptions-cmd</guid>
      <description>&lt;p&gt;Now you can pay your colleagues for a subscription&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzerw01jxr4bm1plmc9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzerw01jxr4bm1plmc9u.png" alt="Manage team subsriptions" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pay for exactly as many subscriptions as you need. You can delete your subscription at any time. Flexible setup is better than “subscription packages”.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Flowchart editor dgrm.net / ChatGPT plugin for diagramming</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Thu, 16 May 2024 16:14:11 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-dgrmnet-chatgpt-plugin-for-diagramming-54kf</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-dgrmnet-chatgpt-plugin-for-diagramming-54kf</guid>
      <description>&lt;p&gt;ChatGPT plugin for diagramming&lt;br&gt;
&lt;a href="https://chatgpt.com/g/g-LSaf72err-dgrm-net"&gt;https://chatgpt.com/g/g-LSaf72err-dgrm-net&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>webdev</category>
      <category>startup</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Flowchart editor dgrm.net / touchpad gestures</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Tue, 19 Mar 2024 06:38:20 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-dgrmnet-touchpad-gestures-1c8o</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-dgrmnet-touchpad-gestures-1c8o</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvgjm2jw5b7tbqcegx21.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvgjm2jw5b7tbqcegx21.gif" alt="Navigate flowchart with touchpad" width="596" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Improved work with the touchpad on laptops.&lt;/p&gt;

&lt;p&gt;There is now excellent support for touchpad gestures. With two fingers you can smoothly move and zoom the diagrams.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
    <item>
      <title>WebRTC. How to establish a p2p connection between browsers</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Thu, 14 Mar 2024 15:31:23 +0000</pubDate>
      <link>https://dev.to/alexboyko/webrtc-how-to-establish-a-p2p-connection-between-browsers-5cib</link>
      <guid>https://dev.to/alexboyko/webrtc-how-to-establish-a-p2p-connection-between-browsers-5cib</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdq5s1ljbba7vyvm75gz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdq5s1ljbba7vyvm75gz.gif" alt="Fig 1. Simultaneous work in the flowchart editor" width="800" height="454"&gt;&lt;/a&gt;&lt;em&gt;Fig 1. Simultaneous work in the flowchart editor&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;WebRTC allows browsers to communicate directly without a server. You can transmit video, sound and data. You can establish a WebRTC connection in different ways. The article describes how a WebRTC connection is established between users of the &lt;a href="https://dgrm.net/"&gt;dgrm.net&lt;/a&gt; flowchart editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  In DGRM, clients do not connect “everyone to everyone”. Events are sent to clients by the browser of the meeting initiator
&lt;/h2&gt;

&lt;p&gt;When a user adds a shape, changes colour, or moves the cursor, event data is sent via WebRTC to the browser of the meeting initiator. The initiator forwards the data to other users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq5l2kdd2duz3jlxuxod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq5l2kdd2duz3jlxuxod.png" alt="Fig 2. Clients are not connected “each to each”. Clients send events to the meeting initiator. The initiator forwards events to other clients." width="800" height="800"&gt;&lt;/a&gt;&lt;em&gt;Fig 2. Clients are not connected “each to each”. Clients send events to the meeting initiator. The initiator forwards events to other clients.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To connect via WebRTC, browsers must exchange connection parameters
&lt;/h2&gt;

&lt;p&gt;When a client connects to an initiator, it must inform the initiator of the connection parameters: connection requirements and its network addresses.&lt;/p&gt;

&lt;p&gt;Example of connection requirements: should the connection support video, what codec to use, etc. DGRM only transfers JSON strings.&lt;/p&gt;

&lt;p&gt;A client can have several network addresses. For example, one address is on the local network, the other is on the external network. If the client and the meeting initiator are on the same local network, the local network address will be used for the connection. The browser itself cannot find out the address on the external network. To obtain its external address, the browser makes a request to a special STUN server. The STUN server returns its external addresses to the browser. There are free STUN servers, for example from Google.&lt;/p&gt;

&lt;p&gt;The connection parameters that the client passes when connecting are called SDPOffer. The meeting initiator generates SDPAnswer based on SDPOffer. And sends SDPAnswer to the client. SDPAnswer contains the network addresses of the initiator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp40fbmabdsabvtt12d02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp40fbmabdsabvtt12d02.png" alt="Fig 3. To connect via WebRTC, the client must pass the SDPOffer connection parameters to the meeting initiator. Based on the SDPOffer, the initiator generates an SDPAnswer and transmits it to the client. After this, the browsers recognize each other’s addresses and a WebRTC connection is established." width="800" height="730"&gt;&lt;/a&gt;&lt;em&gt;Fig 3. To connect via WebRTC, the client must pass the SDPOffer connection parameters to the meeting initiator. Based on the SDPOffer, the initiator generates an SDPAnswer and transmits it to the client. After this, the browsers recognize each other’s addresses and a WebRTC connection is established.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exchange of WebRTC connection parameters via your server
&lt;/h2&gt;

&lt;p&gt;You can exchange connection parameters in different ways. For example, manually via messenger. SDPOffer and SDPOffer are strings. Using JavaScript and the browser API, you can get SDPOffer/SDPAnswer, display it on the page, copy it and send it to the messenger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=0
o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com
s=
c=IN IP4 host.atlanta.example.com
t=0 0
m=audio 49170 RTP/AVP 0 8 97
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:97 iLBC/8000
m=video 51372 RTP/AVP 31 32
a=rtpmap:31 H261/90000
a=rtpmap:32 MPV/90000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Listing 1. SDPOffer example. SDPOffer and SDPAnswer are strings&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DGRM uses its own server to exchange connection parameters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xv949lgc05zt9oj5155.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xv949lgc05zt9oj5155.png" alt="Fig 4. Creating an online meeting in dgrm.net" width="800" height="798"&gt;&lt;/a&gt;&lt;em&gt;Fig 4. Creating an online meeting in dgrm.net&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When the meeting initiator clicks “Live collaboration”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The initiator’s browser sends a “create meeting” request with a random meeting id to the DGRM server.&lt;/li&gt;
&lt;li&gt;The server remembers the meeting id.&lt;/li&gt;
&lt;li&gt;The initiator sends a link with the meeting ID via messenger.&lt;/li&gt;
&lt;li&gt;The client follows the link.&lt;/li&gt;
&lt;li&gt;The client browser generates SDPOffer. Sends the meeting id, your client id and SDPOffer to the server.&lt;/li&gt;
&lt;li&gt;The server remembers SDPOffer.&lt;/li&gt;
&lt;li&gt;The initiator’s browser periodically queries the server “give SDPOffers for the meeting id.”&lt;/li&gt;
&lt;li&gt;The initiator’s browser, when receiving SDPOffer, generates SDPAnswer. Sends the meeting id, client id and SDPAnswer to the server.&lt;/li&gt;
&lt;li&gt;The server remembers SDPAnswer&lt;/li&gt;
&lt;li&gt;After a pause, the client browser requests SDPAnswer from the server.&lt;/li&gt;
&lt;li&gt;As soon as the client receives SDPAnswer, a WebRTC connection is established.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4uewgs8j20vw32lvt91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4uewgs8j20vw32lvt91.png" alt="Fig 5. Scheme for exchanging WebRTC connection parameters via your server&amp;lt;br&amp;gt;
" width="800" height="824"&gt;&lt;/a&gt;&lt;em&gt;Fig 5. Scheme for exchanging WebRTC connection parameters via your server&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Clarifications to the diagram
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why aren’t websockets used for SDPOffer/SDPAnswer exchange?
&lt;/h3&gt;

&lt;p&gt;Here we use periodic server polls. WebRTC connection is established in a couple of seconds. If you use web sockets or server-events, the WebRTC connection will be established faster. But in this scheme there are fewer requirements for the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why can’t the initiator send SDPAnswer to the client directly via WebRTC?
&lt;/h3&gt;

&lt;p&gt;The initiator received SDPOffer. SDPOffer contains client addresses. Why not send SDPAnswer directly to the client? I don’t know. I could not get. I asked in WebRTC groups, they say this is not possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is SDPOffer made by the client and not the initiator?
&lt;/h3&gt;

&lt;p&gt;The initiator can make one SDPOffer for all clients. Then the exchange scheme will be simplified. Why didn’t you do so? The initiator must have a WebRTC connection with each client. One SDPOffer cannot be used in multiple connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  I don’t want to make my own implementation. Are there ready-made solutions?
&lt;/h3&gt;

&lt;p&gt;There are services for developers that not only exchange SDPOffer/SDPAnswer, but also offer a TURN server. If the p2p connection cannot be established, data begins to be sent through the TURN server. TURN is a fallback option.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Flowchart editor http://dgrm.net / images</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Mon, 26 Feb 2024 07:12:08 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-httpdgrmnet-images-29kn</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-httpdgrmnet-images-29kn</guid>
      <description>&lt;p&gt;Now you can add pictures.&lt;/p&gt;

&lt;p&gt;You can add pictures by dragging and dropping, using Ctrl + V or with the context menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfcinqqsokt0vqcu9vi7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfcinqqsokt0vqcu9vi7.gif" alt="Drag and drop to add image" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>workplace</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Flowchart editor dgrm.net / Rights for schemes</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Mon, 18 Dec 2023 17:43:43 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-dgrmnet-rights-to-schemes-100l</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-dgrmnet-rights-to-schemes-100l</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AWESYh8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mv3fvflm9r1cevgy11hl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AWESYh8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mv3fvflm9r1cevgy11hl.jpg" alt="Diagram rights" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can give viewing and editing rights.&lt;br&gt;
Now you can work with common schemes together with your colleagues.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Flowchart editor dgrm.net / DGRM cloud</title>
      <dc:creator>Alexey Boyko</dc:creator>
      <pubDate>Tue, 28 Nov 2023 16:12:16 +0000</pubDate>
      <link>https://dev.to/alexboyko/flowchart-editor-dgrmnet-dgrm-cloud-led</link>
      <guid>https://dev.to/alexboyko/flowchart-editor-dgrmnet-dgrm-cloud-led</guid>
      <description>&lt;p&gt;You can now store charts in the DGRM cloud. To do this, you need to log in to DGRM with Yandex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1nsk7od3tre458ij595.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1nsk7od3tre458ij595.png" alt="Login to flowchart editor with Yandex"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje4dp9i3m3mtmhg5b8lq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje4dp9i3m3mtmhg5b8lq.png" alt="Open diagrams from cloud"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
