<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Eli Bendersky's website - JavaScript</title><link href="https://eli.thegreenplace.net/" rel="alternate"></link><link href="https://eli.thegreenplace.net/feeds/javascript.atom.xml" rel="self"></link><id>https://eli.thegreenplace.net/</id><updated>2025-06-17T13:06:48-07:00</updated><entry><title>Teaching coding with JavaScript and p5.js</title><link href="https://eli.thegreenplace.net/2025/teaching-coding-with-javascript-and-p5js/" rel="alternate"></link><published>2025-05-10T08:57:00-07:00</published><updated>2025-05-10T15:57:46-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2025-05-10:/2025/teaching-coding-with-javascript-and-p5js/</id><summary type="html">&lt;p&gt;When asked which programming language to learn first - especially for kids - my
usual answer is JavaScript &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Nothing beats the direct feedback you get
from code that's able to paint things on the screen, without having to install
anything.&lt;/p&gt;
&lt;p&gt;One library that makes it a particularly pleasant process is &lt;a class="reference external" href="https://p5js.org/"&gt;p5 …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;When asked which programming language to learn first - especially for kids - my
usual answer is JavaScript &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Nothing beats the direct feedback you get
from code that's able to paint things on the screen, without having to install
anything.&lt;/p&gt;
&lt;p&gt;One library that makes it a particularly pleasant process is &lt;a class="reference external" href="https://p5js.org/"&gt;p5.js&lt;/a&gt;, which was created specifically for this educational
purpose. I've had good experience teaching kids basic programming using p5.js.
Here's a simple example of what I mean:&lt;/p&gt;
&lt;canvas class="align-center" id="canvas2"&gt;&lt;/canvas&gt;
&lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/p5.min.js"&gt;&lt;/script&gt;
&lt;script src="https://eli.thegreenplace.net/js/p5-circles/circles.js"&gt;
&lt;/script&gt;&lt;p&gt;(You should see some circles moving on the canvas; click on the canvas to add
more)&lt;/p&gt;
&lt;p&gt;Even though this demo is so trivial, it has many of the elements of creating
simple games - colorful stuff is drawn on the screen, things are moving around
according to simple physical laws, and there's interactivity!&lt;/p&gt;
&lt;p&gt;Here's the entire code required to implement this with p5.js:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;circles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;createCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;circles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randomCircleAtPos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;240&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;circles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xSpeed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ySpeed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Bounce off the walls&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xSpeed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ySpeed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&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="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mousePressed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;circles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randomCircleAtPos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouseX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouseY&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;randomCircleAtPos&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;xSpeed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ySpeed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are many niceties provided by p5.js here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;No need to write any HTML! The &lt;tt class="docutils literal"&gt;createCanvas&lt;/tt&gt; call will create a canvas
element and all subsequent drawing and interaction happens on it &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;setup&lt;/tt&gt; is a &amp;quot;magic&amp;quot; function that gets invoked once at the beginning of
the program.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;draw&lt;/tt&gt; is another magic function that gets automatically called for
every animation frame (p5.js arranges the correct &lt;tt class="docutils literal"&gt;requestAnimationFrame&lt;/tt&gt;
calls behind the scenes).&lt;/li&gt;
&lt;li&gt;There are many useful helper functions for drawing, without having to deal
with the HTML canvas API, e.g. &lt;tt class="docutils literal"&gt;background&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;fill&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;circle&lt;/tt&gt;. In
particular, there's no need to deal with &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath"&gt;canvas contexts or paths&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;mousePressed&lt;/tt&gt;  is yet another magic function that is called on mouse
clicks within the canvas; &lt;tt class="docutils literal"&gt;mouseX&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;mouseY&lt;/tt&gt; are magic globals that
specify the mouse location within the canvas
(without having to worry about client position offsets, etc.)&lt;/li&gt;
&lt;li&gt;Utility functions like &lt;tt class="docutils literal"&gt;color&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;random&lt;/tt&gt; have convenient, simple APIs.
&lt;tt class="docutils literal"&gt;p5.js&lt;/tt&gt; has many others, specifically tuned for developing simulations and
games. There are &lt;a class="reference external" href="https://p5js.org/reference/p5/p5.Vector/"&gt;vectors&lt;/a&gt;,
utilities for &lt;a class="reference external" href="https://p5js.org/reference/p5/noise/"&gt;smooth random noise&lt;/a&gt;, functions for
&lt;a class="reference external" href="https://p5js.org/reference/p5/map/"&gt;mapping between linear ranges&lt;/a&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also wrote a version of the same animation without p5.js, using plain JS and
canvas APIs instead. It's available &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2025/p5-moving-circles"&gt;on GitHub&lt;/a&gt; - feel free to compare!&lt;/p&gt;
&lt;div class="section" id="some-history"&gt;
&lt;h2&gt;Some history&lt;/h2&gt;
&lt;p&gt;It all started with &lt;a class="reference external" href="https://processing.org/"&gt;Processing&lt;/a&gt;, a 25-year-old Java
library designed to teach people how to code by creating animations and games
(and conversely, giving artists and animators simple tools to enhance their work
with code).
In 2007, John Resig (of jQuery fame) developed &lt;a class="reference external" href="https://github.com/processing-js/processing-js"&gt;Processing.js&lt;/a&gt; - a JS clone
of Processing; Khan Academy started using Processing.js for a programming unit on their website.&lt;/p&gt;
&lt;p&gt;p5.js was created in 2013; here's a brief history from their GitHub repository:&lt;/p&gt;
&lt;blockquote&gt;
p5.js was created by &lt;a class="reference external" href="https://github.com/lmccart"&gt;Lauren Lee McCarthy&lt;/a&gt;
in 2013 as a new interpretation of
Processing for the context of the web. Since then we have allowed ourselves
space to deviate and grow, while drawing inspiration from Processing and our
shared community. p5.js is sustained by a community of contributors, with
support from the Processing Foundation.&lt;/blockquote&gt;
&lt;p&gt;As a result of p5's growth in popularity, Processing.js has been archived
a few years ago and new users are directed to p5.js.&lt;/p&gt;
&lt;p&gt;While p5.js has a very similar &lt;em&gt;feel&lt;/em&gt; to the original Java Processing library,
I strongly recommend the former. With the ubiquity of the web these days, there's
really no reason to use the Java variant with all the complexity of installation
and running separate tools it requires. The browser is all you need!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="educational-resources"&gt;
&lt;h2&gt;Educational resources&lt;/h2&gt;
&lt;p&gt;At the time of writing, Khan Academy's &lt;a class="reference external" href="https://www.khanacademy.org/computing/computer-programming"&gt;Computer Programming course&lt;/a&gt; (starting at unit 4)
still uses Processing.js, but it's similar enough to p5.js that I recommend
ignoring the difference and just doing it - it's a great resource.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://thecodingtrain.com/"&gt;The Coding Train&lt;/a&gt; is another fantastic resource
that uses p5.js directly to teach programming for beginners in a friendly and
engaging style. If you prefer a book format with more advanced material, check
out &lt;a class="reference external" href="https://natureofcode.com/"&gt;Nature of Code&lt;/a&gt; from the same author.&lt;/p&gt;
&lt;p&gt;Finally, p5.js comes with its own &lt;a class="reference external" href="https://editor.p5js.org/"&gt;online editor&lt;/a&gt;,
where you can create an arbitrary number of projects (each with multiple files,
if you want), and have a live preview of everything in the browser.&lt;/p&gt;
&lt;img alt="p5.js web editor screenshot" class="align-center" src="https://eli.thegreenplace.net/images/2025/p5-editor.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="using-p5-js-for-professional-programming"&gt;
&lt;h2&gt;Using p5.js for professional programming?&lt;/h2&gt;
&lt;p&gt;OK, so p5.js is a great resource for beginners learning to write code. Is it
recommended for professional programming, though? Should you incorporate p5.js
in your frontend work?&lt;/p&gt;
&lt;p&gt;This depends, but in general I'd recommend against it. At the end of the day,
it all comes down to the
&lt;a class="reference external" href="https://eli.thegreenplace.net/2017/benefits-of-dependencies-in-software-projects-as-a-function-of-effort/"&gt;benefits of dependencies as a function of effort&lt;/a&gt;.
p5.js has a very wide and shallow API - if you're already a seasoned programmer
familiar with JS, the additional functionality p5.js provides is fairly trivial
&lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. Sooner or later you'll find yourself at odds with p5.js's abstraction or
implementation of some concept and will start looking for a way out. For
experienced programmers, the raw canvas API isn't that bad to deal with, so the
biggest benefits of p5.js dissipate rather quickly.&lt;/p&gt;
&lt;p&gt;That said, if you want to hack together a quick game or simulation and p5.js
makes your life easier - why not! Just remember the benefit vs. effort curve.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This sometimes raises eyebrows for experienced programmers, because
JavaScript has a certain reputation. My view is that none of this matters
for beginners - they typically don't care about our pedantic nuances,
and for them JS is as good as any other language. But the &lt;em&gt;environment&lt;/em&gt;
in which JS executes is the real boon. Imagine you're a kid with a
Chromebook; to create a simple game with JS, you don't have to install
&lt;em&gt;anything&lt;/em&gt;. Just open any web-based JS IDE (e.g. CodePen, JSFiddle, or
- better yet - p5.js's &lt;a class="reference external" href="https://editor.p5js.org/"&gt;own online editor&lt;/a&gt;)
and start coding. With some honest copy pasting, you can go from blank
screen to colorful objects moving around and interacting with your mouse
in less than a minute.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you follow the source of my animation on this page, you'll
notice I'm cheating a bit - I &lt;em&gt;do&lt;/em&gt; need to create an explicit canvas
element in HTML because I have to properly embed it within this blog
post. But for demos where all you have on the screen is that canvas
- this isn't needed. Beginning programmers can completely ignore the
existence of HTML and CSS when starting with p5.js!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;As an example of what I mean, &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2025/p5-moving-circles/p5min"&gt;here's p5min&lt;/a&gt; - a minimal clone of
p5.js sufficient to run the circles demo shown earlier in this post.
It's not hard to keep extending it gradually to implement additional
functionality, as needed.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="JavaScript"></category></entry><entry><title>Notes on running Go in the browser with WebAssembly</title><link href="https://eli.thegreenplace.net/2024/notes-on-running-go-in-the-browser-with-webassembly/" rel="alternate"></link><published>2024-09-14T06:05:00-07:00</published><updated>2025-03-02T01:03:51-08:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2024-09-14:/2024/notes-on-running-go-in-the-browser-with-webassembly/</id><summary type="html">&lt;p&gt;Recently I've had to compile Go to WebAssembly to run in the browser in a couple
of small projects (&lt;a class="reference external" href="https://eliben.github.io/go-sudoku/"&gt;#1&lt;/a&gt;,
&lt;a class="reference external" href="https://eliben.github.io/go-sentencepiece/"&gt;#2&lt;/a&gt;), and in general spent some
time &lt;a class="reference external" href="https://eli.thegreenplace.net/tag/webassembly"&gt;looking at WebAssembly&lt;/a&gt;.
I find WebAssembly to be an exciting technology, both for the web and for
other uses (e.g. with WASI …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently I've had to compile Go to WebAssembly to run in the browser in a couple
of small projects (&lt;a class="reference external" href="https://eliben.github.io/go-sudoku/"&gt;#1&lt;/a&gt;,
&lt;a class="reference external" href="https://eliben.github.io/go-sentencepiece/"&gt;#2&lt;/a&gt;), and in general spent some
time &lt;a class="reference external" href="https://eli.thegreenplace.net/tag/webassembly"&gt;looking at WebAssembly&lt;/a&gt;.
I find WebAssembly to be an exciting technology, both for the web and for
other uses (e.g. with WASI); specifically, it's pretty great that we can take
existing projects and components written in Go and run them in the browser.&lt;/p&gt;
&lt;p&gt;In this post, I will summarize some useful patterns in running Go in the browser
via WebAssembly. All the patterns are demonstrated by small, self-contained
programs you can find in &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook"&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="basics-calling-go-from-js"&gt;
&lt;h2&gt;Basics: calling Go from JS&lt;/h2&gt;
&lt;p&gt;This sample serves as the basis for other samples in this post: let's
write a Go function that we'll call in the browser using JS. This function
uses Go's &lt;tt class="docutils literal"&gt;math/big&lt;/tt&gt; stdlib package to calculate the sum of the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Harmonic_series_(mathematics)"&gt;harmonic series&lt;/a&gt;
for some duration &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, and returns the result with high precision:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// calcHarmonic calculates the harmonic series for approximately the given&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;// number of seconds and returns the accumulated result in a string.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nsecs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nsecs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;big&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewRat&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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;addend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;big&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewRat&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="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int64&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="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &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;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FloatString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To export this function to JS in the browser, we add the following
code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Export the name &amp;quot;calcHarmonic&amp;quot; to JS, with our wrapper as value&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;calcHarmonic&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsCalcHarmonic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// The Go main function compiled to WASM is expected to block&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// indefinitely.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// wrap calcHarmonic to be callable from JS&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jsCalcHarmonic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;want one argument&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&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;Float&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This Go file is compiled to the WASM/js target with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GOOS=js GOARCH=wasm go build -o harmonic.wasm harmonic.go
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And load it from JS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Instantiate a new Go object (defined in from wasm_exec.js)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;harmonic.wasm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&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="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;img alt="Shows the UI of our &amp;quot;calculate harmonic sum&amp;quot; demo" class="align-center" src="https://eli.thegreenplace.net/images/2024/calc-harmonic-ui2.png" /&gt;
&lt;p&gt;The JS code that calls &lt;tt class="docutils literal"&gt;calcHarmonic&lt;/tt&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submitButton&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submitButton&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;timeInput&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;outputDiv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, the &lt;tt class="docutils literal"&gt;wasm_exec.js&lt;/tt&gt; file from the Go distribution has to be included
with something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wasm_exec.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The easiest way to obtain this file is download it from the Go project's GitHub
mirror (for the same Go version your Go code is compiled with); this is handled
by the Makefile in our sample project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;wasm_exec.js:
  wget https://raw.githubusercontent.com/golang/go/release-branch.go1.24/lib/wasm/wasm_exec.js
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the basic recipe for invoking Go from JS in the browser: the Go code
is platform-agnostic and presents some API and all the glue logic is done in JS.
The next samples show some variations on this basic scheme.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/basic-call-go-from-js"&gt;Link to the full code for this sample&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="dom-manipulation-from-go"&gt;
&lt;h2&gt;DOM manipulation from Go&lt;/h2&gt;
&lt;p&gt;In the previous example, Go implemented the &lt;tt class="docutils literal"&gt;calcHarmonic&lt;/tt&gt; function, but the
rest of the program's logic was in JS - setting up an event listener for a
button click, updating output, etc.&lt;/p&gt;
&lt;p&gt;We can move more of the code to Go, if we want. The &lt;tt class="docutils literal"&gt;calcHarmonic&lt;/tt&gt; remains
unchanged, but our &lt;tt class="docutils literal"&gt;main&lt;/tt&gt; function in Go becomes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;document&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;getElementById&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submitButton&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;inputElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;getElementById&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;timeInput&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;outputElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;getElementById&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;outputDiv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;addEventListener&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inputElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;inputFloat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strconv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;outputElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;innerText&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We obtain JS values from the &lt;tt class="docutils literal"&gt;js.Global()&lt;/tt&gt; context and can call functions
or set attributes on them. If you squint, this looks very similar to JS code,
but written in Go-ish.&lt;/p&gt;
&lt;p&gt;This code sample demonstrates some useful capabilities of DOM manipulation in Go:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Adding event listeners on DOM elements, with Go callbacks&lt;/li&gt;
&lt;li&gt;Getting values from DOM elements&lt;/li&gt;
&lt;li&gt;Setting attributes on DOM elements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only code JS remaining in our &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; is the WebAssembly loader:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;harmonic.wasm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&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="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All the rest is done in Go! &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/dom-in-go"&gt;Link to the full code for this sample&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a more full-featured sample, check out &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/go-canvas-gameoflife"&gt;this directory&lt;/a&gt;.
It implements a simple Game of Life running in the browser, entirely in Go. All
the game logic, canvas manipulation and event management is done in Go; here
too, the only JS code in the project is the few lines used to load the
WebAssembly module.&lt;/p&gt;
&lt;img alt="Game of Life screenshot" class="align-center" src="https://eli.thegreenplace.net/images/2024/gameoflife-go-wasm.png" /&gt;
&lt;p&gt;I personally prefer keeping the UI logic in JS, but if you're interested in
Go purity all the way - it's definitely feasible.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="using-tinygo-as-an-alternative-compiler"&gt;
&lt;h2&gt;Using TinyGo as an alternative compiler&lt;/h2&gt;
&lt;p&gt;The Go compiler's support for WebAssembly is pretty good these days, but there's
a small snag that may be important to users: the entire Go runtime is compiled
into the WASM binary. On my machine, the &lt;tt class="docutils literal"&gt;.wasm&lt;/tt&gt; files produced for the
sample Go code weigh in at around 2.5 MiB, which will take some time to load
in the browser - especially on slow connections &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There's an alternative: &lt;a class="reference external" href="https://tinygo.org/"&gt;TinyGo&lt;/a&gt; is a Go toolchain
&amp;quot;for small places&amp;quot;, specializing in embedded controllers; the same
considerations apply to WASM. The TinyGo runtime is lightweight compared
to Go, and the binaries are about 1/4 the size. Not everything is perfect with
TinyGo, though: compilation is much slower, and the resulting code is a bit
slower as well. Finally, TinyGo has &lt;a class="reference external" href="https://tinygo.org/docs/reference/lang-support/stdlib/"&gt;some limitations&lt;/a&gt;
that make stdlib packages that rely on reflection not work; this can be painful
when interacting with JS because &lt;tt class="docutils literal"&gt;encoding/json&lt;/tt&gt; relies on reflection - so
you may need to look for an alternative JSON package.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/dom-in-go"&gt;dom-in-go sample directory&lt;/a&gt;
also shows how to build the project with TinyGo; take a look at the Makefile.
Note that TinyGo has its own &lt;tt class="docutils literal"&gt;wasm_exec.js&lt;/tt&gt; support file - it won't work with
the one taken from the standard Go distribution; the Makefile handles this too.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="keeping-the-main-thread-free-webassembly-in-a-web-worker"&gt;
&lt;h2&gt;Keeping the main thread free: WebAssembly in a web worker&lt;/h2&gt;
&lt;p&gt;If we come back to the original sample and run the calculation for some
non-trivial amount of time (say, 2 seconds or more) - you may notice something:
the page appears &amp;quot;frozen&amp;quot; while the calculation is running. You can't interact
with the UI in any way, can't select text with the mouse; if you try to add
periodic &lt;tt class="docutils literal"&gt;console.log&lt;/tt&gt; printouts or some spinner animation - nothing will show
until &lt;tt class="docutils literal"&gt;calcHarmonic&lt;/tt&gt; returns with the result.&lt;/p&gt;
&lt;p&gt;This is the expected behavior for JS when it calls a blocking, CPU-intensive
function! Let's revisit the code again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submitButton&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;submitButton&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;timeInput&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="hll"&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;outputDiv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The highlighted line will block the main thread for 2+ seconds, but the main
thread in JS is also used for all the UI interaction. This is one of the
most common manifestations of &lt;a class="reference external" href="https://eli.thegreenplace.net/2018/go-hits-the-concurrency-nail-right-on-the-head/"&gt;function coloring problem&lt;/a&gt; - blocking
is problematic. Luckily, all modern browsers support &lt;em&gt;Web Workers&lt;/em&gt; - isolated
threads that can execute concurrently.&lt;/p&gt;
&lt;p&gt;It's not hard to make web workers work with WebAssembly, which is what our
next demo shows. The main HTML file includes, in addition to the UI logic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;worker.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`worker.log: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;result&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;resultReady&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;console&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="sb"&gt;`Unknown action: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where &lt;tt class="docutils literal"&gt;worker.js&lt;/tt&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;wasm_exec.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Worker is running&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Load the WASM module with Go code.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;harmonic.wasm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&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="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Worker loaded WASM module&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;console&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="s2"&gt;&amp;quot;Worker failed to load WASM module: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="nx"&gt;onmessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`Worker received message &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;calculate&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;calcHarmonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;result&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`unknown action &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;&amp;#39;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(The Go code remains unchanged.)&lt;/p&gt;
&lt;p&gt;We see that the worker does the WebAssembly loading now, meaning that the
Go code executes in a separate thread and the UI thread is free to run while
the computation is ongoing. This sample adds a spinner that animates until
the web worker returns &lt;tt class="docutils literal"&gt;calcHarmonic&lt;/tt&gt;'s answer, to show the effect.&lt;/p&gt;
&lt;img alt="Shows the UI of our &amp;quot;calculate harmonic sum&amp;quot; demo with a spinner" class="align-center" src="https://eli.thegreenplace.net/images/2024/calc-harmonic-spinner.png" /&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/go-in-web-worker"&gt;Link to the full code for this sample&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="talking-on-a-web-socket-with-go"&gt;
&lt;h2&gt;Talking on a Web Socket with Go&lt;/h2&gt;
&lt;p&gt;A few years ago I &lt;a class="reference external" href="https://eli.thegreenplace.net/2016/go-websocket-server-sample/"&gt;published a sample&lt;/a&gt;
of a Go server talking via web sockets with JavaScript client code. Well, since
the theme here is porting all client code to Go, how about we replace that
JavaScript client with yet more Go?&lt;/p&gt;
&lt;p&gt;This turns out to be fairly simple - not much different from the &amp;quot;DOM
manipulation in Go&amp;quot; section, in fact. But there are some nuances I want
to cover.&lt;/p&gt;
&lt;p&gt;The application is simple - we display a box, and whenever there's mouse
movement over the box, the client sends messages to the server via a web socket;
the server echoes the message back and the client uses it to update a text div:&lt;/p&gt;
&lt;img alt="Screenshot of wasm websocket sample" class="align-center" src="https://eli.thegreenplace.net/images/2024/wasm-websocket-go-screen.png" /&gt;
&lt;p&gt;The server code is standard Go using the &lt;tt class="docutils literal"&gt;golang.org/x/net/websocket&lt;/tt&gt;
package. On the client, however, we have to use browser APIs. Here's the
interesting part of the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wsServerAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ws://127.0.0.1:4050&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// These are equivalent to the following in JS:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;//   ws = new WebSocket(addr) ...&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;wsCtor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;WebSocket&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;wsEcho&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wsCtor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wsServerAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/wsecho&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="nx"&gt;wsTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wsCtor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wsServerAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/wstime&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To send on a web socket, we'll use this function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// wsSend sends a message on a web socket; the web socket must be active and&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;// open (otherwise wsSends logs an error and doesn&amp;#39;t send anything).&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;// The message will be serialized to JSON prior to sending.&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wsSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IsNull&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;readyState&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;WebSocket&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;OPEN&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;send&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;socket is not open&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here's how receiving looks, registering the &lt;tt class="docutils literal"&gt;message&lt;/tt&gt; event listener:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;wsEcho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;addEventListener&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FuncOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;args&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="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;coordMsg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Coordinates: (%v, %v)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ev&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="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;outputElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;innerText&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;coordMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As before, this is just straightforward translation of JS into Go &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. Note
something interesting that's going on here: we have two different Go programs,
talking over web sockets with each other using completely different underlying
libraries. One uses a Go-native implementation of web sockets; the other uses
the browser implementation, exposed via a JS API. In a realistic program, it
would make sense to abstract over these details so the same code could be used
to send/receive data over web sockets, whether it runs on the server or the
client.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/go-wasm-websockets"&gt;Link to the full code for this sample&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="testing-locally-with-node-js"&gt;
&lt;h2&gt;Testing locally with Node.js&lt;/h2&gt;
&lt;p&gt;This section isn't strictly about &amp;quot;running in the browser&amp;quot;, but it covers the
important topic of local testing. Sometimes we don't want the browser in the
loop for our tests; well, good news - we can leverage Node.js's ability to
load and execute WebAssembly modules to run &lt;tt class="docutils literal"&gt;GOOS=js GOARCH=wasm&lt;/tt&gt; Go
binaries locally!&lt;/p&gt;
&lt;img alt="Node.js logo" class="align-center" src="https://eli.thegreenplace.net/images/2024/nodejs-logo.png" style="width: 200px;" /&gt;
&lt;p&gt;The intersting tidbit here is that we can leverage special support implemented
in the Go toolchain to make these invocations similar to running/testing
regular Go programs.
Here's an excerpt from &lt;tt class="docutils literal"&gt;go help run&lt;/tt&gt; describing it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;By default, &amp;#39;go run&amp;#39; runs the compiled binary directly: &amp;#39;a.out arguments...&amp;#39;.
If the -exec flag is given, &amp;#39;go run&amp;#39; invokes the binary using xprog:
  &amp;#39;xprog a.out arguments...&amp;#39;.
If the -exec flag is not given, GOOS or GOARCH is different from the system
default, and a program named go_$GOOS_$GOARCH_exec can be found
on the current search path, &amp;#39;go run&amp;#39; invokes the binary using that program,
for example &amp;#39;go_js_wasm_exec a.out arguments...&amp;#39;. This allows execution of
cross-compiled programs when a simulator or other execution method is
available.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;Makefile&lt;/tt&gt; in our sample handles this fully; we can run a test like
this locally, without opening the browser:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;//go:build js &amp;amp;&amp;amp; wasm&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;syscall/js&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;testing&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestJSArr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello from test in js/wasm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;objs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;eval&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`({&lt;/span&gt;
&lt;span class="s"&gt;arr: [41,42,43],&lt;/span&gt;
&lt;span class="s"&gt;})`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;arr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;got %#v, want %#v&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;arr&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;got %#v, want %#v&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With an invocation like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;GOOS=js GOARCH=wasm go test -exec=supportfiles/go_js_wasm_exec -v .
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2024/go-wasm-js-cookbook/local-test-with-node"&gt;Link to the full code for this sample&lt;/a&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The harmonic series is known to diverge, but &lt;em&gt;very slowly&lt;/em&gt;. You need
over 200 million elements to get to the sum of 20, etc.
(see &lt;a class="reference external" href="https://oeis.org/A004080"&gt;A004080&lt;/a&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;There are some additional mitigations we can explore, like compressing
the WASM binary. This is outside the scope of this post, and it applies
to the TinyGo output as well.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;To be honest, this makes me appreciate JS as an extension language. It
has such a simple ABI! Everything is an object, and we can get/set
object properties (which can be other objects), and call
functions/methods - that's all we need to access all of the browser
APIs.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Go"></category><category term="WebAssembly"></category><category term="JavaScript"></category></entry><entry><title>Asking an LLM to build a simple web tool</title><link href="https://eli.thegreenplace.net/2024/asking-an-llm-to-build-a-simple-web-tool/" rel="alternate"></link><published>2024-07-09T20:09:00-07:00</published><updated>2024-09-14T13:15:30-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2024-07-09:/2024/asking-an-llm-to-build-a-simple-web-tool/</id><summary type="html">&lt;p&gt;I've been really enjoying following &lt;a class="reference external" href="https://simonwillison.net/"&gt;Simon Willison's blog posts&lt;/a&gt; recently. Simon shows other
programmers the way LLMs will be used for code assistance in the future, and
posts full interactions with LLMs to build small tools or parts of larger
applications.&lt;/p&gt;
&lt;p&gt;A &lt;a class="reference external" href="https://simonwillison.net/2024/Jul/8/box-shadow-css-generator/"&gt;recent post&lt;/a&gt;
caught my attention; here Simon got …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been really enjoying following &lt;a class="reference external" href="https://simonwillison.net/"&gt;Simon Willison's blog posts&lt;/a&gt; recently. Simon shows other
programmers the way LLMs will be used for code assistance in the future, and
posts full interactions with LLMs to build small tools or parts of larger
applications.&lt;/p&gt;
&lt;p&gt;A &lt;a class="reference external" href="https://simonwillison.net/2024/Jul/8/box-shadow-css-generator/"&gt;recent post&lt;/a&gt;
caught my attention; here Simon got an LLM (Claude 3.5 Sonnet in this case) to
build a complete tool that lets one configure/tweak box shadow settings
and copy the resulting CSS code for use in a real application. One thing that
seemed interesting is that the LLM in this case used some heavyweight
dependencies (React + JSX) to implement this; Almost 3 MiB of dependency for
something that clearly needs only a few dozen lines of HTML + JS to implement;
yikes.&lt;/p&gt;
&lt;p&gt;So I've decided to try my own experiment and get an LLM to do this without any
dependencies. It turned out to be very easy, because the LLM I used (in this
case ChatGPT 4o, but it could really have been any of the top-tier LLMs, I
think) opted for the no-dependency approach from the start. I was preparing to
ask it to adjust the code to remove dependencies, but this turned out to be
unnecessary.&lt;/p&gt;
&lt;p&gt;The resulting tool is very similar to Simon's in functionality; it's deployed
at &lt;a class="reference external" href="https://eliben.org/box-shadow-tool/"&gt;https://eliben.org/box-shadow-tool/&lt;/a&gt;; here's a screenshot:&lt;/p&gt;
&lt;img alt="Screenshot of box shadow tool" class="align-center" src="https://eli.thegreenplace.net/images/2024/box-shadow-screenshot.png" /&gt;
&lt;p&gt;Here are my prompts:&lt;/p&gt;
&lt;blockquote&gt;
CSS for a slight box shadow, build me a tool that helps me twiddle settings
and preview them and copy and paste out the CSS&lt;/blockquote&gt;
&lt;p&gt;ChatGPT produced a working tool but it didn't really look good on the page.&lt;/p&gt;
&lt;blockquote&gt;
Yes, make the tool itself look a bit better with some CSS so it's all centered
on the screen and there's enough space for the preview box&lt;/blockquote&gt;
&lt;p&gt;It still wasn't quite what I wanted.&lt;/p&gt;
&lt;blockquote&gt;
the container has to be wider so all the text and sliders fix nicely, and
there's still not enough space for the shadows of the preview box to show
without overlapping with other elements&lt;/blockquote&gt;
&lt;p&gt;Now it was looking better; I wanted a button to copy-paste, like in Simon's
demo:&lt;/p&gt;
&lt;blockquote&gt;
this looks better; now add a nice-looking button at the bottom that copies the
resulting css code to the clipboard&lt;/blockquote&gt;
&lt;p&gt;The code ChatGPT produced for the clipboard copy operation was flagged by
vscode as deprecated, so I asked:&lt;/p&gt;
&lt;blockquote&gt;
it seems like &amp;quot;document.execCommand('copy')&amp;quot; is deprecated; is there a more
accepted way to do this?&lt;/blockquote&gt;
&lt;p&gt;The final version can be seen in the &lt;a class="reference external" href="https://eliben.org/box-shadow-tool/"&gt;online demo&lt;/a&gt; (view-source). The complete ChatGPT
transcript is &lt;a class="reference external" href="https://chatgpt.com/share/a05935cb-3e6d-4f5d-997f-5d8541450d1c"&gt;available here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="insights"&gt;
&lt;h2&gt;Insights&lt;/h2&gt;
&lt;p&gt;Overall, this was a positive experience. While a tool like this is very simple
to implement manually, doing it with an LLM was even quicker. The results are
still not perfect in terms of alignment and space, but they're good enough. At
this point one would probably just take over and do the final tweaks manually.&lt;/p&gt;
&lt;p&gt;I was pleasantly surprised by how stable the LLM managed to keep its output
throughout the interaction; it only modified the parts I asked it to, and the
rest of the code remained identical. Stability has been an issue with LLMs
(particularly for images), and I'm happy to see it holds well for code (there
could be some special tuning or prompt engineering for ChatGPT to make this work
well).&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Machine Learning"></category><category term="JavaScript"></category></entry><entry><title>ES Module imports in Node.js and the browser</title><link href="https://eli.thegreenplace.net/2023/es-module-imports-in-nodejs-and-the-browser/" rel="alternate"></link><published>2023-10-23T20:58:00-07:00</published><updated>2024-09-14T13:15:30-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2023-10-23:/2023/es-module-imports-in-nodejs-and-the-browser/</id><summary type="html">&lt;p&gt;For a &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/cubic-spline-interpolation/"&gt;recent project&lt;/a&gt;, I wanted to
have some JS code (in multiple files) available for testing from the command-line
with Node.js, but also to be able to load the same code into a web page to be
invoked directly from a browser.&lt;/p&gt;
&lt;p&gt;I've encountered this same issue before …&lt;/p&gt;</summary><content type="html">&lt;p&gt;For a &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/cubic-spline-interpolation/"&gt;recent project&lt;/a&gt;, I wanted to
have some JS code (in multiple files) available for testing from the command-line
with Node.js, but also to be able to load the same code into a web page to be
invoked directly from a browser.&lt;/p&gt;
&lt;p&gt;I've encountered this same issue before for my &lt;a class="reference external" href="https://eliben.org/js8080/"&gt;in-browser 8080 assembler and
simulator project&lt;/a&gt;, and used a combination of
CommonJS &lt;tt class="docutils literal"&gt;require&lt;/tt&gt;s with a &lt;a class="reference external" href="https://browserify.org/"&gt;bundler tool&lt;/a&gt; to make it work. But
we're in 2023 now, and CommonJS is supposed to be phasing out. So my goal for
the new project was to do this using ES modules (ESM) and without any separate
tooling.&lt;/p&gt;
&lt;p&gt;Let's see how it works.&lt;/p&gt;
&lt;div class="section" id="project-structure"&gt;
&lt;h2&gt;Project structure&lt;/h2&gt;
&lt;p&gt;Here's the structure of the &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2023/js-gauss-spline"&gt;js-gauss-spline&lt;/a&gt; project
serving as our demo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ tree
.
├── eqsolve.js
├── package.json
├── plot.html
├── README.md
├── spline.js
└── test
    └── test.js
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The files &lt;tt class="docutils literal"&gt;eqsolve.js&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;spline.js&lt;/tt&gt; implement the functionality we want
to both test on the command-line and import in the browser.
Their functionality is exposed via &lt;tt class="docutils literal"&gt;export&lt;/tt&gt;ed functions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="testing-in-node-js"&gt;
&lt;h2&gt;Testing in Node.js&lt;/h2&gt;
&lt;p&gt;The test code lives in &lt;tt class="docutils literal"&gt;test/test.js&lt;/tt&gt;, and it starts like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;node:assert/strict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;solve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../eqsolve.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buildSplineEquations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../spline.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exported functions we want to test are imported with relative paths. The
file also imports Node's built-in &lt;tt class="docutils literal"&gt;assert&lt;/tt&gt; functionality; it uses &lt;tt class="docutils literal"&gt;assert&lt;/tt&gt;s
directly, without any unit-testing framework.&lt;/p&gt;
&lt;p&gt;To run the tests, simply invoke &lt;tt class="docutils literal"&gt;node&lt;/tt&gt; (which has to be a recent version
that properly supports ES modules):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ node --version
v20.5.0
$ node test/test.js
success
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="running-in-the-browser"&gt;
&lt;h2&gt;Running in the browser&lt;/h2&gt;
&lt;p&gt;So far so good. Now let's import these files into a web application running in the
browser; in our project, the main entry point is &lt;tt class="docutils literal"&gt;plot.html&lt;/tt&gt;. It has more
custom JS code, along with whatever HTML elements are needed. Here's the
JS part that imports functions from &lt;tt class="docutils literal"&gt;eqsolve.js&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;spline.js&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://cdn.jsdelivr.net/npm/d3@7/+esm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buildSplineEquations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;./spline.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;solve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;./eqsolve.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ... more web-app JS code here&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules"&gt;According to MDN&lt;/a&gt;, browsers have supported
ES modules for quite a while now, so if you have a reasonably recent browser it should
support &lt;tt class="docutils literal"&gt;script &lt;span class="pre"&gt;type=&amp;quot;module&amp;quot;&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;import&lt;/tt&gt; statements.&lt;/p&gt;
&lt;p&gt;Note that our code here imports an additional JS library using ESM - &lt;tt class="docutils literal"&gt;d3&lt;/tt&gt;,
directly from a URL.&lt;/p&gt;
&lt;p&gt;When testing this web page locally, opening it as a file with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;file:///&lt;/span&gt;&lt;/tt&gt;
scheme won't work; you'll get CORS errors in the browser console, because
&lt;tt class="docutils literal"&gt;import&lt;/tt&gt;ing local files from the page is not supported. We'll need to serve
the directory locally using a file server.&lt;/p&gt;
&lt;p&gt;Luckily this is very easy to do with my &lt;a class="reference external" href="https://github.com/eliben/static-server/"&gt;static-server project&lt;/a&gt; &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ static-server .
2023/10/21 07:07:44.168573 Serving directory &amp;quot;.&amp;quot; on http://127.0.0.1:8080
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this server running, opening &lt;a class="reference external" href="http://127.0.0.1:8080/plot.html"&gt;http://127.0.0.1:8080/plot.html&lt;/a&gt; should
successfully load everything.&lt;/p&gt;
&lt;p&gt;Direct support of JS imports in the browser is a big step forward for the
ecosystem; now it's easy to properly structure non-trivial web applications
without requiring a separate build step with external tooling.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you don't have &lt;tt class="docutils literal"&gt;go&lt;/tt&gt; installed,
the NPM &lt;a class="reference external" href="https://www.npmjs.com/package/http-server"&gt;http-server&lt;/a&gt;
package will work just as well.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="JavaScript"></category></entry><entry><title>Cubic spline interpolation</title><link href="https://eli.thegreenplace.net/2023/cubic-spline-interpolation/" rel="alternate"></link><published>2023-10-12T05:57:00-07:00</published><updated>2025-06-17T13:06:48-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2023-10-12:/2023/cubic-spline-interpolation/</id><summary type="html">&lt;p&gt;This post explains how cubic spline interpolation works, and presents a full
implementation in JavaScript, hooked up to a SVG-based visualization.
As a side effect, it also covers Gaussian elimination and presents a JavaScript
implementation of that as well.&lt;/p&gt;
&lt;p&gt;I love topics that mix math and programming in a meaningful …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This post explains how cubic spline interpolation works, and presents a full
implementation in JavaScript, hooked up to a SVG-based visualization.
As a side effect, it also covers Gaussian elimination and presents a JavaScript
implementation of that as well.&lt;/p&gt;
&lt;p&gt;I love topics that mix math and programming in a meaningful way, and cubic
spline interpolation is an excellent example of such a topic. There's a bunch
of linear algebra here and some calculus, all connected with code to create
a useful tool.&lt;/p&gt;
&lt;div class="section" id="motivation"&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;In an &lt;em&gt;interpolation&lt;/em&gt; problem, we're given a set of points (we'll be using
2D points &lt;em&gt;X,Y&lt;/em&gt; throughout this post) and are asked to estimate Y values for
Xs not in this original set, specifically for Xs that lie between Xs of the
original set (estimation for Xs outside the bounds of the original set
is called &lt;em&gt;extrapolation&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;As a concrete example, consider the set of points (0, 1), (1, 3), (2, 2); here
they are plotted in the usual coordinate system:&lt;/p&gt;
&lt;img alt="Three points on a 2D plot" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-3points.png" /&gt;
&lt;p&gt;Interpolation is estimating the value of Y for Xs between 0 and 2, given just
this data set. Obviously, the more complex the underlying function/phenomenon,
and the fewer original points we're given, interpolation becomes more difficult
to do accurately.&lt;/p&gt;
&lt;p&gt;There are many techniques to interpolate between a given set of points.
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Polynomial_interpolation"&gt;Polynomial interpolation&lt;/a&gt; can perfectly fit N
points with an N-1 degree polynomial, but this approach can be problematic for
large a N; high-degree polynomials tend to overfit their data, and suffer from
other numerical issues like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Runge's_phenomenon"&gt;Runge's phenomenon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Instead of interpolating all the points with a single function, a very popular
alternative is using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Spline_(mathematics)"&gt;Splines&lt;/a&gt;, which are piece-wise
polynomials. The idea is to fit a low-degree polynomial between every pair of
adjacent points in the original data set; for N points, we get N-1 different
polynomials. The simplest and best known variant of this technique is linear
interpolation:&lt;/p&gt;
&lt;img alt="Three points on a 2D plot with linear interpolation connecting them" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-linear.png" /&gt;
&lt;p&gt;Linear interpolation has clear benefits: it's very fast, and when N is large
it produces reasonable results. However, for small Ns the result isn't great,
and the approximation is very crude. Here's the linear spline interpolation of
the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Sinc_function"&gt;Sinc function&lt;/a&gt; sampled
at 7 points:&lt;/p&gt;
&lt;img alt="Sinc function with linear interpolation" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-sinc-linear.png" /&gt;
&lt;p&gt;We can certainly do much better.&lt;/p&gt;
&lt;p&gt;How about higher-degree splines? We can try second degree polynomials, but it's
better to jump straight to cubic (third degree). Here's why: to make our
interpolation realistic and aesthetically pleasing, we want the neighboring
polynomials not only to touch at the original points (the linear splines already
do this), but to actually look like they're part of the same curve. For this
purpose, we want the &lt;em&gt;slope&lt;/em&gt; of the polynomials to be continuous, meaning that
if two polynomials meet at point P, their first derivatives at this point are
equal. Moreover, to ensure smoothness and to minimize needless bending &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, we
also want the second derivatives of the two polynomials to be equal at P. The
lowest degree of polynomial that gives us this level of control is 3 (since the
second derivative of a quadratic polynomial is constant); hence cubic splines.&lt;/p&gt;
&lt;p&gt;Here's a cubic spline interpolating between the three points of the original
example:&lt;/p&gt;
&lt;img alt="Three points on a 2D plot with cubic spline interpolation connecting them" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-cubic.png" /&gt;
&lt;p&gt;And the &lt;em&gt;Sinc&lt;/em&gt; function:&lt;/p&gt;
&lt;img alt="Sinc function with cubic spline interpolation connecting them" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-sinc-cubic.png" /&gt;
&lt;p&gt;Because of the continuity of first and second derivatives, cubic splines look
very natural; on the other hand, since the degree of each polynomial remains
at most 3, they don't overfit too much. Hence they're such a popular tool for
interpolation and design/graphics.&lt;/p&gt;
&lt;p&gt;All the plots in this post have been produced by &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2023/js-gauss-spline"&gt;JavaScript code&lt;/a&gt;
that implements cubic spline interpolation from scratch. Let's move on to learn
how it works.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="setting-up-equations-for-cubic-spline-interpolation"&gt;
&lt;h2&gt;Setting up equations for cubic spline interpolation&lt;/h2&gt;
&lt;p&gt;Given a set of N points, we want to produce N-1 cubic polynomials between these
points. While these are distinct polynomials, they are connected through mutual
constraints on the original points, as we'll see soon.&lt;/p&gt;
&lt;p&gt;More formally, we're going to define N-1 polynomials in the inclusive range
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/9b9b2d7bb5787b4e4c6c897fdedd72630c5bb16a.svg" style="height: 19px;" type="image/svg+xml"&gt;i \in\{0 ...N-2\}&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/2ac0b93cd5715f7041d230d1d057cd4797bdb458.svg" style="height: 22px;" type="image/svg+xml"&gt;\[p_i(x)=a_ix^3+b_ix^2+c_ix+d_i\]&lt;/object&gt;
&lt;p&gt;For each polynomial, we have to find 4 coefficients: &lt;em&gt;a&lt;/em&gt;, &lt;em&gt;b&lt;/em&gt;, &lt;em&gt;c&lt;/em&gt; and &lt;em&gt;d&lt;/em&gt;;
in total, for N-1 polynomials we'll need 4N-4 coefficients. We're going to
find these coefficients by expressing the constraints we have as linear
equations, and then solving a system of linear equations. We'll need 4N-4
equations to ensure we can find a unique solution for 4N-4 unknowns.&lt;/p&gt;
&lt;p&gt;Let's use our sample set of three original points to demonstrate how this
calculation works: (0, 1), (1, 3), (2, 2). Since N is 3, we'll be looking for
two polynomials and a total of 8 coefficients.&lt;/p&gt;
&lt;p&gt;The first set of constraints is obvious - each polynomial has to pass through
the two points it's interpolating between. The first polynomial passes through
the points (0, 1) and (1, 3), so we can write the equations:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/00090cc844b4a8b06c6ed084b0a814a86c13c148.svg" style="height: 46px;" type="image/svg+xml"&gt;\[\begin{align*}
p_0(0)&amp;amp;=0a_0 + 0b_0 + 0c_0 + d_0=1\\
p_0(1)&amp;amp;=a_0+b_0+c_0+d_0=3
 \end{align*}\]&lt;/object&gt;
&lt;p&gt;The second polynomial passes through the points (1, 3) and (2, 2), resulting
in the equations:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/20edf7b504d79094c9fdcabe836664bf10b42fc6.svg" style="height: 46px;" type="image/svg+xml"&gt;\[\begin{align*}
p_1(1)&amp;amp;=a_1+b_1+c_1+d_1=3\\
p_1(2)&amp;amp;=8a_1 + 4b_1 + 2c_1 + d_1=2
 \end{align*}\]&lt;/object&gt;
&lt;p&gt;We have 4 equations, and need 4 more.&lt;/p&gt;
&lt;p&gt;We constrain the first and second derivatives of the polynomials to be equal at
the points where they meet. In our example, there are only two polynomials that
meet at a single point, so we'll get two equations: their derivatives are equal
at point (1, 3).&lt;/p&gt;
&lt;p&gt;Recall that the first and second derivatives of a cubic polynomial are:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/d26d85730cd1985330ab76d27cb48bb03a9278ae.svg" style="height: 48px;" type="image/svg+xml"&gt;\[\begin{align*}
p_i&amp;#x27;(x)&amp;amp;=3a_ix^2+2b_ix+c_i\\
p_i&amp;#x27;&amp;#x27;(x)&amp;amp;=6a_ix+2b_i
 \end{align*}\]&lt;/object&gt;
&lt;p&gt;The equation we get from equating the first derivatives is:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/49c0538e2adf8ce5aae6a0e1fcfb48e9d8ccb946.svg" style="height: 21px;" type="image/svg+xml"&gt;\[p_0&amp;#x27;(1)=3a_0+2b_0+c_0=p_1&amp;#x27;(1)=3a_1+2b_1+c_1\]&lt;/object&gt;
&lt;p&gt;Or, expressed as a linear equation of all coefficients:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/d6d310b5eebfe69c6b3adc5f8fb5c33758077afe.svg" style="height: 14px;" type="image/svg+xml"&gt;\[3a_0+2b_0+c_0-3a_1-2b_1-c_1=0\]&lt;/object&gt;
&lt;p&gt;Similarly, the equation we get from equating the second derivatives is:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/a492f1e4effa7a1669ceb33c135920dec84b2b5f.svg" style="height: 21px;" type="image/svg+xml"&gt;\[p_0&amp;#x27;&amp;#x27;(1)=6a_0+2b_0=p_1&amp;#x27;&amp;#x27;(1)=6a_1+2b_1\]&lt;/object&gt;
&lt;p&gt;Expressed as a linear equation of all coefficients:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/1307f100d5e17b3226f1a6561fca8409d8206185.svg" style="height: 14px;" type="image/svg+xml"&gt;\[6a_0+2b_0-6a_1-2b_1=0\]&lt;/object&gt;
&lt;p&gt;This brings us to a total of 6 equations. The last two equations will come from
&lt;em&gt;boundary conditions&lt;/em&gt;. Notice that - so far - we didn't say much about how our
interpolating polynomials behave at the end points, except that they pass
through them. Boundary conditions are constraints we create to define how our
polynomials behave at these end points.
There are several approaches to this,
but here we'll just discuss the most commonly-used one: a &lt;em&gt;natural&lt;/em&gt; spline.
Mathematically it says that the first polynomial has a second derivative of 0
at the first original point, and the last polynomial has a second derivative of
0 at the last original point. In our example:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/378d9e0421bc85fe06ade8ad93ffd620e47274db.svg" style="height: 48px;" type="image/svg+xml"&gt;\[\begin{align*}
p_0&amp;#x27;&amp;#x27;(0)=0\\
p_1&amp;#x27;&amp;#x27;(2)=0
\end{align*}\]&lt;/object&gt;
&lt;p&gt;Substituting the second derivative equations:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/6cc066ad9122be577dacad8a36b8e7ae1ce387b3.svg" style="height: 48px;" type="image/svg+xml"&gt;\[\begin{align*}
p_0&amp;#x27;&amp;#x27;(0)&amp;amp;=2b_0=0\\
p_1&amp;#x27;&amp;#x27;(2)&amp;amp;=12a_1+2b_1=0
\end{align*}\]&lt;/object&gt;
&lt;p&gt;We have 8 equations now:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/722d3e11b3ddfc89fe4c6d9c7c1db0aba2ade7be.svg" style="height: 203px;" type="image/svg+xml"&gt;\[\begin{align*}
d_0&amp;amp;=1\\
a_0+b_0+c_0+d_0&amp;amp;=3\\
a_1+b_1+c_1+d_1&amp;amp;=3\\
8a_1 + 4b_1 + 2c_1 + d_1&amp;amp;=2\\
3a_0+2b_0+c_0-3a_1-2b_1-c_1&amp;amp;=0\\
6a_0+2b_0-6a_1-2b_1&amp;amp;=0\\
2b_0&amp;amp;=0\\
12a_1+2b_1&amp;amp;=0
\end{align*}\]&lt;/object&gt;
&lt;p&gt;To restate the obvious - while our example only uses 2 polynomials, this
approach generalizes to any number. For N original points, we'll interpolate
with N-1 polynomials, resulting in 4N-4 coefficients. We'll get:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;2N-2 equations from setting the points these polynomials pass through&lt;/li&gt;
&lt;li&gt;N-2 equations from equating first derivatives at internal points&lt;/li&gt;
&lt;li&gt;N-2 equations from equating second derivatives at internal points&lt;/li&gt;
&lt;li&gt;2 equations from boundary conditions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a total of 4N-4 equations.&lt;/p&gt;
&lt;p&gt;The code that constructs these equations from a given set of points is available
&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/blob/main/2023/js-gauss-spline/spline.js"&gt;in this file&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="solving-the-equations"&gt;
&lt;h2&gt;Solving the equations&lt;/h2&gt;
&lt;p&gt;We now have 8 equations with 8 variables. Some of them are trivial, so it's
tempting to just solve the system by hand, and indeed one can do it very easily.
In the general case, however, it would be quite difficult - imagine
interpolating 10 polynomials resulting in 36 equations!&lt;/p&gt;
&lt;p&gt;Fortunately, the full power of linear algebra is now at our disposal. We can
express this set of linear equations as a matrix multiplication problem
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/e7d3683a610f89a991289fc2c2c64ba38eb6a004.svg" style="height: 13px;" type="image/svg+xml"&gt;Ax=b&lt;/object&gt;, where &lt;em&gt;A&lt;/em&gt; is a matrix of coefficients, &lt;em&gt;x&lt;/em&gt; is a vector of
unknowns and &lt;em&gt;b&lt;/em&gt; is the vector of right-hand side constants:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/5243a06db4c849ae927dff870e2db1f3ab2c217d.svg" style="height: 170px;" type="image/svg+xml"&gt;\[Ax=b\Rightarrow \begin{pmatrix}
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 1 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0\\
1 &amp;amp; 1 &amp;amp; 1 &amp;amp; 1 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0\\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 1 &amp;amp; 1 &amp;amp; 1 &amp;amp; 1\\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 8 &amp;amp; 4 &amp;amp; 2 &amp;amp; 1\\
3 &amp;amp; 2 &amp;amp; 1 &amp;amp; 0 &amp;amp; -3 &amp;amp; -2 &amp;amp; -1 &amp;amp; 0\\
6 &amp;amp; 2 &amp;amp; 0 &amp;amp; 0 &amp;amp; -6 &amp;amp; -2 &amp;amp; 0 &amp;amp; 0\\
0 &amp;amp; 2 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0\\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 12 &amp;amp; 2 &amp;amp; 0 &amp;amp; 0\\
\end{pmatrix}\begin{pmatrix}
a_0 \\
b_0 \\
c_0 \\
d_0 \\
a_1 \\
b_1 \\
c_1 \\
d_1\end{pmatrix}=\begin{pmatrix}
1\\
3\\
3\\
2\\
0\\
0\\
0\\
0
\end{pmatrix}\]&lt;/object&gt;
&lt;p&gt;Solving this system is straightforward using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Gaussian_elimination"&gt;Gaussian elimination&lt;/a&gt;.
&lt;a class="reference external" href="https://github.com/eliben/code-for-blog/blob/main/2023/js-gauss-spline/eqsolve.js"&gt;Our JavaScript implementation&lt;/a&gt;
does this in a few steps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Performs Gaussian elimination to bring &lt;em&gt;A&lt;/em&gt; into row-echelon form, using the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Gaussian_elimination#Pseudocode"&gt;algorithm outlined on Wikipedia&lt;/a&gt;. This
approach tries to preserve numerical stability by selecting the row with the
largest (in absolute value) value for each column &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Further transforms the resulting matrix into &lt;em&gt;reduced&lt;/em&gt; row-echelon form
(a.k.a. Gauss-Jordan elimination)&lt;/li&gt;
&lt;li&gt;Extracts the solution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our example, the solution ends up being the vector (-0.75, 0, 2.75, 1, 0.75,
-4.5, 7.25, -0.5); therefore, the interpolating polynomials are:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/04c71afa0281eb99662ce3d99d4c4546ef08af89.svg" style="height: 50px;" type="image/svg+xml"&gt;\[\begin{align*}
p_0(x)&amp;amp;=-0.75x^3+2.75x+1\\
p_1(x)&amp;amp;=0.75x^3-4.5x^2+7.25x-0.5
\end{align*}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="performing-the-interpolation-itself"&gt;
&lt;h2&gt;Performing the interpolation itself&lt;/h2&gt;
&lt;p&gt;Now that we have the interpolating polynomials, we can generate any number of
interpolated points. For all &lt;em&gt;x&lt;/em&gt; between 0 and 1 we use &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/ad5cb52cf88277ad5a1880722c8ae8b3a6edfd42.svg" style="height: 19px;" type="image/svg+xml"&gt;p_0(x)&lt;/object&gt;,
and for &lt;em&gt;x&lt;/em&gt; between 1 and 2 we use &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/7c2338e3575da884f060665a36a3503d970957a5.svg" style="height: 19px;" type="image/svg+xml"&gt;p_1(x)&lt;/object&gt;. In our JavaScript
code this is done by the &lt;tt class="docutils literal"&gt;doInterpolate&lt;/tt&gt; function. We've already seen
the result:&lt;/p&gt;
&lt;img alt="Three points on a 2D plot with cubic spline interpolation connecting them" class="align-center" src="https://eli.thegreenplace.net/images/2023/interp-cubic.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="code"&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;p&gt;The complete code sample for this post &lt;a class="reference external" href="https://github.com/eliben/code-for-blog/tree/main/2023/js-gauss-spline"&gt;is available on GitHub&lt;/a&gt;.
It includes functions for constructing equations for cubic splines from an
original set of points, code for solving linear equations with Gauss-Jordan
elimination, and a demo HTML page that plots the points and linear/spline
interpolations.&lt;/p&gt;
&lt;p&gt;The code is readable, heavily-commented JavaScript with no dependencies (except
D3 for the plotting).&lt;/p&gt;
&lt;p&gt;An additional demo that uses similar functionality is &lt;a class="reference external" href="https://eliben.github.io/line-plotting/"&gt;line-plotting&lt;/a&gt;; it plots arbitrary mathematical
functions with optional interpolation (when the number of sampled points is
low).&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This requirement actually has neat historical roots. In the days before
computers, &amp;quot;splines&amp;quot; were elastic rulers engineers and drafters would
use to interpolate between points by hand. These rulers would bend and
connect at the original points, and it was considered best practice to
minimize bending.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This helps avoid division by very small numbers, which may cause issues
when using finite-precision floating point.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Math"></category><category term="JavaScript"></category></entry></feed>