<?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: Mike 🐈‍⬛</title>
    <description>The latest articles on DEV Community by Mike 🐈‍⬛ (@skhmt).</description>
    <link>https://dev.to/skhmt</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%2F169775%2Fb57d78d5-c889-4989-8c0f-8686b29cc373.jpg</url>
      <title>DEV Community: Mike 🐈‍⬛</title>
      <link>https://dev.to/skhmt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skhmt"/>
    <language>en</language>
    <item>
      <title>This Windows script isn't a cry for help</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Fri, 08 Nov 2024 03:20:36 +0000</pubDate>
      <link>https://dev.to/skhmt/this-windows-script-isnt-a-cry-for-help-1k5k</link>
      <guid>https://dev.to/skhmt/this-windows-script-isnt-a-cry-for-help-1k5k</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; /&lt;span class="o"&gt;*&lt;/span&gt; ::

@echo &lt;span class="na"&gt;off&lt;/span&gt;

&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="na"&gt;/a &lt;/span&gt;&lt;span class="kd"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="na"&gt;/a &lt;/span&gt;&lt;span class="kd"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;

&lt;span class="nl"&gt;:loop&lt;/span&gt;

&lt;span class="kd"&gt;CScript&lt;/span&gt; //nologo //E:JScript &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="vm"&gt;%~F0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"+{F15}"&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="na"&gt;/a &lt;/span&gt;&lt;span class="kd"&gt;rand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;%RANDOM%&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;%max%&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;%min%&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; / &lt;span class="m"&gt;32768&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;%min%&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="nb"&gt;ping&lt;/span&gt; &lt;span class="na"&gt;-n &lt;/span&gt;&lt;span class="nv"&gt;%rand%&lt;/span&gt; &lt;span class="na"&gt;-w &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;127&lt;/span&gt;.0.0.1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;NUL&lt;/span&gt;

&lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="nl"&gt;:loop&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;/ &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;WScript&lt;/span&gt;.CreateObject&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"WScript.Shell"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.SendKeys&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;WScript&lt;/span&gt;.Arguments&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What does this do?
&lt;/h2&gt;

&lt;p&gt;Take a moment and see if you can figure out what's happening here.&lt;/p&gt;

&lt;p&gt;Go on. I'll wait for 15 to 45 seconds, and then send the Shift + F15 key combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would you do that?
&lt;/h2&gt;

&lt;p&gt;Because the F15 key exists but most keyboards no longer have it, so pressing Shift + F15 is very unlikely to do anything except act as a key combination being pressed in general, preventing the computer from sleeping in most cases (except some VDI setups).&lt;/p&gt;

&lt;p&gt;It works without admin rights and on a vanilla Windows install, although some admins disable JScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the f...
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;0&amp;lt; /* ::&lt;/code&gt; is input &lt;a href="https://ss64.com/nt/syntax-redirection.html" rel="noopener noreferrer"&gt;redirection&lt;/a&gt; from a file for file descriptor 0, which is a special case and refers to standard input (STDIN). For example, if you ran &lt;code&gt;0&amp;lt; test.txt find "hello"&lt;/code&gt;, the shell would actually run &lt;code&gt;find "hello" 0&amp;lt;test.txt&lt;/code&gt;, which would be the equivalent of running &lt;code&gt;find "hello"&lt;/code&gt; then typing in the contents of &lt;code&gt;test.txt&lt;/code&gt; into the prompt. So what does this do? &lt;code&gt;0&amp;lt; /*&lt;/code&gt; actually produces an error. But when you put a comment command (either :: or REM) at the end, the command becomes &lt;code&gt;:: 0&amp;lt; /*&lt;/code&gt; which does nothing as it's a comment. We use &lt;code&gt;::&lt;/code&gt; instead of &lt;code&gt;REM&lt;/code&gt; because REM will print out the line to the terminal while &lt;code&gt;::&lt;/code&gt; will not do that, so aesthetically &lt;code&gt;::&lt;/code&gt; is nicer.&lt;/p&gt;

&lt;p&gt;So why the &lt;code&gt;/*&lt;/code&gt; when any string can go there? Because that's the start of a javascript/jscript block comment and the jscript interpreter needs to not try to parse the batch scripting between the &lt;code&gt;/*&lt;/code&gt; and the &lt;code&gt;*/&lt;/code&gt;. But then how does the jscript interpreter see this chunk of code? It reads the entire block from &lt;code&gt;0&amp;lt; /* ::&lt;/code&gt; to &lt;code&gt;*/ 0;&lt;/code&gt; as: &lt;code&gt;0 &amp;lt; 0;&lt;/code&gt; which evaluates to false, but that doesn't matter - it's valid jscript and doesn't have any side effects and the primary hurdle has been accomplished: a way to selectively parse batch script or jscript based on which interpreter is reading the file.&lt;/p&gt;

&lt;p&gt;I'm going to skip over turning off echo, setting variables, and label looping, as these are fairly simple concepts, although fun fact - ECMAScript also has labels.&lt;/p&gt;

&lt;p&gt;The next important line is &lt;code&gt;CScript //nologo //E:JScript "%~F0" "+{F15}"&lt;/code&gt;, which sends this .bat file to &lt;code&gt;CScript&lt;/code&gt;. CScript starts a script in a command-line environment with several script engine choices (JScript in thise case). &lt;code&gt;WScript&lt;/code&gt; is similar but runs a script in a Windows GUI and in this context is not completely related to the last line in the batch file. &lt;code&gt;$~F0&lt;/code&gt; is the full path, filename, and extension of the batch file currently running. &lt;code&gt;+{F15}&lt;/code&gt; is Shift and the F15 key for &lt;a href="https://ss64.com/vb/sendkeys.html" rel="noopener noreferrer"&gt;SendKeys&lt;/a&gt;. To reiterate, when this batch file is sent, it sees the entire batch script as just &lt;code&gt;0&amp;lt;0;&lt;/code&gt; then runs the last line, which is also valid JScript.&lt;/p&gt;

&lt;p&gt;The only interesting things about &lt;code&gt;set /a rand = (%RANDOM% * (%max% - %min% + 1) / 32768) + %min% + 1&lt;/code&gt; is that &lt;code&gt;%RANDOM%&lt;/code&gt; is a built-in variable that produces an integer from 0 to 32767 (inclusive), using &lt;code&gt;set /a&lt;/code&gt; always rounds down, and the last &lt;code&gt;+ 1&lt;/code&gt; is specifically for the following line; when using &lt;code&gt;sleep&lt;/code&gt; or &lt;code&gt;timeout&lt;/code&gt;, you would not add the extra 1.&lt;/p&gt;

&lt;p&gt;The next line, &lt;code&gt;ping -n %rand% -w 1 127.0.0.1 &amp;gt; NUL&lt;/code&gt;, is an old way to create a timer that's less processor intensive than &lt;a href="https://ss64.com/nt/sleep.html" rel="noopener noreferrer"&gt;sleep&lt;/a&gt; and &lt;a href="https://ss64.com/nt/timeout.html" rel="noopener noreferrer"&gt;timeout&lt;/a&gt;. You basically ping yourself &lt;code&gt;%rand%&lt;/code&gt; times with 1 second between each attempt; the +1 from the previous line is because there is no wait before the first ping. &lt;code&gt;&amp;gt; NUL&lt;/code&gt; redirects the standard output to NUL, which of course just discards it.&lt;/p&gt;

&lt;p&gt;If this script didn't have an infinite loop, you'd want to have a &lt;code&gt;goto :eof&lt;/code&gt; before &lt;code&gt;*/ 0;&lt;/code&gt; to make your batch script skip over the remainder of the file that is not valid script.&lt;/p&gt;

&lt;p&gt;And we finally get to the end and the actual JScript: &lt;code&gt;WScript.CreateObject("WScript.Shell").SendKeys(WScript.Arguments(0));&lt;/code&gt;. JScript is roughly equivalent to JavaScript 1.5/ECMAScript3 and has a WScript API roughly analogous to &lt;a href="https://ss64.com/vb/" rel="noopener noreferrer"&gt;VBScript&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Probably don't do things this way. But maybe you might want to, and now some of it makes sense.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>microsoft</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Naming Things</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Wed, 18 Sep 2019 23:27:09 +0000</pubDate>
      <link>https://dev.to/skhmt/naming-things-4fca</link>
      <guid>https://dev.to/skhmt/naming-things-4fca</guid>
      <description>&lt;p&gt;So I was implementing a &lt;code&gt;forEach&lt;/code&gt; method and asked my co-worker, "is there a name for a for-loop that iterates backwards? Something like:"&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;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;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;Of course, there wasn't one. So I set out to create one.&lt;/p&gt;

&lt;p&gt;Meet &lt;code&gt;rofEach&lt;/code&gt; and the &lt;code&gt;rof loop&lt;/code&gt;. It's not only the word "for" backwards, it also means "Reverse Order For".&lt;/p&gt;

&lt;p&gt;You're welcome, world. 🎤⬇️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>cpp</category>
      <category>productivity</category>
      <category>jokes</category>
    </item>
    <item>
      <title>Why are IndexedDB operations significantly slower in Chrome vs Firefox?</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Tue, 27 Aug 2019 23:10:59 +0000</pubDate>
      <link>https://dev.to/skhmt/why-are-indexeddb-operations-significantly-slower-in-chrome-vs-firefox-1bnd</link>
      <guid>https://dev.to/skhmt/why-are-indexeddb-operations-significantly-slower-in-chrome-vs-firefox-1bnd</guid>
      <description>&lt;p&gt;I was writing a simple key/value promise wrapper around IndexedDB, continuing a project I started a couple of years ago but stopped when &lt;a href="https://github.com/localForage/localForage" rel="noopener noreferrer"&gt;LocalForage&lt;/a&gt; released, since that does pretty much the same thing. But while running some benchmarks by &lt;a href="http://nolanlawson.github.io/database-comparison/" rel="noopener noreferrer"&gt;Nolan Lawson&lt;/a&gt;, I noticed a problem. Depending on the operation, Chrome is 2x to 7x slower than Firefox when working with IndexedDB. For a trivial insert (objectStore put() operation), it's a bit more than 2x slower. But more than that and it gets significantly worse.&lt;/p&gt;

&lt;p&gt;Running the test code I have has Firefox 68 at 170ms and 2800ms for a single .put() transaction and 10k .put() transactions. Running the same code in Chrome 76 is 430ms and 19,400ms. Yes, that's about 700% slower on Chrome when doing a lot of transactions. In Nolan Lawson's &lt;a href="http://nolanlawson.github.io/database-comparison/" rel="noopener noreferrer"&gt;database comparison&lt;/a&gt;, you can see that with the LocalForage and PouchDB (non-WebSQL) tests.&lt;/p&gt;

&lt;p&gt;This kind of matters because things like LocalForage don't combine many operations into a single transaction, meaning multiple database put/set operations will be a lot slower on Chrome than Firefox. But I'm not sure how often you'll be doing tons of inserts.&lt;/p&gt;

&lt;p&gt;Included is some code I wrote that you can paste into your browser's dev tools for a simple benchmark. I tried to isolate the test to just inserting 10,000 objects in one transaction vs inserting 10,000 objects in 10,000 transactions, but there are some other small things going on (e.g. casting &lt;code&gt;number&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt;, a &lt;code&gt;for&lt;/code&gt; loop, a function call, an array access, and an array &lt;code&gt;push()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;So... why? Is it actually just that Chrome is much slower with IndexedDB transactions, or is it something else? Is there ever a reason to do thousands of object inserts into a database all at once?&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;testRuns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;runSingleTX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testRuns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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="nf"&gt;runManyTX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testRuns&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runSingleTX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runs&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="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="k"&gt;async&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="p"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;init&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;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runs&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;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// benchmark start&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readwrite&lt;/span&gt;&lt;span class="dl"&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;objStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;objStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncomplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; objects inserted in a single transaction: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms`&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;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runManyTX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runs&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="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="k"&gt;async&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;init&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;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runs&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;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// benchmark start&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// doesn't matter THAT much, since "readwrite" transactions are basically synchronous&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&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;endTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; objects inserted one per transaction: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;endTime&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms`&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;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// a transaction for a single .put() operation&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readwrite&lt;/span&gt;&lt;span class="dl"&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;objStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;objStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncomplete&lt;/span&gt; &lt;span class="o"&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// utility to generate random data outside of benchmarking&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&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;random&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// utility to clear the database of all entries&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;database&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="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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readwrite&lt;/span&gt;&lt;span class="dl"&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;objStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;objStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncomplete&lt;/span&gt; &lt;span class="o"&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// open/create the database&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&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="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="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;indexedDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theDB&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;// create the db the first time&lt;/span&gt;
        &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onupgradeneeded&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncomplete&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onsuccess&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&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="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&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;



</description>
      <category>javascript</category>
      <category>indexeddb</category>
      <category>chrome</category>
      <category>firefox</category>
    </item>
    <item>
      <title>5 differences you might not have noticed between desktop and web UIs</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Thu, 15 Aug 2019 16:53:30 +0000</pubDate>
      <link>https://dev.to/skhmt/5-differences-you-might-not-have-noticed-between-desktop-and-web-uis-26gc</link>
      <guid>https://dev.to/skhmt/5-differences-you-might-not-have-noticed-between-desktop-and-web-uis-26gc</guid>
      <description>&lt;p&gt;If you're developing a desktop app in &lt;a href="https://electronjs.org/" rel="noopener noreferrer"&gt;Electron&lt;/a&gt;, &lt;a href="https://nwjs.io/" rel="noopener noreferrer"&gt;NW.js&lt;/a&gt;, or a similar tool, it's tempting to just create a "website for the desktop" and leave it at that. But there are a few different UI conventions that you may not have noticed between websites and desktop applications!&lt;/p&gt;

&lt;h2&gt;
  
  
  The cursor does not change to a pointer (finger) from the default (arrow) on buttons
&lt;/h2&gt;

&lt;p&gt;On websites, if you mouse over actionable elements, your cursor generally turns into a pointer. This includes both hyperlinks and buttons that control the UI. Interestingly, by default when using the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; tag, the cursor acts just as a desktop UI does and stays an arrow. But when using a stylized &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag that looks like a button, or perhaps by intentionally changing the behavior of a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, the cursor changes to pointer. &lt;/p&gt;

&lt;p&gt;On desktop applications the pointer is used exclusively for hyperlinks and not for the UI actions. For example, your desktop browser's bookmarks and buttons don't change your cursor into the finger pointer, it stays as the default arrow. Chrome's Dev Tools actually has inconsistent behavior for this - the &lt;code&gt;Elements&lt;/code&gt;, &lt;code&gt;Console&lt;/code&gt;, &lt;code&gt;Sources&lt;/code&gt;, &lt;code&gt;Network&lt;/code&gt;, etc tabs use a pointer cursor like a browser, but if you're in the &lt;code&gt;Elements&lt;/code&gt; tab and select an element, the second set of tabs for &lt;code&gt;Styles&lt;/code&gt;, &lt;code&gt;Event Listeners&lt;/code&gt;, &lt;code&gt;DOM Breakpoints&lt;/code&gt;, &lt;code&gt;Properties&lt;/code&gt;, etc follow the desktop convention of leaving your cursor as the default arrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can't highlight/select/copy any text
&lt;/h2&gt;

&lt;p&gt;On websites, mousing over any non-link will generally give you the text-selection (caret) cursor and allow you to select and copy that text.&lt;/p&gt;

&lt;p&gt;On desktop applications, you are generally not allowed to select text nor does your cursor change; text that is supposed to be selectable generally resides in a text field.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can't select nor drag images
&lt;/h2&gt;

&lt;p&gt;On websites, you can select and copy an image, or straight up drag the image around. An easy way to save an image, for example, is to just drag it from a site directly to your desktop.&lt;/p&gt;

&lt;p&gt;On desktop applications, images are handled like text; you can't interact with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Right clicking is a thing that people do on the desktop
&lt;/h2&gt;

&lt;p&gt;The right-click context menu may need to be handled in a desktop application, either by disabling right-click in most contexts or populating it with sensible actions. For example, Electron by default doesn't do anything when right-clicking, while right-clicking a text field &lt;em&gt;should&lt;/em&gt; allow you to cut/copy/paste/undo/select-all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be sure to catch random hotkeys that shouldn't work on desktop
&lt;/h2&gt;

&lt;p&gt;Does &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;r&lt;/code&gt; do something in your app that it shouldn't on a desktop? How about &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;-&lt;/code&gt;/&lt;code&gt;+&lt;/code&gt;? Does &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;w&lt;/code&gt; kill your program when it shouldn't on the desktop? Refreshing and zooming might be allowable behavior, but it also might not be, depending on your app. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>electron</category>
      <category>nwjs</category>
      <category>ux</category>
    </item>
    <item>
      <title>Horizontally and Vertically Centering in CSS</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Tue, 09 Jul 2019 19:44:59 +0000</pubDate>
      <link>https://dev.to/skhmt/horizontally-and-vertically-centering-in-css-2248</link>
      <guid>https://dev.to/skhmt/horizontally-and-vertically-centering-in-css-2248</guid>
      <description>&lt;p&gt;If you're anything like me, vertically and horizontally centering an HTML element has been the bane of your existence at some point in your career.&lt;/p&gt;

&lt;p&gt;Luckily, this solution works in &lt;a href="https://caniuse.com/#feat=flexbox" rel="noopener noreferrer"&gt;every browser, even IE 11&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;flex&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;justify-content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;center&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;align-items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;center&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;justify-content&lt;/code&gt; is for horizontal alignment by default. Removing it or setting &lt;code&gt;justify-content: flex-start;&lt;/code&gt; places the child element on the &lt;strong&gt;left&lt;/strong&gt;. Setting &lt;code&gt;justify-content: flex-end;&lt;/code&gt; places the child element on the &lt;strong&gt;right&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;align-items&lt;/code&gt; is for vertical alignment by default. Removing it or setting &lt;code&gt;align-items: flex-start;&lt;/code&gt; places the child element at the &lt;strong&gt;top&lt;/strong&gt;. Setting &lt;code&gt;align-items: flex-end;&lt;/code&gt; places the child element at the &lt;strong&gt;bottom&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's an &lt;a href="https://jsfiddle.net/zt8sb6qm/1/" rel="noopener noreferrer"&gt;example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can probably stop using &lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;absolute&lt;/code&gt; left/right and top/bottom positioning, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;line-height&lt;/code&gt;, &lt;code&gt;transform&lt;/code&gt;, and any other tricks :)&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Creating a native executable in Windows with GraalVM</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Wed, 12 Jun 2019 00:33:00 +0000</pubDate>
      <link>https://dev.to/skhmt/creating-a-native-executable-in-windows-with-graalvm-3g7f</link>
      <guid>https://dev.to/skhmt/creating-a-native-executable-in-windows-with-graalvm-3g7f</guid>
      <description>&lt;p&gt;&lt;a href="https://www.graalvm.org/" rel="noopener noreferrer"&gt;GraalVM&lt;/a&gt; is pretty awesome for &lt;a href="https://www.graalvm.org/docs/why-graal/" rel="noopener noreferrer"&gt;a lot of reasons&lt;/a&gt;. But the one that has me most hyped is the ability to create native executables from Java bytecode. This isn't like ExcelsiorJET (R.I.P.), which makes you include a large runtime, nor is it like &lt;a href="http://launch4j.sourceforge.net/" rel="noopener noreferrer"&gt;Launch4J&lt;/a&gt; and Oracle's &lt;code&gt;javapackager&lt;/code&gt; tool, both of which create a dummy executables that points to your &lt;code&gt;.jar&lt;/code&gt; and a packaged JRE.&lt;/p&gt;

&lt;p&gt;GraalVM makes &lt;em&gt;real&lt;/em&gt; native executables without a packaged runtime.&lt;/p&gt;

&lt;p&gt;Unfortunately, GraalVM's &lt;code&gt;native-image&lt;/code&gt;, and indeed its entire support for Windows, is in early adopter status. There are some error messages that aren't accurate, some bugs, and some features missing.&lt;/p&gt;

&lt;p&gt;But it does work, more-or-less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to know before you get started
&lt;/h2&gt;

&lt;p&gt;There's about a 8.5 MB minimum file size for a bare bones &lt;code&gt;hello world&lt;/code&gt; java application. That's kind of a lot, but also a lot less than including the entire JRE.&lt;/p&gt;

&lt;p&gt;Some other articles or guides recommend installing &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git for Windows&lt;/a&gt; and &lt;a href="https://www.python.org/downloads/release/python-2713/" rel="noopener noreferrer"&gt;Python 2.7&lt;/a&gt;, but I'm like 99% sure that's for building GraalVM before running. &lt;/p&gt;

&lt;p&gt;All the Swing and JavaFX applications I've tried haven't been able to build in the Windows version of &lt;code&gt;native-image&lt;/code&gt; in 19.0.0.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.graalvm.org/docs/reference-manual/aot-compilation/#image-generation-options" rel="noopener noreferrer"&gt;Image Generation Options&lt;/a&gt; page is a good resource for troubleshooting, you may have to place some of your classes into the &lt;code&gt;--initialize-at-run-time=&amp;lt;comma separated list of class/package names&amp;gt;&lt;/code&gt; option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to run GraalVM 19.0.0's &lt;code&gt;native-image&lt;/code&gt; in Windows
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Uninstall any &lt;code&gt;Visual C++ 2010 Redistributable&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;Get the Windows version of GraalVM 19.0.0: &lt;a href="https://github.com/oracle/graal/releases/tag/vm-19.0.0" rel="noopener noreferrer"&gt;https://github.com/oracle/graal/releases/tag/vm-19.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Extract it somewhere easy to find&lt;/li&gt;
&lt;li&gt;Get the Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO): &lt;a href="https://www.microsoft.com/en-us/download/details.aspx?id=8442" rel="noopener noreferrer"&gt;https://www.microsoft.com/en-us/download/details.aspx?id=8442&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mount the image, open &lt;code&gt;F:\Setup\SDKSetup.exe&lt;/code&gt; directly&lt;/li&gt;
&lt;li&gt;A default install should be perfect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the Windows SDK 7.1 Command Prompt&lt;/strong&gt; by going to &lt;code&gt;Start&lt;/code&gt; &amp;gt; &lt;code&gt;Microsoft Windows SDK v7.1&lt;/code&gt; &amp;gt; &lt;code&gt;Windows SDK 7.1 Command Prompt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run one of these commands based on if you have a &lt;code&gt;.jar&lt;/code&gt; or a &lt;code&gt;.class&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\path\to\graalvm-ce-19.0.0\bin\native-image -jar \path\to\helloworld.jar --no-fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\path\to\graalvm-ce-19.0.0\bin\native-image \path\to\helloworld.class --no-fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;*.exp&lt;/code&gt;, &lt;code&gt;*.lib&lt;/code&gt;, and &lt;code&gt;*.pdb&lt;/code&gt; files seem to be artifacts of the build process, they're not required for distribution.&lt;/p&gt;

&lt;p&gt;But that's it, your &lt;code&gt;.exe&lt;/code&gt; is made! &lt;/p&gt;

&lt;h2&gt;
  
  
  Update: As of GraalVM 19.3.0, still no support for Swing nor JavaFX.
&lt;/h2&gt;

&lt;p&gt;For GraalVM 19.3.0 on Windows with the new support for Java 11, you'll need &lt;code&gt;Visual Studio 2017 Community Edition&lt;/code&gt; instead of the &lt;code&gt;Microsoft Windows SDK for Windows 7&lt;/code&gt;, which you can &lt;a href="https://my.visualstudio.com/Downloads?q=visual%20studio%202017&amp;amp;wt.mc_id=o~msft~vscom~older-downloads" rel="noopener noreferrer"&gt;get here&lt;/a&gt; with a free Visual Studio Dev Essentials account. Instead of running the &lt;code&gt;Windows SDK 7.1 Command Prompt&lt;/code&gt;, you'll of course run &lt;code&gt;x64 Native Tools Command Prompt for VS 2017&lt;/code&gt; as your command line in &lt;code&gt;Start&lt;/code&gt; &amp;gt; &lt;code&gt;Visual Studio 2017&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gluon is &lt;a href="https://gluonhq.com/gluon-substrate-and-graalvm-native-image-with-javafx-support/" rel="noopener noreferrer"&gt;working on&lt;/a&gt; providing JavaFX support for 19.3.0 - it already has it for macOS and Linux, and Windows should be coming soon.&lt;/p&gt;

&lt;p&gt;Finally, if you do distribute an exe made with native-image on windows, you will need to include &lt;code&gt;vcruntime140.dll&lt;/code&gt; with it, which is about 84kb. &lt;/p&gt;

</description>
      <category>java</category>
      <category>graalvm</category>
      <category>windows</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Sending data through Mixed Content filters</title>
      <dc:creator>Mike 🐈‍⬛</dc:creator>
      <pubDate>Sat, 08 Jun 2019 16:50:40 +0000</pubDate>
      <link>https://dev.to/skhmt/sending-data-through-mixed-content-filters-3i0g</link>
      <guid>https://dev.to/skhmt/sending-data-through-mixed-content-filters-3i0g</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr / bottom line up front
&lt;/h2&gt;

&lt;p&gt;Using overt steganography (phanerography?), you can retrieve data on a cooperative HTTP server from an HTTPS hosted site while only triggering Mixed Passive/Display Content warnings on the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why tho?
&lt;/h2&gt;

&lt;p&gt;The most basic use case is including data from a server on the local network in a page hosted on the Internet via HTTPS. Local servers have trouble getting CA-issued HTTPS certificates because HTTPS certificates require a domain name, and local servers generally don't have one. But there still might be a use case for including data from a local server on a site hosted on the Internet, perhaps a configuration page for IoT devices.&lt;/p&gt;

&lt;p&gt;If you load non-secure data on an HTTPS page, one of two things could happen. If your content is in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;, or some &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; tags, it will be loaded as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content#Mixed_passivedisplay_content" rel="noopener noreferrer"&gt;Mixed Passive/Display Content&lt;/a&gt;. This means the site will lose its lock icon and the console will throw warnings about mixed content, but otherwise the site will work. However, if your content is loaded in any other way (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, &lt;code&gt;XMLHttpRequest&lt;/code&gt;, etc) your unsecure content will fail to load as it'll be considered &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content#Mixed_active_content" rel="noopener noreferrer"&gt;Mixed Active Content&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most IoT devices or other network appliances simply forgo HTTPS. Plex has &lt;a href="https://blog.filippo.io/how-plex-is-doing-https-for-all-its-users/" rel="noopener noreferrer"&gt;another solution&lt;/a&gt;, but it costs $5k-$10k USD per year. This article covers an alternative to those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Binary data transmission via portable network graphics
&lt;/h2&gt;

&lt;p&gt;Out of images, audio, and video, images are the easiest to programmatically create and have the lowest overhead. And of the more-or-less universally supported image formats, &lt;a href="https://www.w3.org/TR/PNG/" rel="noopener noreferrer"&gt;PNG&lt;/a&gt;s are ideal in that they have a grayscale mode where one byte is one pixel, they include gzip compression, they have a very low overhead, and they are not lossy.&lt;/p&gt;

&lt;p&gt;The overhead is a constant 66 bytes for up to 2 gigabytes of data, which means even without compression (which you could apply to both), it'll be more efficient than base64 for transmitting binary data bigger than about 200 bytes, at the cost of some cpu cycles.&lt;/p&gt;

&lt;h2&gt;
  
  
  The server (Kotlin/JVM)
&lt;/h2&gt;

&lt;p&gt;Let's start with the server. The server must receive all requests as an HTTP &lt;code&gt;GET&lt;/code&gt; request and so all options have to be in a query string or param string. How to do that is outside the scope of this article, but it's super easy.&lt;/p&gt;

&lt;p&gt;After it receives a request, it has to transform some data into a PNG, then return that to the requester.&lt;/p&gt;

&lt;p&gt;This manually creates a PNG file from a string - it could have been an array of bytes, but I wrote it as a string for this example. The output PNG is a single row with a width equal to the size of the input data and each pixel represents a byte in greyscale. The cover image for this article is "Hello World" run through this, but blown up a bunch so it's visible.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;*arrayName&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a pointer, it's the Kotlin spread operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;makePNG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dataAsByteArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Charsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UTF_8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// string (utf8) as a byte array&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pngSignature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="nf"&gt;pngIHDR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataAsByteArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&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="p"&gt;+&lt;/span&gt;
            &lt;span class="nf"&gt;pngIDAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataAsByteArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="nf"&gt;pngIEND&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PNG Signature - https://www.w3.org/TR/PNG/#5PNG-file-signature&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;pngSignature&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="mi"&gt;119&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PNG IHDR chunk - https://www.w3.org/TR/PNG/#11IHDR&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;pngIHDR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ihdrLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&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="mi"&gt;0&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="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ihdrType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ihdrData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;intToBA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// width&lt;/span&gt;
        &lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;intToBA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// height&lt;/span&gt;
        &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// bitdepth - 8 so each pixel is a byte&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// color type - 0 is greyscale&lt;/span&gt;
        &lt;span class="mi"&gt;0&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="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// compression, filter, and interlace methods - must be 0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ihdrCRC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCRC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ihdrType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ihdrData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ihdrLength&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;ihdrType&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;ihdrData&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;ihdrCRC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PNG IDAT chunk - https://www.w3.org/TR/PNG/#11IDAT&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;pngIDAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;idatType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;idatData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;byteArrayOf&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="p"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// filter type 0 (no filter)&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;idatCRC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCRC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idatType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idatData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;idatLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;intToBA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idatData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// compressed data length&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idatLength&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;idatType&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;idatData&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
            &lt;span class="n"&gt;idatCRC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PNG IEND chunk - https://www.w3.org/TR/PNG/#11IEND&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;pngIEND&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;byteArrayOf&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,-&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,-&lt;/span&gt;&lt;span class="mi"&gt;126&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;I know that was a lot, but out of all the code above, probably 95% of it is boilerplate to create a basic PNG. The IHDR is interesting, but only because it uses bitdepth &lt;code&gt;8&lt;/code&gt; and color type &lt;code&gt;0&lt;/code&gt; to allow exactly 1 byte per pixel. The rest of the chunks aren't anything special unless you're interested in the PNG file format and implementing it in the JVM.&lt;/p&gt;

&lt;p&gt;The convenience functions &lt;code&gt;getCRC()&lt;/code&gt;, &lt;code&gt;intToBA()&lt;/code&gt;, and &lt;code&gt;deflate()&lt;/code&gt; create a CRC using Java's CRC library, convert an integer into a byte array, and DEFLATE data using Java's Deflater library, respectively. They're included in the &lt;a href="https://gist.github.com/Skhmt/b5d1b4145b5d316ca74dee652b669e94" rel="noopener noreferrer"&gt;full server code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The site (javascript)
&lt;/h2&gt;

&lt;p&gt;The website hosted on HTTPS needs to solve two problems, the first is to send data along with the request to an HTTP server and then get that data.&lt;/p&gt;

&lt;p&gt;It sends data via a query string because of course the data communication has to go through an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. This limits the request data to 1KB according to most browser restrictions.&lt;/p&gt;

&lt;p&gt;The second problem is getting the data. The server solves the problem by sending a png that essentially wraps and DEFLATEs the data, but now the browser has to make some sense of it. It does so by drawing the &lt;code&gt;img&lt;/code&gt; onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element, then reading each pixel's red value (red, green, and blue are all the same in a greyscale image) and pushing that into an array:&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;function&lt;/span&gt; &lt;span class="nf"&gt;pngGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&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;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crossOrigin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anonymous&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;
        &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&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="mi"&gt;0&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;utf8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&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;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&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;byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getImageData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&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="mi"&gt;1&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="nx"&gt;data&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;utf8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stringFromUTF8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could be improved by cleaning up the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements this creates in the DOM, but in this example it's actually &lt;a href="//skhmt.github.io/https.html"&gt;outputting it&lt;/a&gt; for the user to see. There are also some optimizations both here and in the server code that could be done (e.g. pre-allocating the &lt;code&gt;utf8&lt;/code&gt; array's length).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;stringFromUTF8Array()&lt;/code&gt; function used above was written by &lt;a href="https://weblog.rogueamoeba.com/2017/02/27/javascript-correctly-converting-a-byte-array-to-a-utf-8-string/" rel="noopener noreferrer"&gt;Ed Wynne&lt;/a&gt;. I didn't modify it at all for this example. Of course if you wanted to transfer binary data, you wouldn't need to translate the byte array into a string.&lt;/p&gt;

&lt;h3&gt;
  
  
  A huge caveat with this implementation
&lt;/h3&gt;

&lt;p&gt;The provided code only allows the creation of a 2,147,483,647 pixel wide PNG file with a single row, which has a problem... while it's technically allowed by the PNG spec, programs like Photoshop only allow 300,000 x 300,000 pixel images while  Chrome and Firefox have a max &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; width of 32,767 pixels. So even if more rows than one were implemented, it'd &lt;em&gt;only&lt;/em&gt; allow about 1 gigabyte per PNG. It shouldn't be a difficult fix, but this is only a proof of concept so it wasn't implemented in the code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Regaining confidentiality and data integrity
&lt;/h2&gt;

&lt;p&gt;The major problem with this is that it lacks confidentiality and data integrity. In other words, people sniffing your network traffic via unsecured Wi-Fi or Man-In-The-Middle can theoretically read and/or change the image containing your data. This is a problem with all Mixed Passive/Display Content.&lt;/p&gt;

&lt;p&gt;One way to solve this is to roll your own encryption/decryption via something like &lt;a href="https://github.com/asmcrypto/asmcrypto.js" rel="noopener noreferrer"&gt;asmCrypto.js&lt;/a&gt; or the &lt;a href="https://github.com/bitwiseshiftleft/sjcl" rel="noopener noreferrer"&gt;Stanford JS Crypto Library&lt;/a&gt;. You can then encrypt the response data via the normal Java crypto libraries and decrypt the response after reading the bytes from the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;. You'd have to pass the key in a side channel, with both the HTTPS site/server and HTTP server talking to a HTTPS server to post the key in a database. That HTTPS server+db could also be hosting the HTTPS website.&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%2Fvofljjx4vtbgll0czg14.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%2Fvofljjx4vtbgll0czg14.png" width="610" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing remarks
&lt;/h2&gt;

&lt;p&gt;In the future browsers may throw more blatant warnings for Mixed Passive/Display Content, or they may start treating such content as Mixed Active Content and just block it outright. Essentially, this is a work-around that may not exist forever. We at &lt;a href="https://www.gameglass.gg/" rel="noopener noreferrer"&gt;GameGlass&lt;/a&gt; have decided against implementing it in production for that reason.&lt;/p&gt;

&lt;p&gt;This may have applications in sending large amounts of data because it's a bit better than base64, but why not just send the binary directly instead of wrapping it in a .png? &lt;/p&gt;

&lt;p&gt;This may also have an application in exfiltrating data from a compromised machine, but I can't think of any situation in which this would be the preferred solution over any of the more established methods, including just sending the binary data.&lt;/p&gt;

&lt;p&gt;This could be used for obfuscating a payload I guess, but that'd last about as long as it takes for someone to read this article.&lt;/p&gt;

&lt;p&gt;But even if it's not super useful nor that ground breaking, I think it's pretty neat. Thanks to &lt;a href="https://www.linkedin.com/in/ronkarroll" rel="noopener noreferrer"&gt;Ron Karroll&lt;/a&gt; and the rest of the guys at GameGlass for letting me bounce ideas off their heads!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>kotlin</category>
      <category>steganography</category>
      <category>png</category>
    </item>
  </channel>
</rss>
