Jekyll2022-01-26T14:43:25-05:00/feed.xmlMy ramblingsA young adult's adventures with technologyAndroid gaming on a budget2022-01-02T00:00:00-05:002022-01-02T00:00:00-05:00/2022/01/02/1KJmlJNzpjYQs_hZo8crSgksTkwdfLcdteO6YVhsFPsk<p>I’ve never really been much of a PC gamer - growing up, I mostly played
on various Nintendo consoles. I recall trying to build a PC back in
2014, and giving up due to how expensive it was (I was still a student
at the time). Somewhat ironically, the build that I specced out at the
time would cost about the same today, due to the GPU market being what
it is. Phones at the time weren’t really powerful enough to do nearly as
much as they can today - I recall playing a few Gameboy games on an
emulator for my Nexus 5, along with Angry Birds.</p>
<p>Recently, I thought I would try playing <a href="https://www.google.com/url?q=https://genshin.mihoyo.com/en/&sa=D&source=editors&ust=1641167502445000&usg=AOvVaw3Y5DpumKnQ-RIf5sReIdyG">Genshin
Impact</a>
. I tried running it on the fastest hardware I currently own (a
refurbished gaming laptop from 2015) but I was unable to get over 12 or
so FPS even on the lowest settings. That machine has an i7 4720HQ, 16GB
of DDR3 ram, and an Nvidia 860m. I figured I would try and run it on my
current phone (Redmi Note 5), as Genshin is also on Android.
Unfortunately, my phone isn’t supported, and even if it were I highly
doubt it would get anything close to playable on a four year old budget
phone.</p>
<p>I considered buying a console as well - the market for a Switch OLED or
a PS5 however seems to be dominated by scalpers, so I figured that
wouldn’t be an option. Trying to get into gaming during a pandemic after
chip shortages have become common isn’t easy.</p>
<p>I’ve also been getting more and more interested in emulation of the
Nintendo consoles I’ve owned - specifically 3DS, DS, and the Wii. To my
surprise, on Youtube folks were able to run Wii games just as fast as
the original, on Android phones. I figured I’d buy a phone just for
playing games, without upgrading my current phone. Below are a list of
requirements and notes that I used to decide on a phone to buy.</p>
<h1 id="requirements">Requirements</h1>
<ol>
<li>Cheap. We’re talking < $150 on a phone, so it’ll likely have to be
used or refurbished</li>
<li>Okay battery life. Since I won’t be using it as a phone, I’m looking
for > 5 hours, roughly on par with a Nintendo Switch. If the phone
is used, I can also expect the battery to not hold a full charge.</li>
<li>A decently sized screen, around 6 inches diagonally.</li>
<li>Minimum resolution of 1920 x 1080 for general crispness of text.</li>
<li>Bonus points for an OLED panel</li>
<li>Able to play Genshin Impact on high settings at 60fps. Able to
emulate PS2, Wii, and anything less powerful.</li>
<li>Able to do video out over USB C, so that I can dock it to a monitor.</li>
</ol>
<p>That’s quite a few requirements, I know. Thankfully, a couple of them
narrow down the available choices a lot. Most of the Chinese phone
manufacturers do not do video over USB C, so I excluded companies like
Xiaomi, Redmi, Oppo, and Realme.</p>
<p>Point 6 is the most interesting one. To emulate many titles, a
Snapdragon processor is preferred as it tends to get the best
performance for emulators. If you have a Mediatek or Exynos CPU you
often need to download and run a patched emulator (such as Citra MMJ
instead of Citra) for playable frame rates. More specifically for Point
6 though, it seems that a Snapdragon 845 and up would be able to get me
the performance I’d want.</p>
<p>Point 1 also cut out many other options. A Samsung Galaxy S10, a three
year old phone, still goes for $200-$300 used. There are a few other
tricks that one can use to find a cheaper phone. One is to add “bad
imei” or just “imei” to the search. These are phones that have been
blacklisted by a cellular provider, so while they don’t function as a
cellular device they still can work over Wifi for gaming. Of course, for
a phone to be blacklisted, the owner either lost the phone, had it
stolen, or didn’t pay the cellular company. A second trick is to try and
buy a “demo” unit phone. This can be a hit or a miss - demo phones can
be an entirely different model, missing a cellular connection, having
reduced storage, or other caveats. I’d only recommend this if you’re
able to find others who’ve reported on the status of demo phones.</p>
<p>Eventually, after digging into the flagship phones that had launched in
2018 and 2019 I was able to at least settle on a company: LG.</p>
<p>LG made some very standard phones that would usually have a gimmick to
encourage the buyer to purchase the phone. And yes, I said made . LG has
closed their smartphone division, which is part of the reason their
flagships can be found on sites like eBay for cheap, around $120. In
the end, I was deciding between the LG G8 and the LG G8x. The G8’s
gimmick is being able to control certain things in the phone with your
hand, and the G8x’s gimmick is a case that includes a second flip out
display. Both can be found for around the same price on eBay and have
largely similar specs as far as performance goes. The G8 is lighter and
slightly smaller, while the G8x has a lower display resolution.</p>
<p>In the end I picked the G8 purely because it was lighter, and ordered
it. I’ve had the phone for a couple of days now, and I’m pretty happy.
It met all of my requirements, and cost me $130 (after tax). There
weren’t any with a bad IMEI that were worth it, and the demo units seem
to have a quirk where only a few GB of the 128GB is made available to
the user, so I skipped out on those.</p>
<p>Additionally, I picked up one of those telescoping bluetooth controllers
that fit many phones, and look a bit like a Nintendo Switch. The
specific model I used is a Saitake 7007F1, but there are many. If you
care a lot about latency there is also the Razer Kishi, a more expensive
option. I wanted something I could still use with stuff other than a
phone though, so I stuck with bluetooth.</p>
<p>Here’s a quick photo of the device.</p>
<p><img src="https://lh6.googleusercontent.com/wor-OEGUeOvOoKaOuWVgky1HwQwnz-LI8avV9K3TojjCe7739mXXy5L7Kw2hBzwaBReN9gJ4LLfsqhPMg5jwnDdL7XcHDxb2Ve-QdgqILTDLSSwbP0aviwt9_hPoNotCFg" alt="" /></p>
<p>Pretty impressive to get a 1440p OLED display, with stereo speakers,
that can play modern gaming titles all for less than $150! I hope that
this was helpful to anyone else looking to do something similar.</p>arhamI’ve never really been much of a PC gamer - growing up, I mostly played on various Nintendo consoles. I recall trying to build a PC back in 2014, and giving up due to how expensive it was (I was still a student at the time). Somewhat ironically, the build that I specced out at the time would cost about the same today, due to the GPU market being what it is. Phones at the time weren’t really powerful enough to do nearly as much as they can today - I recall playing a few Gameboy games on an emulator for my Nexus 5, along with Angry Birds.Building a simple typing test website using hyperscript2021-12-18T00:00:00-05:002021-12-18T00:00:00-05:00/2021/12/18/hyperscript-simple-type<p>Recently, I’ve been playing around with <a href="https://hyperscript.org/">_hyperscript</a>. It’s from the same folks that made <a href="https://htmx.org/">HTMX</a>, so you know it’ll be an… interesting project. I used it to build <a href="https://arhamjain.com/typehand">typehand</a> which is a tiny little typing speed measurer inspired by <a href="https://typings.gg">typings.gg</a>. If you want to see an explanation of the code, skip to the <a href="#typehand">typehand section</a>.</p>
<h2 id="personal-tangent">Personal Tangent</h2>
<p>When I first started doing web development around 2012, I would manually link to scripts (usually jQuery) and CSS files in the head of my HTML before writing code. I had heard about this cool thing called “npm” and “node” but trying to install it on my Windows laptop was a hassle. Once I did manage to somehow install it, it made this folder called <code class="language-plaintext highlighter-rouge">node_modules</code> that <a href="https://stackoverflow.com/questions/28175200/how-to-delete-node-modules-deep-nested-folder-in-windows">was impossible to delete</a>. Eventually, <a href="https://docs.microsoft.com/en-us/windows/wsl/about">WSL</a> came along and I was able to successfully install and use the npm ecosystem.</p>
<p>After a couple of years though, the npm ecosystem no longer felt like it was helping me build projects faster. I’d go to make a tiny website that would just generate a random word or submit a form and be pulling in VueJS out of sheer habit (along with its dependencies). Build times were slow as well on my hardware, usually around 10 seconds to hot reload and closer to a minute to build. When I went to <code class="language-plaintext highlighter-rouge">npm install something</code>, it became mentally exhausting to watch <code class="language-plaintext highlighter-rouge">something</code> pull in hundreds and hundreds of Javascript and CSS files. Web dev just didn’t feel as fun as it had been when I started. When you have a hammer, everything starts to look like a Single Page Application and I wanted out.</p>
<p>Don’t get me wrong - SPAs have their place in the world. There are quite a few web applications I interact with and depend on to do work, which I can’t imagine using if they didn’t have some of the features of an SPA. It was around this point that I saw HTMX, and tried using it in a few small projects. It’s very powerful and could probably replace needing to write Javascript for many, many websites.</p>
<p>What about websites that actually need a bit of JS though? Or those that don’t need a server running backend logic?</p>
<p>That’s what hyperscript aims to solve.</p>
<h2 id="hyperscript">Hyperscript</h2>
<p>hyperscript is a <strong>language</strong> that works especially well for those little interactions that need a bit of JS to work. I like to think of it as <code class="language-plaintext highlighter-rouge">jQuery</code> if it were it’s own language, or as a language dedicated to working with the DOM.</p>
<p>Their website states</p>
<blockquote>
<p>hyperscript is an easy and approachable language designed for modern front-end web development</p>
</blockquote>
<p>I’ve found this to be a pretty accurate statement of what one can do with it.</p>
<p><em>Well hold on, I’ve seen languages try to replace JS before. How is this different from Typescript, Flow, and Coffeescript?</em></p>
<p>I’ve seen some of the hyperscript developers describe their approach as “intentionally unscaleable”. Those languages above are designed to solve the problem of <strong>writing correct Javascript quickly</strong>. I’m going to generalize and say that they’re also designed to write business logic, aka code that runs via Node on a server. hyperscript doesn’t do that. Writing business logic in Hyperscript is a lot more painful than those languages (including JS), but working with the DOM is a lot, lot easier.</p>
<p>Enough talk - here’s a quick example to show an element after clicking a button taken from their website. In other words, on click show #show-target.</p>
<p><strong>JS</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><button</span> <span class="na">onclick=</span><span class="s">"document.getElementById('show-target-1').style.display = 'block'"</span><span class="nt">></span>
Show Element
<span class="nt"></button></span>
<span class="nt"><div</span> <span class="na">style=</span><span class="s">"display: none"</span> <span class="na">id=</span><span class="s">"show-target-1"</span><span class="nt">></span>
Hidden Element
<span class="nt"></div></span>
</code></pre></div></div>
<p><strong>jQuery</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script></span>
<span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">#showBtn</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">#show-target-2</span><span class="dl">"</span><span class="p">).</span><span class="nx">show</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"><button</span> <span class="na">id=</span><span class="s">"showBtn"</span><span class="nt">></span>
Show Element
<span class="nt"></button></span>
<span class="nt"><div</span> <span class="na">style=</span><span class="s">"display: none"</span> <span class="na">id=</span><span class="s">"show-target-2"</span><span class="nt">></span>
Hidden Element
<span class="nt"></div></span>
</code></pre></div></div>
<p><strong>hyperscript</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><button</span> <span class="na">_=</span><span class="s">"on click show #show-target-3"</span><span class="nt">></span>
Show Element
<span class="nt"></button></span>
<span class="nt"><div</span> <span class="na">style=</span><span class="s">"display: none"</span> <span class="na">id=</span><span class="s">"show-target-3"</span><span class="nt">></span>
Hidden Element
<span class="nt"></div></span>
</code></pre></div></div>
<p>I also really like <a href="https://twitter.com/htmx_org/status/1459364371067453442">this demo</a> that the developers posted on Twitter, it shows just how powerful it can be compared to what you’d need to write out in Javascript.</p>
<h2 id="typehand">typehand</h2>
<p>As usual, when I want to try out some new framework, language, or stack, I build a small project to validate its claims. I figured a typing test website would be large enough project to attempt doing, without it taking too long. I won’t be going through all of the code/HTML, just the bits with hyperscript. I would recommend going to <a href="https://arhamjain.com/typehand/">typehand</a> and right clicking to view the source if you want to follow along (never thought that would be useful in 2021). I’ve also put the source in a <a href="https://gist.github.com/ajusa/09dd3c1aab78515a32504dd1578e503a">Github gist</a>. This code uses the development build of hyperscript at the time of writing, not the latest stable release.</p>
<h3 id="requirements">Requirements</h3>
<ol>
<li>Randomly generate words to type from a wordlist (in this case, generously taken from typings.gg).</li>
<li>Allow a user to select how many words to type [10, 25, 50, 100, 250]</li>
<li>Have the word that the user needs to type highlighted. If the user mistypes the word, mark it as incorrect. If the user types the word correctly, mark it as green.</li>
<li>As the user types the word, the moment they make a mistake on that word, make the typing field change color to tell them that somehow.</li>
<li>Once the user has typed the last word, calculate their accuracy and words per minute (WPM). Display those statistics.</li>
</ol>
<h3 id="code-explanation">Code Explanation</h3>
<p>I’ll be jumping around a bit. Thankfully the code is so short that it shouldn’t be too difficult for anyone reading this to following along.</p>
<p>First, loading the wordlist.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>init fetch words.json as json
then set $allwords to it
then trigger reset(count: 50) on #words
</code></pre></div></div>
<p>The first line here does a fetch request to <code class="language-plaintext highlighter-rouge">words.json</code>, which is just a JSON file containing English words in an array. The second bit triggers an event called <code class="language-plaintext highlighter-rouge">reset</code> on the element with an id of <code class="language-plaintext highlighter-rouge">words</code>. Notice the async transparent nature of hyperscript when we trigger the event after the JSON has loaded - you don’t see any promises and callbacks here, even though internally that’s what is happening.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">on</span> <span class="nx">reset</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span>
<span class="kd">set</span> <span class="nx">$startTime</span> <span class="nx">to</span> <span class="o">-</span><span class="mi">1</span>
</code></pre></div></div>
<p>Let’s jump to the <code class="language-plaintext highlighter-rouge">reset</code> event, since we trigger it on load. The first line listens for it being triggered, and it additionally unpacks <code class="language-plaintext highlighter-rouge">event.count</code> into the <code class="language-plaintext highlighter-rouge">count</code> variable. The next line sets <code class="language-plaintext highlighter-rouge">$startTime</code> to -1. The <code class="language-plaintext highlighter-rouge">$</code> in front of <code class="language-plaintext highlighter-rouge">startTime</code> means it is a global variable, so any hyperscript can access it. hyperscript does have element scoped variables, but as I was trying to keep things simple I didn’t use them. <code class="language-plaintext highlighter-rouge">$startTime</code> is used to hold the (Unix) time that the user starts typing - when we reset the words to be typed however, the user hasn’t typed so we just set it to -1.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">repeat</span> <span class="nx">count</span> <span class="nx">times</span>
<span class="nx">append</span> <span class="s2">`<code class='yet'></span><span class="p">${</span><span class="nx">random</span> <span class="k">in</span> <span class="nx">$allwords</span><span class="p">}</span><span class="s2"></code>`</span> <span class="nx">to</span> <span class="nx">randwords</span>
<span class="nx">end</span>
<span class="kd">set</span> <span class="nx">my</span> <span class="nx">innerHTML</span> <span class="nx">to</span> <span class="nx">randwords</span>
</code></pre></div></div>
<p>Apologies for the syntax highlighting - there is a Prism based highlighter for hyperscript <a href="https://github.com/dz4k/prism-hyperscript">here</a>, but my blog doesn’t support it.</p>
<p>Anyway, the next line repeats <code class="language-plaintext highlighter-rouge">count</code> times, the parameter passed in as an event. We append a string with interpolation to the variable <code class="language-plaintext highlighter-rouge">randwords</code>. hyperscript is pretty loose with variable declarations and some scoping rules to make it easier to write simple code. Note the bit inside of the <code class="language-plaintext highlighter-rouge">${}</code> block. Each iteration, we end up selecting a random word from <code class="language-plaintext highlighter-rouge">$allwords</code>, the global variable we filled with words using JSON earlier. We then go ahead and set this HTML string to fill the element’s innerHTML. We’ve fulfilled requirement 1.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">add</span> <span class="p">.</span><span class="nx">light</span><span class="o">-</span><span class="nx">purple</span> <span class="nx">to</span> <span class="nx">first</span> <span class="p">.</span><span class="nx">yet</span>
<span class="kd">set</span> <span class="err">#</span><span class="nx">maininput</span><span class="dl">'</span><span class="s1">s value to </span><span class="dl">''</span><span class="s1">
call #maininput.focus()
</span></code></pre></div></div>
<p>Finally, we add the class <code class="language-plaintext highlighter-rouge">.light-purple</code> to the first word that is <code class="language-plaintext highlighter-rouge">.yet</code> to be typed, fulfilling part of requirement 3. Since this all part of resetting the list of words, we are now ready to let the user start typing. We clear the input field and focus it, so that they can immediately start typing.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">on</span> <span class="nx">load</span>
<span class="nx">tell</span> <span class="nx">my</span> <span class="nx">children</span> <span class="nx">add</span> <span class="p">.</span><span class="nx">secondary</span> <span class="nx">add</span> <span class="p">[@</span><span class="nd">href</span><span class="o">=</span><span class="err">#</span><span class="p">]</span>
<span class="nx">on</span> <span class="nx">click</span> <span class="k">from</span> <span class="o"><</span><span class="nx">a</span><span class="o">/></span> <span class="k">in</span> <span class="nx">me</span>
<span class="nx">take</span> <span class="p">.</span><span class="nx">contrast</span> <span class="k">from</span> <span class="o"><</span><span class="nx">a</span><span class="o">/></span> <span class="k">in</span> <span class="nx">me</span> <span class="k">for</span> <span class="nx">it</span>
<span class="nx">trigger</span> <span class="nx">reset</span><span class="p">(</span><span class="nx">count</span><span class="p">:</span> <span class="nx">it</span><span class="p">.</span><span class="nx">innerText</span><span class="p">)</span> <span class="nx">on</span> <span class="err">#</span><span class="nx">words</span>
</code></pre></div></div>
<p>You can listen for multiple events in hyperscript, and that’s what we do there. First, when the page loads we go ahead and add some classes and attributes to the children, which are the links we can click to change the number of words to be typed. Note that the next line listens for a click on the parent element. This is simpler than needing to listen to each link for a click.</p>
<p>Once someone clicks on the child <code class="language-plaintext highlighter-rouge"><a/></code> tag, we take the <code class="language-plaintext highlighter-rouge">.contrast</code> class from all of them, and add it to the link that was clicked. We then trigger the <code class="language-plaintext highlighter-rouge">reset</code> event as before, but using the innerText of the clicked link to fill in the count. With this, we’ve fulfilled requirement 2.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">on</span> <span class="nx">click</span> <span class="nx">trigger</span> <span class="nx">reset</span><span class="p">(</span><span class="nx">count</span><span class="p">:</span> <span class="err">#</span><span class="nx">words</span><span class="p">.</span><span class="nx">children</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="nx">on</span> <span class="err">#</span><span class="nx">words</span>
</code></pre></div></div>
<p>This is the code for the reset button. It is pretty similar to how the links from above work, only we set the count to be the number of children (words) that the element with id <code class="language-plaintext highlighter-rouge">words</code> has.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">on</span> <span class="nx">input</span><span class="p">[.</span><span class="nx">yet</span><span class="dl">'</span><span class="s1">s length is 1 and my value is .yet</span><span class="dl">'</span><span class="nx">s</span> <span class="nx">innerText</span><span class="p">]</span> <span class="nx">or</span>
<span class="nx">keydown</span><span class="p">(</span><span class="nx">key</span><span class="p">)[</span><span class="nx">key</span> <span class="nx">is</span> <span class="dl">'</span><span class="s1"> </span><span class="dl">'</span> <span class="nx">and</span> <span class="nx">some</span> <span class="p">.</span><span class="nx">yet</span><span class="p">]</span>
<span class="k">if</span> <span class="nx">my</span> <span class="nx">value</span>
<span class="nx">call</span> <span class="nx">nextWord</span><span class="p">(</span><span class="nx">my</span> <span class="nx">value</span><span class="p">)</span>
<span class="nx">end</span>
<span class="kd">set</span> <span class="nx">my</span> <span class="nx">value</span> <span class="nx">to</span> <span class="dl">''</span>
<span class="nx">halt</span>
</code></pre></div></div>
<p>The events on the input here are pretty dense, so I’ll try my best to explain them. This event handles when to go ahead and write the next word. There are basically two times we want to do this: the user hits space after typing some text, or if the user has typed the last word correctly.</p>
<p>The first event we check for here is checking that second condition - is there one word left to type, and did we type it correctly. If that’s the case, it’ll run the code at the end. Note that we are listening to event 1 or event 2 happening to run this code - earlier we were listening to two different events with two different pieces of code.</p>
<p>The conditions for the second event is that the user pressed the spacebar down, and that there are still words <code class="language-plaintext highlighter-rouge">.yet</code> to be typed. So if either of these events are true, we do a few things. First, if there was something typed (the input isn’t blank), we call the <code class="language-plaintext highlighter-rouge">writeWord</code> function with the value inside of the input. Regardless of whether there was something in the input, we clear the value (setting it to ‘’) and <code class="language-plaintext highlighter-rouge">halt</code>, which stops the event (preventDefault for the JS folks). This stops the space from actually being added to the input, as this is a <code class="language-plaintext highlighter-rouge">keydown</code> event which runs before the input has been updated with the value of the key.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">on</span> <span class="nx">input</span><span class="p">[</span><span class="nx">$startTime</span> <span class="nx">is</span> <span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="kd">set</span> <span class="nx">$startTime</span> <span class="nx">to</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
<span class="nx">on</span> <span class="nx">input</span><span class="p">[</span><span class="nx">some</span> <span class="p">.</span><span class="nx">yet</span><span class="p">]</span>
<span class="kd">set</span> <span class="p">@</span><span class="nd">aria</span><span class="o">-</span><span class="nx">invalid</span> <span class="nx">to</span> <span class="nx">not</span> <span class="p">(</span><span class="nx">first</span> <span class="p">.</span><span class="nx">yet</span><span class="dl">'</span><span class="s1">s innerText).startsWith(my value)
</span></code></pre></div></div>
<p>These are easier to explain thankfully. The first one just sets <code class="language-plaintext highlighter-rouge">$startTime</code> to the current time when the user types (so long as it was just reset, setting its value to -1). The second one handles requirement 4. As long as there are words remaining, it sets whether the field is valid or not based on whether the value of the input starts with the current word we need to type (first of the words yet to be typed). Since the attribute is <code class="language-plaintext highlighter-rouge">invalid</code> we need to negate it with the <code class="language-plaintext highlighter-rouge">not</code>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">def</span> <span class="nx">nextWord</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span>
<span class="nx">tell</span> <span class="nx">first</span> <span class="p">.</span><span class="nx">yet</span>
<span class="nx">remove</span> <span class="p">.</span><span class="nx">yet</span> <span class="p">.</span><span class="nx">light</span><span class="o">-</span><span class="nx">purple</span>
<span class="k">if</span> <span class="nx">value</span> <span class="nx">is</span> <span class="nx">your</span> <span class="nx">innerText</span> <span class="nx">add</span> <span class="p">.</span><span class="nx">green</span> <span class="nx">otherwise</span> <span class="nx">add</span> <span class="p">.</span><span class="nx">red</span> <span class="nx">end</span>
<span class="k">if</span> <span class="nx">no</span> <span class="p">.</span><span class="nx">yet</span>
<span class="kd">set</span> <span class="nx">minutes</span> <span class="nx">to</span> <span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="nx">$startTime</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mi">1000</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
<span class="nx">put</span> <span class="p">(</span><span class="err">#</span><span class="nx">words</span><span class="p">.</span><span class="nx">children</span><span class="p">.</span><span class="nx">length</span> <span class="o">/</span> <span class="nx">minutes</span><span class="p">)</span> <span class="k">as</span> <span class="nx">Fixed</span> <span class="nx">into</span> <span class="err">#</span><span class="nx">wpm</span>
<span class="nx">put</span> <span class="p">((.</span><span class="nx">green</span><span class="p">.</span><span class="nx">length</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span> <span class="o">/</span> <span class="err">#</span><span class="nx">words</span><span class="p">.</span><span class="nx">children</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="k">as</span> <span class="nx">Fixed</span> <span class="nx">into</span> <span class="err">#</span><span class="nx">acc</span>
<span class="nx">otherwise</span> <span class="nx">add</span> <span class="p">.</span><span class="nx">light</span><span class="o">-</span><span class="nx">purple</span> <span class="nx">to</span> <span class="nx">next</span> <span class="p">.</span><span class="nx">yet</span> <span class="nx">end</span>
<span class="nx">end</span>
</code></pre></div></div>
<p>Last stretch here! This is a function in hyperscript. I separated it out to aid in readability, but it technically could have been embedded into the input tag.</p>
<p>The <code class="language-plaintext highlighter-rouge">tell</code> statement here basically aliases <code class="language-plaintext highlighter-rouge">first .yet</code> to the word <code class="language-plaintext highlighter-rouge">you</code>, and it makes some operations affect it by default. Again, <code class="language-plaintext highlighter-rouge">first .yet</code> is the word that the user just typed when we end up in this function. We remove the classes yet and light-purple from that word. If the value matched the word itself, we add green, otherwise we mark it as red (requirement 3). The if statement check if there are <code class="language-plaintext highlighter-rouge">no .yet</code> left - in other words, no words left to type. If that is the case, we do some math to get the WPM. Notice the accuracy calculation - we just use the DOM to hold this state for us and figure out how many words were red to calculate the percent. With that, we’ve met requirement 5.</p>
<p>If there are words left however, we need to highlight the next word in purple, which is exactly what that last piece of code does.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Personally, this was fun! I can imagine that writing this in normal ES5 would have been a bit of a slog, as I know that the difficult part of that would just be finding all of the DOM manipulation methods and figuring out when I need to iterate a list of elements to add and remove classes.</p>
<p>As far as performance goes, yes hyperscript is slower than Javascript. It’s an interpreted language implemented in Javascript. For the majority of interactions you use it for though, I’d expect performance to not matter all that much - it’s still just doing DOM manipulation the same way Javascript does.</p>
<p>I’m not claiming that hyperscript will replace Javascript, but it is very <em>interesting</em> to use. The strangest thing about this project was that I found it slightly more difficult to write code, but much easier to read my code back. Everything was plain English, and I didn’t have to go digging too much for JS functions that manipulate the DOM. In other words, if you already understand DOM events, you can pick up hyperscript in a few hours and be productive.</p>
<p>Speaking of productivity, I was able to write this simple project in a few hours. It was pretty much my first time using hyperscript to do anything substantial, and I’d say it went pretty well. However, I did find a few bugs and make a few feature requests as I was writing this code. hyperscript is still in <strong>beta</strong>, though its shaping up nicely. If you’re planning on using this for a larger project, you may want to wait for 1.0 to avoid any sort of breaking changes.</p>arhamRecently, I’ve been playing around with _hyperscript. It’s from the same folks that made HTMX, so you know it’ll be an… interesting project. I used it to build typehand which is a tiny little typing speed measurer inspired by typings.gg. If you want to see an explanation of the code, skip to the typehand section.Using Nim for Web Development in 20212021-11-22T00:00:00-05:002021-11-22T00:00:00-05:00/2021/11/22/nim-webdev<p>These are some of my thoughts and recommendations on using Nim in 2021 for web development. If you want a summary of what I think you should use, skip to the end. As this is my first post on the topic, I’ll be including a bit of history. My intent is not to compare to any specific libraries in other languages. While I’ve used others, it’s been a while and I don’t want to spend a ton of time researching and relearning them for the sake of a <strong>Nim</strong> related article.</p>
<h2 id="what-is-nim">What is <a href="https://nim-lang.org/">Nim</a>?</h2>
<blockquote>
<p>Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.</p>
</blockquote>
<p>That’s what the website says, and I’ve found it to be pretty accurate in the last four years that I’ve used it.</p>
<p>Let’s dig more into web development in Nim. Unlike other languages, there are a few things that set Nim apart for web development.</p>
<ol>
<li>Nim has native support for compiling to C and JS (without using Webassembly) so it can be used to write Javascript code similar to how Typescript can be used to write Javascript</li>
<li>Nim has meta-programming features that include templates and macros.</li>
<li>Nim has a small community and fewer libraries in general.</li>
</ol>
<h2 id="using-nim-for-front-end-development">Using Nim for front-end development</h2>
<p>Since Nim can natively output JS, we can use it everywhere we want to use JS. The main library that is used to create Single Page Applications (or SPAs) in Nim is <a href="https://github.com/karaxnim/karax">Karax</a>. It uses a virtual DOM similar to React.</p>
<p>Karax can be thought of as an alternative to Vue/React/Angular. It is written in pure Nim, and has the benefits of a compiled, statically typed language - namely type checking and compile time optimization. Another feature of Karax is the DSL, or domain specific language. It can be used to generate HTML within Nim on the server (when compiled to C) or the client (when compiled to JS)</p>
<p>Here’s an example of just using the <em>Karax DSL</em> to generate HTML, without the bit that does the virtual DOM.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">karax</span> <span class="o">/</span> <span class="o">[</span><span class="n">karaxdsl</span><span class="p">,</span> <span class="n">vdom</span><span class="o">]</span>
<span class="k">const</span> <span class="n">places</span> <span class="o">=</span> <span class="o">@[</span><span class="s">"boston"</span><span class="p">,</span> <span class="s">"cleveland"</span><span class="p">,</span> <span class="s">"los angeles"</span><span class="p">,</span> <span class="s">"new orleans"</span><span class="o">]</span>
<span class="k">proc </span><span class="nf">render</span><span class="o">*</span><span class="p">():</span> <span class="n">VNode</span> <span class="o">=</span>
<span class="n">buildHtml</span><span class="p">(</span><span class="n">tdiv</span><span class="p">(</span><span class="n">class</span> <span class="o">=</span> <span class="s">"mt-3"</span><span class="p">)):</span>
<span class="n">h1</span><span class="p">:</span> <span class="n">text</span> <span class="s">"My Web Page"</span>
<span class="n">p</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Hello world"</span>
<span class="n">ul</span><span class="p">:</span>
<span class="k">for</span> <span class="n">place</span> <span class="ow">in</span> <span class="n">places</span><span class="p">:</span>
<span class="n">li</span><span class="p">:</span> <span class="n">text</span> <span class="n">place</span>
<span class="n">dl</span><span class="p">:</span>
<span class="n">dt</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Can I use Karax for client side single page apps?"</span>
<span class="n">dd</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Yes"</span>
<span class="n">dt</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Can I use Karax for server side HTML rendering?"</span>
<span class="n">dd</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Yes"</span>
<span class="n">echo</span> <span class="n">render</span><span class="p">()</span>
</code></pre></div></div>
<p>If you want to see how to use this sort of syntax to create an SPA, check out the Karax project README and examples. There are also a few tutorials and example projects made by the community <a href="https://github.com/karaxnim/awesome-karax">here</a>.</p>
<h2 id="using-nim-for-backend-development">Using Nim for backend development</h2>
<p>There are two web frameworks that I would recommend: <a href="https://github.com/dom96/jester">Jester</a> and <a href="https://github.com/planety/prologue">Prologue</a>. There are quite a few others, but most aren’t as mature or well tested as these two. In case you are curious about performance metrics between the two, you can look at <a href="https://web-frameworks-benchmark.netlify.app/result?l=nim">these benchmarks</a>.</p>
<h3 id="some-history">Some History</h3>
<p>Note that you may have heard of <a href="https://github.com/dom96/httpbeast">httpbeast</a>, <a href="https://github.com/xflywind/httpx">httpx</a>, and <a href="https://github.com/disruptek/httpleast">httpleast</a> in the Nim community. The difference between these is a bit confusing, so I’ll try to explain it here.</p>
<p>httpbeast was created by <a href="https://github.com/dom96">dom96</a> with the intent of getting Nim to perform really well as a web server. As such, it is dedicated to serving HTTP 1.1 requests as quickly as possible and doesn’t really do anything else. Jester then adds a layer on top of httpbeast to handle routing, cookies, and other niceties you’d expect from a “web framework”.</p>
<p>httpbeast doesn’t really support Windows due to parts its reliance on epoll/kqueue, so <a href="https://github.com/xflywind/httpx">xflywind</a> forked it into httpx to add Windows support using IOCP. httpx powers Prologue, the other web framework I mentioned.</p>
<p>httpleast is unrelated to any of these projects, and aims to serve HTTP requests as fast as possible using <a href="https://github.com/nim-works/cps">CPS</a>. As of 2021, it’s still alpha level software and there are no sizeable Nim web frameworks using it to serve requests.</p>
<p><img src="/images/2021/relation.png" alt="relation" /></p>
<h3 id="jester">Jester</h3>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">jester</span>
<span class="n">routes</span><span class="p">:</span>
<span class="n">get</span> <span class="s">"/"</span><span class="p">:</span>
<span class="n">resp</span> <span class="s">"<h1>Hello World!</h1>"</span>
</code></pre></div></div>
<p>Jester is pretty bare-bones, but as it is written by a core Nim developer it is kept up to date and supports most things a “micro” web framework would support. Since it is minimal, it leaves it to the programmer to decide what to use to parse JSON, validate requests, and do templating. Since Jester is so minimal the only documentation is provided in a README file, and it is assumed that the developer has done web development before. The core features it has are routing, form handling, cookie handling, and serving static files.</p>
<h3 id="prologue">Prologue</h3>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">prologue</span>
<span class="k">proc </span><span class="nf">hello</span><span class="o">*</span><span class="p">(</span><span class="n">ctx</span><span class="p">:</span> <span class="n">Context</span><span class="p">)</span> <span class="p">{.</span><span class="n">async</span><span class="p">.}</span> <span class="o">=</span>
<span class="n">resp</span> <span class="s">"<h1>Hello World!</h1>"</span>
<span class="k">let</span> <span class="n">app</span> <span class="o">=</span> <span class="n">newApp</span><span class="p">()</span>
<span class="n">app</span><span class="p">.</span><span class="n">addRoute</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">hello</span><span class="p">)</span>
<span class="n">app</span><span class="p">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div></div>
<p>Prologue on the other hand, is a bit more full featured and has much more comprehensive documentation. It also has official support for websockets, openapi, middleware, and more. There are a couple of official middleware plugins such as auth, cors, clickjacking, and csrf protection.</p>
<h3 id="comparison">Comparison</h3>
<p>Personally, I tend to use Jester. It’s been around longer, and there are a few more third party libraries that add support for it because of that. For example, there is a simple <a href="https://github.com/treeform/ws">websockets library</a> and an <a href="https://github.com/FedericoCeratto/nim-httpauth">authentication library</a> that ship with support for Jester.</p>
<p>However, Prologue has many more features. If you were to compare the two in Python-land, I’d say that Jester is closer to Flask and Prologue is closer to Django in terms of the size and functionality of the two. Both are still very good frameworks that have a lot to offer.</p>
<table>
<thead>
<tr>
<th> </th>
<th>Jester</th>
<th>Prologue</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Created</strong></td>
<td>2012</td>
<td>2020</td>
</tr>
<tr>
<td><strong>Middleware</strong></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Auth</strong></td>
<td>Yes <sup id="fnref:external" role="doc-noteref"><a href="#fn:external" class="footnote" rel="footnote">1</a></sup></td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Advanced Security (CSRF, Clickjacking)</strong></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Documentation</strong></td>
<td>Some</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Routing</strong></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Static Files</strong></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Cookies</strong></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Mocking</strong></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>CORS</strong></td>
<td>Manual</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Websockets</strong></td>
<td>Yes <sup id="fnref:external:1" role="doc-noteref"><a href="#fn:external" class="footnote" rel="footnote">1</a></sup></td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Uploads</strong></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Simple</strong></td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><strong>Cross-platform</strong></td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<h3 id="templating">Templating</h3>
<p>Templating is how we get our data to render as HTML. I’ve already discussed the Karax DSL, which is what I prefer as it was written by Araq (creator of Nim). However, it doesn’t really look like HTML so it is non-trivial to just paste in a snippet from Bootstrap and have it work. There is a <a href="https://github.com/nim-lang-cn/html2karax">package</a> that can convert from HTML directly to the Karax DSL, but it is a bit clunky to use.</p>
<p>There are a few good alternatives in this space, if you prefer something else. A newer, more active project is <a href="https://github.com/enthus1ast/nimja">Nimja</a>, which has jinja2 style templates.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="o">%</span> <span class="k">block</span> <span class="n">content</span> <span class="o">%</span><span class="p">}</span>
<span class="p">{</span><span class="c"># A random loop to show off. #}</span>
<span class="o"><</span><span class="n">h1</span><span class="o">></span><span class="n">Random</span> <span class="n">links</span><span class="o"></</span><span class="n">h1</span><span class="o">></span>
<span class="p">{</span><span class="o">%</span> <span class="k">const</span> <span class="n">links</span> <span class="o">=</span> <span class="o">[</span>
<span class="p">(</span><span class="n">title</span><span class="p">:</span> <span class="s">"google"</span><span class="p">,</span> <span class="n">target</span><span class="p">:</span> <span class="s">"https://google.de"</span><span class="p">),</span>
<span class="p">(</span><span class="n">title</span><span class="p">:</span> <span class="s">"fefe"</span><span class="p">,</span> <span class="n">target</span><span class="p">:</span> <span class="s">"https://blog.fefe.de"</span><span class="p">)</span><span class="o">]</span>
<span class="o">%</span><span class="p">}</span>
<span class="p">{</span><span class="o">%</span> <span class="k">for</span> <span class="p">(</span><span class="n">ii</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span> <span class="ow">in</span> <span class="n">links</span><span class="p">.</span><span class="n">pairs</span><span class="p">()</span> <span class="o">%</span><span class="p">}</span>
<span class="p">{{</span><span class="n">ii</span><span class="p">}}</span> <span class="o"><</span><span class="n">a</span> <span class="n">href</span><span class="o">=</span><span class="s">"{{item.target}}"</span><span class="o">></span><span class="n">This</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">link</span> <span class="n">to</span><span class="p">:</span> <span class="p">{{</span><span class="n">item</span><span class="p">.</span><span class="n">title</span><span class="p">}}</span><span class="o"></</span><span class="n">a</span><span class="o">><</span><span class="n">br</span><span class="o">></span>
<span class="p">{</span><span class="o">%</span> <span class="n">endfor</span> <span class="o">%</span><span class="p">}</span>
<span class="o"><</span><span class="n">h1</span><span class="o">></span><span class="n">Members</span><span class="o"></</span><span class="n">h1</span><span class="o">></span>
<span class="p">{</span><span class="c"># `users` was a param to the `renderIndex` proc #}</span>
<span class="p">{</span><span class="o">%</span> <span class="k">for</span> <span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span> <span class="ow">in</span> <span class="n">users</span><span class="p">.</span><span class="n">pairs</span> <span class="o">%</span><span class="p">}</span>
<span class="o"><</span><span class="n">a</span> <span class="n">href</span><span class="o">=</span><span class="s">"/users/{{idx}}"</span><span class="o">></span><span class="p">{</span><span class="o">%</span> <span class="n">importnwt</span> <span class="s">"./partials/_user.nwt"</span> <span class="o">%</span><span class="p">}</span><span class="o"></</span><span class="n">a</span><span class="o">><</span><span class="n">br</span><span class="o">></span>
<span class="p">{</span><span class="o">%</span> <span class="n">endfor</span> <span class="o">%</span><span class="p">}</span>
<span class="p">{</span><span class="o">%</span> <span class="n">endblock</span> <span class="o">%</span><span class="p">}</span>
</code></pre></div></div>
<p>Nim also has libraries for working with Mustache templates, but those can be found on <a href="https://nimble.directory">Nimble</a> without too much hassle.</p>
<h3 id="databasesorms">Databases/ORMs</h3>
<p>Nim ships with support for SQLite, MySQL, and PostgreSQL in the standard library. Of course, these libraries are at a lower level - you need to manually write and form your SQL queries. Most language are pretty similar in this regard.</p>
<p>So, what I suggest to use for storing data is <a href="https://github.com/moigagoo/norm">Norm</a>, an ORM that lets you define your schema (as a Nim object), and then generate SQL based on it. It supports both SQLite and PostgreSQL, which should be sufficient for most projects.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">norm</span> <span class="o">/</span> <span class="o">[</span><span class="n">model</span><span class="p">,</span> <span class="n">sqlite</span><span class="o">]</span>
<span class="k">type</span>
<span class="n">User</span><span class="o">*</span> <span class="o">=</span> <span class="k">ref</span> <span class="k">object</span> <span class="k">of</span> <span class="n">Model</span> <span class="c"># define our schema</span>
<span class="n">email</span><span class="o">*</span><span class="p">:</span> <span class="kt">string</span>
<span class="k">let</span> <span class="n">dbConn</span><span class="o">*</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">":memory:"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">""</span><span class="p">)</span> <span class="c"># open a SQLite memory DB</span>
<span class="n">dbConn</span><span class="p">.</span><span class="n">createTables</span><span class="p">(</span><span class="n">User</span><span class="p">())</span> <span class="c"># create the table if it doesn't exist</span>
<span class="k">var</span> <span class="n">userFoo</span> <span class="o">=</span> <span class="n">newUser</span><span class="p">(</span><span class="s">"foo@foo.foo"</span><span class="p">)</span>
<span class="n">dbConn</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">userFoo</span><span class="p">)</span> <span class="c"># create and insert a row</span>
</code></pre></div></div>
<p>In case Norm is too high level or different from libraries that you’ve used in the past, I can also recommend <a href="https://github.com/itsumura-h/nim-allographer">allographer</a>. It helps in forming SQL queries in Nim, so it isn’t quite as high level as an ORM, but it is much better than manually writing queries.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">proc </span><span class="nf">main</span><span class="p">(){.</span><span class="n">async</span><span class="p">.}</span> <span class="o">=</span>
<span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">await</span> <span class="n">rdb</span>
<span class="p">.</span><span class="n">table</span><span class="p">(</span><span class="s">"users"</span><span class="p">)</span>
<span class="p">.</span><span class="n">select</span><span class="p">(</span><span class="s">"id"</span><span class="p">,</span> <span class="s">"email"</span><span class="p">,</span> <span class="s">"name"</span><span class="p">)</span>
<span class="p">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="p">.</span><span class="n">offset</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="p">.</span><span class="n">get</span><span class="p">()</span>
<span class="n">echo</span> <span class="n">result</span>
</code></pre></div></div>
<p>Note that <code class="language-plaintext highlighter-rouge">allographer</code> relies more on manually specifiying a schema in SQL terms rather than specifying a Nim type.</p>
<p>Nim does have drivers for MongoDB and other non-relational database, but I personally haven’t used any, which makes it difficult for me to recommend any libraries. What I will say is that Nim has very nice interop with Python through <a href="https://github.com/yglukhov/nimpy">nimpy</a>, if you’re willing to lose the speed benefits in exchange for a massive ecosystem. Nim also has very good interop with C libraries, though I wouldn’t recommend writing your own wrappers if you are new to the language.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Nim has a strong enough web ecosystem to build web applications. The main issues today are documentation, and finding these libraries in the first place.</p>
<p>As far as my preferred stack goes, as you might expect, I use Jester for the backend, Karax’s DSL for templating, and Norm for persistence/storage. I recommend treeform’s websockets library (mentioned earlier) for websockets with Jester, and Federico’s httpauth library (also mentioned earlier) for handling authentication in a secure way.</p>
<p>However, I don’t feel comfortable recommending Nim for front-end web development, specifically Karax. There are too many rough edges and bugs for it to be ready for a production grade web service. Karax is also less performant than React/Vue due to it’s nature - there’s a layer of abstraction to access JS APIs, which inherently makes it slower unless Nim’s JS output is improved significantly. <sup id="fnref:js" role="doc-noteref"><a href="#fn:js" class="footnote" rel="footnote">2</a></sup></p>
<p>Instead, what I recommend is using something like <a href="https://htmx.org/">HTMX</a> and <a href="https://hyperscript.org/">hyperscript</a> to add interactivity to pages, rather than reaching for an SPA framework. With something like React/Karax, you have four states to keep track of:</p>
<ol>
<li>Backend state/database</li>
<li>Frontend state/application</li>
<li>Virtual DOM state</li>
<li>Actual DOM state</li>
</ol>
<p>With something like HTMX/hyperscript, you just have two: backend state, and the DOM state. This is inherently unscalable - you won’t be able to build the next Google Docs with this stack without ripping your hair out. However, it works fine for 90% of web applications.</p>
<p>Anyway, that’s just my opinion. If you are seriously considering Nim for web development, I’d suggest trying out a few combinations of the above to see what it feels like on a tiny project. Pick your favorite and use that!</p>
<p>If I did miss something here, just let me know via Discord (I’m in the Nim #webdev channel).</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:external" role="doc-endnote">
<p>Support with an external library <a href="#fnref:external" class="reversefootnote" role="doc-backlink">↩</a> <a href="#fnref:external:1" class="reversefootnote" role="doc-backlink">↩<sup>2</sup></a></p>
</li>
<li id="fn:js" role="doc-endnote">
<p>Nim’s JS output is suitable for replacing JS however. You could use it write Javascript libraries, without a problem. See <a href="https://github.com/juancarlospaco/nodejs">nodejs</a> for doing this more easily. <a href="#fnref:js" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>arhamThese are some of my thoughts and recommendations on using Nim in 2021 for web development. If you want a summary of what I think you should use, skip to the end. As this is my first post on the topic, I’ll be including a bit of history. My intent is not to compare to any specific libraries in other languages. While I’ve used others, it’s been a while and I don’t want to spend a ton of time researching and relearning them for the sake of a Nim related article.Building a simple room-based chat application in Nim (using HTMX)2021-11-22T00:00:00-05:002021-11-22T00:00:00-05:00/2021/11/22/nim-simple-chat<p>This is going to be a bit of a weird tutorial - rather than walking you through the natural progression of how one would build an application, I’ll instead be explaining the code step by step of the existing application. This is done for two reasons: first, it’s easier to write a tutorial. Second, if you just want to see the final code, you can skip ahead and not have to bear with me as I explain things that you may already know.</p>
<p>A quick note - I use the word templating and template a bit in this explanation. A <code class="language-plaintext highlighter-rouge">template</code> refers to a construct in Nim that allows for code substitution. Templating refers to translating our data into HTML, using Karax’s DSL in this instance.</p>
<p>With that out of the way, let’s get started! The final code can be found <a href="https://github.com/ajusa/simple-chat">in this Github repo</a>. Here are a few screenshots of what we’ll be building.</p>
<p><img src="/images/2021/simple-chat-home.png" alt="sign in" /></p>
<p><img src="/images/2021/simple-chat-room.png" alt="chatroom" /></p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">std</span><span class="o">/[</span><span class="n">strutils</span><span class="p">,</span> <span class="n">asyncdispatch</span><span class="p">,</span> <span class="n">sets</span><span class="p">,</span> <span class="n">hashes</span><span class="p">,</span> <span class="n">json</span><span class="o">]</span>
<span class="k">import</span> <span class="n">karax</span><span class="o">/[</span><span class="n">karaxdsl</span><span class="p">,</span> <span class="n">vdom</span><span class="o">]</span><span class="p">,</span> <span class="n">jester</span><span class="p">,</span> <span class="n">ws</span><span class="p">,</span> <span class="n">ws</span><span class="o">/</span><span class="n">jester_extra</span>
</code></pre></div></div>
<p>On the first line we’ve got stdlib imports, and the second line has all of the external libraries we’ll be using. To install them, just run</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nimble <span class="nb">install </span>karax jester ws
</code></pre></div></div>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">converter</span> <span class="n">toString</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">VNode</span><span class="p">):</span> <span class="kt">string</span> <span class="o">=</span> <span class="o">$</span><span class="n">x</span>
<span class="k">type</span> <span class="n">User</span> <span class="o">=</span> <span class="k">object</span>
<span class="n">name</span><span class="p">:</span> <span class="kt">string</span>
<span class="n">socket</span><span class="p">:</span> <span class="n">WebSocket</span>
<span class="k">proc </span><span class="nf">hash</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">User</span><span class="p">):</span> <span class="n">Hash</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="k">var</span> <span class="n">chatrooms</span> <span class="o">=</span> <span class="n">initTable</span><span class="o">[</span><span class="kt">string</span><span class="p">,</span> <span class="n">HashSet</span><span class="o">[</span><span class="n">User</span><span class="o">]]</span><span class="p">()</span>
</code></pre></div></div>
<p>Now, we’ve got some real code. The first line is a converter. In Nim, converters are automatically ran to convert between types if needed. In our case, we are converting a “VNode” to a string whenever it is required. A VNode is just a DOM element, which is what the Karax DSL uses to represent HTML. However, Jester only responds with string. With this converter, we can simply <code class="language-plaintext highlighter-rouge">resp vnode</code> and have it automatically get converted.</p>
<p>The next few lines simply set up the data structures for this chat application. A user consists of a username, and the websocket corresponding to their computer. We define a hash function for this type so that we can use it in a hashset. Then, we define the core in-memory variable that’ll be storing all of our users and rooms. It’s a map from string, to HashSet[User]. In other words, each room name maps to a set of users that are connected.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span> <span class="n">index</span><span class="o">*</span><span class="p">(</span><span class="n">rest</span><span class="p">:</span> <span class="n">untyped</span><span class="p">):</span> <span class="n">untyped</span> <span class="o">=</span>
<span class="n">buildHtml</span><span class="p">(</span><span class="n">html</span><span class="p">(</span><span class="n">lang</span> <span class="o">=</span> <span class="s">"en"</span><span class="p">)):</span>
<span class="n">head</span><span class="p">:</span>
<span class="n">meta</span><span class="p">(</span><span class="n">charset</span> <span class="o">=</span> <span class="s">"UTF-8"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">"viewport"</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="p">)</span>
<span class="n">link</span><span class="p">(</span><span class="n">rel</span> <span class="o">=</span> <span class="s">"stylesheet"</span><span class="p">,</span> <span class="n">href</span> <span class="o">=</span> <span class="s">"https://unpkg.com/@picocss/pico@latest/css/pico.min.css"</span><span class="p">)</span>
<span class="n">script</span><span class="p">(</span><span class="n">src</span> <span class="o">=</span> <span class="s">"https://unpkg.com/htmx.org@1.6.0"</span><span class="p">)</span>
<span class="n">title</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Simple Chat"</span>
<span class="n">body</span><span class="p">:</span>
<span class="n">nav</span><span class="p">(</span><span class="n">class</span><span class="o">=</span><span class="s">"container-fluid"</span><span class="p">):</span>
<span class="n">ul</span><span class="p">:</span> <span class="n">li</span><span class="p">:</span> <span class="n">a</span><span class="p">(</span><span class="n">href</span> <span class="o">=</span> <span class="s">"/"</span><span class="p">,</span> <span class="n">class</span><span class="o">=</span><span class="s">"secondary"</span><span class="p">):</span> <span class="n">strong</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Simple Chat"</span>
<span class="n">main</span><span class="p">(</span><span class="n">class</span><span class="o">=</span><span class="s">"container"</span><span class="p">):</span> <span class="n">rest</span>
</code></pre></div></div>
<p>Now we get into some of the templating/boilerplate. This snippet builds our “header” and takes in the rest of the page “rest” as input. Taking a closer look, we can see that we load a stylesheet (<a href="https://picocss.com/">PicoCSS</a>) and <a href="https://htmx.org/">HTMX</a>. We then create a quick navbar, all using the Karax DSL.</p>
<p>Note that this is a <code class="language-plaintext highlighter-rouge">template</code> and not a <code class="language-plaintext highlighter-rouge">proc</code>. So, anything that is passed in as the rest argument performs simple code substitution. We’ll see an example of that in a second.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">proc </span><span class="nf">chatInput</span><span class="p">():</span> <span class="n">VNode</span> <span class="o">=</span> <span class="n">buildHtml</span><span class="p">(</span><span class="n">input</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"message"</span><span class="p">,</span> <span class="n">id</span><span class="o">=</span><span class="s">"clearinput"</span><span class="p">,</span> <span class="n">autofocus</span><span class="o">=</span><span class="s">""</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="s">""</span><span class="p">))</span>
<span class="k">proc </span><span class="nf">sendAll</span><span class="p">(</span><span class="n">users</span><span class="p">:</span> <span class="n">HashSet</span><span class="o">[</span><span class="n">User</span><span class="o">]</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="kt">string</span><span class="p">)</span> <span class="o">=</span>
<span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">users</span><span class="p">:</span> <span class="k">discard</span> <span class="n">user</span><span class="p">.</span><span class="n">socket</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">template</span> <span class="n">buildMessage</span><span class="o">*</span><span class="p">(</span><span class="n">msg</span><span class="p">:</span> <span class="n">untyped</span><span class="p">):</span> <span class="n">untyped</span> <span class="o">=</span>
<span class="n">buildHtml</span><span class="p">(</span><span class="n">tdiv</span><span class="p">(</span><span class="n">id</span><span class="o">=</span><span class="s">"content"</span><span class="p">,</span> <span class="n">hx</span><span class="o">-</span><span class="n">swap</span><span class="o">-</span><span class="n">oob</span><span class="o">=</span><span class="s">"beforeend"</span><span class="p">)):</span>
<span class="n">tdiv</span><span class="p">:</span> <span class="n">msg</span>
</code></pre></div></div>
<p>We’ve got some helper procedures here. chatInput generates our input field using the Karax DSL that grabs focus. It’s also required, so that the user can’t just spam sending empty messages. <code class="language-plaintext highlighter-rouge">sendAll</code> just sends a string to all of the users in a room by iterating over them.</p>
<p><code class="language-plaintext highlighter-rouge">buildMessage</code> is another template that we can use to help us reuse code for building HTML. You’ll also notice an interesting attribute: <code class="language-plaintext highlighter-rouge">hx-swap-oob</code>. The documentation for that can be found <a href="https://htmx.org/attributes/hx-swap-oob/">here</a>, but in a nutshell what this does is put the innerHTML of this <code class="language-plaintext highlighter-rouge"><div></code> into a new element, which is the last child of anything matching an id of “content”. If you’ve never used HTMX you may think that this is a very ugly way of doing things, but trust me: it works.</p>
<p>In other words, any time the client sees this div being returned, it appends the content into the end of the DOM element with an id of “content”.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">routes</span><span class="p">:</span>
<span class="n">get</span> <span class="s">"/"</span><span class="p">:</span>
<span class="k">let</span> <span class="n">html</span> <span class="o">=</span> <span class="n">index</span><span class="p">:</span>
<span class="n">h1</span><span class="p">:</span> <span class="n">text</span> <span class="s">"Join a room!"</span>
<span class="n">form</span><span class="p">(</span><span class="n">action</span><span class="o">=</span><span class="s">"/chat"</span><span class="p">,</span> <span class="p">`</span><span class="k">method</span><span class="p">`</span><span class="o">=</span><span class="s">"get"</span><span class="p">):</span>
<span class="n">label</span><span class="p">:</span>
<span class="n">text</span> <span class="s">"Room"</span>
<span class="n">input</span><span class="p">(</span><span class="k">type</span><span class="o">=</span><span class="s">"text"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">"room"</span><span class="p">)</span>
<span class="n">label</span><span class="p">:</span>
<span class="n">text</span> <span class="s">"Username"</span>
<span class="n">input</span><span class="p">(</span><span class="k">type</span><span class="o">=</span><span class="s">"text"</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">"name"</span><span class="p">)</span>
<span class="n">input</span><span class="p">(</span><span class="k">type</span><span class="o">=</span><span class="s">"submit"</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="s">"Join"</span><span class="p">)</span>
<span class="n">resp</span> <span class="n">html</span>
</code></pre></div></div>
<p>Finally, some Jester code! This is the index page, where we can join a room. You’ll notice we use our <code class="language-plaintext highlighter-rouge">index</code> template form earlier, to help us build the first part of the page, before we insert the rest of it. This code is pretty self explanatory as it’s just HTML, but we’re creating a form that asks for a room name and a username. Submitting this form will then send a <code class="language-plaintext highlighter-rouge">GET</code> request to <code class="language-plaintext highlighter-rouge">/chat</code>.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">get</span> <span class="s">"/chat"</span><span class="p">:</span>
<span class="k">let</span> <span class="n">html</span> <span class="o">=</span> <span class="n">index</span><span class="p">:</span>
<span class="n">h1</span><span class="p">:</span> <span class="n">text</span> <span class="o">@</span><span class="s">"room"</span>
<span class="n">tdiv</span><span class="p">(</span><span class="n">hx</span><span class="o">-</span><span class="n">ws</span><span class="o">=</span><span class="s">"connect:/chat/"</span> <span class="o">&</span> <span class="o">@</span><span class="s">"room"</span> <span class="o">&</span> <span class="s">"/"</span> <span class="o">&</span> <span class="o">@</span><span class="s">"name"</span><span class="p">):</span>
<span class="n">p</span><span class="p">(</span><span class="n">id</span><span class="o">=</span><span class="s">"content"</span><span class="p">)</span>
<span class="n">form</span><span class="p">(</span><span class="n">hx</span><span class="o">-</span><span class="n">ws</span><span class="o">=</span><span class="s">"send"</span><span class="p">,</span> <span class="n">id</span><span class="o">=</span><span class="s">"message"</span><span class="p">):</span> <span class="n">chatInput</span><span class="p">()</span>
<span class="n">resp</span> <span class="n">html</span>
</code></pre></div></div>
<p>After submitting that form, we end up in this bit of code. Based on the <code class="language-plaintext highlighter-rouge">name</code> attributes from the earlier snippet, we can access the username and room the user submitted with <code class="language-plaintext highlighter-rouge">@"name"</code> and <code class="language-plaintext highlighter-rouge">@"room"</code>. You’ll notice the <code class="language-plaintext highlighter-rouge">hx-ws</code> attribute here as well, from HTMX. The <a href="https://htmx.org/attributes/hx-ws/">documentation for it</a> says it will try and open a websocket connection to the URL it is given.</p>
<p>Moving on, we see a paragraph with the id “content”. This is where the earlier snippet can be used to append HTML inside of that tag. Within this div, we have a form with the attribute <code class="language-plaintext highlighter-rouge">hx-ws</code> again, only this time it is set to “send”. Looking at the documentation from earlier shows that any time this form is submitted, a JSON response will be send to the closest opened websocket. In other words, submitting this form will send something like</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"here is my message!"</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>over the websocket connection to the server.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">get</span> <span class="s">"/chat/@room/@name"</span><span class="p">:</span>
<span class="k">var</span> <span class="n">ws</span> <span class="o">=</span> <span class="n">await</span> <span class="n">newWebSocket</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="k">var</span> <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="o">@</span><span class="s">"name"</span><span class="p">,</span> <span class="n">socket</span><span class="p">:</span> <span class="n">ws</span><span class="p">)</span>
</code></pre></div></div>
<p>Here’s where we handle the websocket connection. We use <a href="https://github.com/treeform/ws">treeform’s library</a> to create a new websocket, and then we create a new User that corresponds to that websocket. The following code is a bit tricky, as it uses the helpers we defined earlier.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">try</span><span class="p">:</span>
<span class="n">chatrooms</span><span class="p">.</span><span class="n">mgetOrPut</span><span class="p">(</span><span class="o">@</span><span class="s">"room"</span><span class="p">,</span> <span class="n">initHashSet</span><span class="o">[</span><span class="n">User</span><span class="o">]</span><span class="p">()).</span><span class="n">incl</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
<span class="k">let</span> <span class="n">joined</span> <span class="o">=</span> <span class="n">buildMessage</span><span class="p">:</span>
<span class="n">italic</span><span class="p">:</span> <span class="n">text</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>
<span class="n">italic</span><span class="p">:</span> <span class="n">text</span> <span class="s">" has joined the room"</span>
<span class="n">chatrooms</span><span class="o">[@</span><span class="s">"room"</span><span class="o">]</span><span class="p">.</span><span class="n">sendAll</span><span class="p">(</span><span class="n">joined</span><span class="p">)</span>
<span class="k">while</span> <span class="n">user</span><span class="p">.</span><span class="n">socket</span><span class="p">.</span><span class="n">readyState</span> <span class="o">==</span> <span class="n">Open</span><span class="p">:</span>
<span class="k">let</span> <span class="n">sentMessage</span> <span class="o">=</span> <span class="p">(</span><span class="n">await</span> <span class="n">user</span><span class="p">.</span><span class="n">socket</span><span class="p">.</span><span class="n">receiveStrPacket</span><span class="p">()).</span><span class="n">parseJson</span><span class="o">[</span><span class="s">"message"</span><span class="o">]</span>
<span class="k">discard</span> <span class="n">user</span><span class="p">.</span><span class="n">socket</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">chatInput</span><span class="p">())</span>
<span class="k">let</span> <span class="n">reply</span> <span class="o">=</span> <span class="n">buildMessage</span><span class="p">:</span>
<span class="n">bold</span><span class="p">:</span> <span class="n">text</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>
<span class="n">text</span> <span class="s">": "</span> <span class="o">&</span> <span class="n">sentMessage</span><span class="p">.</span><span class="n">getStr</span><span class="p">()</span>
<span class="n">chatrooms</span><span class="o">[@</span><span class="s">"room"</span><span class="o">]</span><span class="p">.</span><span class="n">sendAll</span><span class="p">(</span><span class="n">reply</span><span class="p">)</span>
</code></pre></div></div>
<p>Oh boy, here we go. Well, everything is wrapped in a <code class="language-plaintext highlighter-rouge">try</code> block first of all. First, we create a new HashSet in that table we defined earlier if it didn’t already exist. We also add the user to the HashSet that holds the users for the given room. The following lines generate the message that is sent on join. We use the <code class="language-plaintext highlighter-rouge">buildMessage</code> template to ensure it is appended to the end of the main content. Then, we use the <code class="language-plaintext highlighter-rouge">sendAll</code> proc to ensure it is sent to every user connected to that room.</p>
<p>The following while loop only runs while the user is connected (readyState == Open). Remember, we are using Nim’s async module, so the loop is non-blocking. We wait for a message to be sent over websockets, parse the JSON, and grab the “message” key. I talked about the JSON format a bit earlier.</p>
<p>The following line is weird - we send the chatInput back to the client we just got a message from. If you remember from earlier, anything that is sent over the websocket will have it’s id attribute checked, and the content will affect the DOM. In this case, the element that is sent over will replace the <code class="language-plaintext highlighter-rouge">input</code> tag that the user has on their machine. This is done for two reasons:</p>
<ol>
<li>The input tag that the user has on their machine has some text in it (the message that they sent). By sending over a blank one, we clear the message.</li>
<li>Due to the <code class="language-plaintext highlighter-rouge">autofocus</code> attribute, we regain focus of the text input.</li>
</ol>
<p>Normally, you’d probably clear the input with Javascript. Since we’re using HTMX however, this is the recommended way of doing things. Additionally, imagine that you had to filter the messages (for example, to check for slurs). In this way, you’d be able to quickly and easily clear the input and include a message along with the cleared input telling the user why the message wasn’t accepted.</p>
<p>After that somewhat lengthy explanation, we build the message itself in Karax’s DSL, using our buildMessage helper. Then, we send the message to all the connected users.</p>
<div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">except</span><span class="p">:</span>
<span class="n">chatrooms</span><span class="o">[@</span><span class="s">"room"</span><span class="o">]</span><span class="p">.</span><span class="n">excl</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
<span class="k">let</span> <span class="n">left</span> <span class="o">=</span> <span class="n">buildMessage</span><span class="p">:</span>
<span class="n">italic</span><span class="p">:</span> <span class="n">text</span> <span class="n">user</span><span class="p">.</span><span class="n">name</span>
<span class="n">italic</span><span class="p">:</span> <span class="n">text</span> <span class="s">" has left the room"</span>
<span class="n">chatrooms</span><span class="o">[@</span><span class="s">"room"</span><span class="o">]</span><span class="p">.</span><span class="n">sendAll</span><span class="p">(</span><span class="n">left</span><span class="p">)</span>
<span class="n">resp</span> <span class="s">""</span>
</code></pre></div></div>
<p>Remember how all that code was wrapped in a <code class="language-plaintext highlighter-rouge">try</code> block? Here’s the matching <code class="language-plaintext highlighter-rouge">except</code>. An exception in this code will usually be thrown if there was some sort of error with the websocket. If you wanted to be more precise you could catch the specific exceptions, but as this is SIMPLE chat I didn’t bother.</p>
<p>If there’s an error with the websocket, we take that to mean that the user has disconnected. We remove them from the chat room that they were in. Then, we build a message to show that the user has left the room. Finally, we send that to all of the remaining clients in the room! The last <code class="language-plaintext highlighter-rouge">resp</code> is there to make sure that Jester has something to terminate the connection with for the user that left.</p>
<h2 id="conclusion">Conclusion</h2>
<p>And that’s it! Around 70 lines of code to build a room based chat using nothing but HTMX and Jester! The best part of this code? You don’t need to know or understand Javascript to work with this stack, it’s fantastic. The code surface for bugs on the client side is pretty much 0, but on the server side it does go up a bit with the <code class="language-plaintext highlighter-rouge">hx-swap</code> logic. Still, at least the logic is only in one codebase.</p>
<p>This stack isn’t for everyone, and I’m not claiming that you can build every sort of application with it. I consider it to be very powerful for what it can accomplish without a whole lot of code. I hope that this was informational and helpful!</p>arhamThis is going to be a bit of a weird tutorial - rather than walking you through the natural progression of how one would build an application, I’ll instead be explaining the code step by step of the existing application. This is done for two reasons: first, it’s easier to write a tutorial. Second, if you just want to see the final code, you can skip ahead and not have to bear with me as I explain things that you may already know.Summary and Analysis Blog: Rosencrantz and Guildenstern are Dead2018-03-25T17:55:51-04:002018-03-25T17:55:51-04:00/uncategorized/2018/03/25/summary-and-analysis-blog-rosencrantz-and-guildenstern-are-dead<p><span style="font-weight: 400;">Author: Tom Stoppard. Born in Czechoslovakia, his family escaped due to Hitler. He spent most of his life in Britain, and writes plays. He is still alive, in his 80’s.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Setting: Takes place in Elsinore, and in the boat that Hamlet is put on at the end of </span><em><span style="font-weight: 400;">Hamlet</span></em><span style="font-weight: 400;">. Takes place during the same time as Hamlet.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Plot: The plot is the same as Hamlet, only it is told from Rosencrantz and Guildenstern. R and G are on their way to Elsinore, because they were summoned by Claudius. On the way there, they are flipping a coin that keeps coming up heads. They meet some Players, a group of actors, and interact with them before arriving at Elsinore. They talk with Claudius and Gertrude, who welcome them, and ask R and G to question Hamlet about his madness. After this scene, R and G work on questioning each other to prepare for Hamlet. They do this in a very absurd way, playing a game of questions. </span></p>
<p> </p>
<p><span style="font-weight: 400;">After their meeting with Hamlet, they bemoan how they were outsmarted by him in the questions that they asked and his answers. The Player comes back, talks with R and G about life and how to go with the flow, and leaves. Claudius and Gertrude enter, and they ask R and G about Hamlet’s condition. </span></p>
<p> </p>
<p><span style="font-weight: 400;">We see the Tragedians perform a rendition of the Murder of Gonzago, only this one shows representatives of R and G dying on a boat. There is a cut to the scene where R and G are asked to find Polonius’ body. They set up a comical trap with their belts, that fails to catch Hamlet. They eventually find Hamlet and deliver him to the King. </span></p>
<p> </p>
<p><span style="font-weight: 400;">We have a scene with the boat on its way to England, with Hamlet in tow. They open the letter with their instructions, and see that they are going to have Hamlet killed. The Players jump out of barrels on the ship, saying that they are escaping Denmark. Suddenly, pirates attack and Hamlet is shown having jumped ship. R and G bemoan their fate, and reread the letter, seeing that it now calls for their deaths. Guildenstern snaps and tries to kill the Player with a dagger, which is revealed to be a trick dagger. R and G’s death are now acted out, and the end has each say a few lines, then vanish (dying). The play closes with the ambassadors from England saying that R and G are dead.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Rosencrantz and Guildenstern – the main characters of the piece. Very hard for others to distinguish between, as they are very similar in mannerisms and absurdity.</span></p>
<p> </p>
<p><span style="font-weight: 400;">The Player – a know it all character, who seems to have already read Hamlet as a play. He is witty, and knows his fate, but he goes along with it. He often interacts with R and G and tries to reassure them.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Hamlet – He is still in the play, but his influence is that he is a puzzle for R and G to solve for the King and Queen. He has no new lines added.</span></p>
<p> </p>
<p><span style="font-weight: 400;">There is no narrative voice, since this is a play. The author’s style is very interesting. The point of view leans towards R and G, since they have the majority of the lines. The tone is very absurdist, where very few things have emotion or seriousness attached. The only emotional lines happen near the end, where Guildenstern almost kills the player. There isn’t much imagery other than the stage directions, which tend to emphasize the place being very generic and plain.</span></p>
<p> </p>
<p><span style="font-weight: 400;">There is a lot of symbolism throughout the play. Pirates can be likened to a disaster happening all of a sudden, which the non stop heads can show a high or low in life. In general, this play had a lot of intertextuality, like one of the articles we read about it. There is also a lot of back and forth between what is real, and acted, and the real world vs acting. </span></p>
<p> </p>
<p><span style="font-weight: 400;">“Pirates can happen to anyone” – This quote shows that life can be confusing, and horrible things and good things can happen at random. The pirate ship was good for Hamlet, but it proved deadly for R and G.</span></p>
<p> </p>
<p><span style="font-weight: 400;">“Life in a box is better than no life at all, I expect. You’d have a chance at least. You could lie there thinking: Well, at least I’m not dead.” – This is a very interesting question that he poses, and it reminded me a lot of schrodinger’s cat. It also shows how R and G feel that they would rather be immortal than mortal, even if their lives were terrible. It is quite similar to how they are living their lives anyway.</span></p>
<p> </p>
<p><span style="font-weight: 400;">In R&G, Stoppard comments on the futility of human purpose in a world where people are confined to a role that they cannot control. (1st Hour)</span></p>
<p> </p>
<p><span style="font-weight: 400;">R and G are put into a world that they have no power over. They even mention it at the start of the play, saying that there must have been something they could have done to not have ended up with this conclusion. The title of the piece seems to say that their fate is fixed, and nothing they can do will ever change that. The setting is mostly one of Elsinore, which Hamlet describes as a prison. R and G focus on that statement as well, after their talk with Hamlet. In a prison, you have a lot less free will than outside, in the world. The imagery that is given is harsh and unassuming, seeming to restrict them to one choice. The play (Murder of Gonzago) foreshadows their deaths, and also shows that no matter what happens, they will die on that boat.</span></p>arhamAuthor: Tom Stoppard. Born in Czechoslovakia, his family escaped due to Hitler. He spent most of his life in Britain, and writes plays. He is still alive, in his 80’s.Response to Course Materials 3/252018-03-22T13:36:22-04:002018-03-22T13:36:22-04:00/uncategorized/2018/03/22/response-to-course-materials-3-25<p><span style="font-weight: 400;">We finished our Hamlet discussion first. I think I got a lot out of that discussion, as listening to other people’s ideas is always fun. I do remember some of the conspiracy theories we started throwing around, and while they were entertaining, I feel like we should have stayed more on task.</span></p>
<p> </p>
<p><span style="font-weight: 400;">We then started Rosencrantz and Guildenstern are Dead. This play was rather confusing at first, like the American dream. So much of it seemed repetitive and boring. As time went on, however, I realized that the author was making differences in the dialogue to show us something. Yes, this is an absurdist play, but as we discussed in class, some of the lines were misused idioms, or a line would be repeated with a different subject. </span></p>
<p> </p>
<p><span style="font-weight: 400;">There are a few parallels I noticed between this piece and the American Dream. For one, we lose some characters by the end, with something changing. R and G fade away, as does Grandma in some ways. The American Dream also has a seemingly omniscient character (Grandma), with R and G having the Player. </span></p>
<p> </p>
<p><span style="font-weight: 400;">Annotating the literary analyses of R and G was very interesting. It is weird how literature can have so many faces, so many interpretations of the exact same story. I think that Stoppard has had more variation of responses when compared with the original </span><em><span style="font-weight: 400;">Hamlet</span></em><span style="font-weight: 400;">. I really liked the critique that said something to the affect of all writers being contained by Shakespeare. I feel like we saw this idea at the start of the year, with the book on how to read literature. So many writers have borrowed from Shakespeare, or maybe Shakespeare just wrote about 90% of the human experience. I like the idea of Stoppard complaining through a piece because it just seems like such a human thing to do.</span></p>
<p> </p>
<p><span style="font-weight: 400;">We then had our discussion of Rosencrantz and Guildenstern are Dead. Evan’s theory of everything writing itself was a little far fetched. Overall, I didn’t like how much we theorized about how this play happened. I feel like we should have focused more on the symbols and discrepancies within the text, rather than some parallel universe theory. </span></p>
<p> </p>
<p><span style="font-weight: 400;">I really wish we could have had more time to discuss!</span></p>
<p> </p>
<p><span style="font-weight: 400;">Also, somewhere in here we did some poetry multiple choice, which I found harrowing. I need to brush up on my poetry skills. We ended up tying with another hour. The stories we read could be seen in so many different ways depending on what you knew about the specifics the author refers to. For example, we were comparing a physician to a doctor for medical knowledge, or some sort of Frankenstein body digger.</span></p>
<p> </p>arhamWe finished our Hamlet discussion first. I think I got a lot out of that discussion, as listening to other people’s ideas is always fun. I do remember some of the conspiracy theories we started throwing around, and while they were entertaining, I feel like we should have stayed more on task.2008 Open Prompt Answer2018-03-18T20:24:04-04:002018-03-18T20:24:04-04:00/uncategorized/2018/03/18/2008-open-prompt-answer<p><span style="font-weight: 400;">In Shakespeare’s Hamlet, we are introduced to Horatio, Hamlet’s best friend. He always follows Hamlet where possible, and quickly carries out orders. He appears to care for Hamlet throughout the play. The relationship between Horatio and Hamlet shows us that Horatio is too perfect, and that he was essential in carrying out the death of Claudius.</span></p>
<p> </p>
<p><span style="font-weight: 400;">We first see Horatio at the start of the play. He is trusted by the castle guards, who know that he is close to Hamlet. The castle guards show him the ghost, and he endeavours to tell Hamlet the next day, so that he is well informed. Horatio doesn’t go around telling anyone else, nor does he discuss it with the guards. The guards work for the king, but Horatio makes sure that the guards don’t tell anyone, so that Claudius never hears about it. He seems to have predicted every scenario, and does for the good of Hamlet. In this way, Horatio is perfect.</span></p>
<p> </p>
<p><span style="font-weight: 400;">We later see Horatio and Hamlet in a number of scenes. Horatio is often advising Hamlet of what to do. When Hamlet needs to check if his uncle reacts unfavorably to the play he has put on, he turns to Horatio to watch his uncle. In this way, Hamlet shows that he trusts Horatio with his family members’ lives. That level of trust is almost never seen in the real world showing that Horatio must be very loyal to Hamlet. Later in the play, Horatio receives the letters from Hamlet, the key component that the entire plan rests on. Horatio delivers the letters perfectly, and he does this without letting Claudius know what is happening. Everyone seems to trust him, and not realize he is loyal to only Hamlet. This amount of amiability, and ability to look at a difficult situations and get a solution makes Horatio a “Mary Sue” – someone without any flaws.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Near the end of the play, Horatio advises Hamlet on what to do. He warns Hamlet that the entire setup with Hamlet fighting Laertes is a trap, but Hamlet doesn’t listen to him. Horatio demonstrates that he knows the future, or that he is very intelligent. Then, when everyone is dying, he offers to commit suicide as well. This is a huge moment. What kind of friend would willingly die, when everyone is so young? There were many rational decisions that Horatio could have made at this point, as he had demonstrated in the past. Horatio instead does exactly what Hamlet tells him to do, and makes sure that Hamlet’s voice is heard from beyond the grave in the choosing of the next king of Denmark.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Throughout Hamlet, we see Horatio and Hamlet’s relationship. The relationship appears to never change, but instead it is revealed to us through the many actions and the amount of trust Hamlet has in Horatio. Horatio is loved by all, but he only appears to care for Hamlet. His unquestionable loyalty, and his extreme intelligence make him a perfect man, someone who proves essential to Hamlet’s quest.</span></p>arhamIn Shakespeare’s Hamlet, we are introduced to Horatio, Hamlet’s best friend. He always follows Hamlet where possible, and quickly carries out orders. He appears to care for Hamlet throughout the play. The relationship between Horatio and Hamlet shows us that Horatio is too perfect, and that he was essential in carrying out the death of Claudius.2008 Open Prompt Analysis2018-03-11T23:17:58-04:002018-03-11T23:17:58-04:00/uncategorized/2018/03/11/2008-open-prompt-analysis<p> </p>
<p><span style="font-weight: 400;">Author 1:</span></p>
<p><span style="font-weight: 400;">This author does change what they think the thesis is from the beginning to the end. They initially talk about foil and how they influence each other, but end up talking about how Huang influences just Lindo by making her stronger. There is also a lot of plot summary thrown in. While that is good and illustrates the point, the author could have talked more about the points they were trying to make rather than showing us the points, leaving it to the reader to figure it out. Lastly, I also felt that Huang wasn’t shown as being too much of an opposite with Lindo. Lindo is discussed too much rather than equally. Other than those few things, I think that this author did a really good job of answering the question. They had good answers, and although I disagree a little in the way they approached them, they did it well.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Author 2: </span></p>
<p><span style="font-weight: 400;">The thesis is very strong and talks about how a minor character (the father) causes Celie to feel terrible for the rest of her life. The first body paragraph tackles Maslow’s pyramid of needs, which is a strong literary argument. However, the paragraph starts more and more to explore how females in general are oppressed rather than staying on topic with the relationship between Celie and her father. The paragraphs start to focus more and more on feminism and being a strong female. While I don’t disagree with the sentiment, I feel like the author should have talked more about the relationship and how it makes an impact on the book, rather than talking about the plot and how the main character grows and learns. I don’t really think that the main character and her father are foils, since they are antagonists with some stuff in common. If they bounce ideas off each other, it might make more sense.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Author 3: </span></p>
<p><span style="font-weight: 400;">This essay starts out making a claim that Baba and Hassan were foils because they were very similar. Foils are people who are different, and whose relationship is shown to be important. While this relationship was important, Amir often said that Baba and Hassan were similar, that Baba wanted a son more like Hassan. The first body paragraph supports the illogical thesis and introduction, so the author knows how to draw from evidence. They could have stated their point more clearly, and used more than one example to make a point. The third body paragraph moves onto differences between the two. I feel like this should have been explored more in order to have a much better essay that could have answered the prompt a whole lot better. The conclusion has very little to do with the thesis, and could work to answer a different prompt much better. Overall, a rather weak essay.</span></p>arhamHamlet Final Summary and Analysis2018-03-11T22:17:22-04:002018-03-11T22:17:22-04:00/uncategorized/2018/03/11/hamlet-final-summary-and-analysis<p><span style="font-weight: 400;">Shakespeare is the author, lived during the 1500’s to 1600’s. He was a famous playwright, and had his own theatre that he wrote his plays for.</span></p>
<p> </p>
<p><span style="font-weight: 400;">The setting is Elsinore, in Denmark. It is set during a similar time to Shakespeare, or before him.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Plot: Hamlet is a moody teenager, whose father recently died. His father’s ghost appears to him, and informs Hamlet that the current king (Hamlet’s uncle) killed King Hamlet, and that Hamlet should get revenge. Hamlet vows revenge. His family worries about him, especially his recently remarried (to his uncle) mother. His mother and his uncle seek out Hamlet’s old friends to spy on him and tell them what is wrong. Hamlet realizes that his friends are trying to spy on him. He also comes up with a plan to make sure that his uncle did actually kill his father, to verify what the ghost said. This plan involves a play that mimics what the ghost told Hamlet, and then watching the King’s reaction. Once the King reacts unfavorably, Hamlet starts making plans to kill him. Hamlet kills Polonius (mistaking him for his uncle) while talking with his mother. He is then sent to England, but he escapes and sends his old friends (Rosencrantz and Guildenstern) to their deaths. He returns, realizes his girlfriend committed suicide, and attacks her surviving brother. They all head back to the castle, where his uncle and the brother are poisoning everything. A fencing match takes place, to kill Hamlet, but Hamlet poisons the brother, who poisons Hamlet. The queen drinks a cup of poison and dies. Hamlet makes the King drink poison so he dies. Hamlet hands the throne off to another prince (Young Fortinbras) before dying.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Characters:</span></p>
<p><span style="font-weight: 400;">Hamlet – A young teenager who studies in Germany. His father just died, and he feels a strong sense of revenge against his uncle who killed him. He is quite intelligent, and driven.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Claudius – The younger brother of King Hamlet. He is shown to be quite cunning and deceitful, willing to kill his nephew to hold on to the throne with no challengers. He loves Hamlet’s mother, and never fights people in person. </span></p>
<p> </p>
<p><span style="font-weight: 400;">Laertes – Overprotective older brother. He loves his family, and is popular with the inhabitants of the castle. Willing to do anything for his honor, including poison. Maybe easy to manipulate, or Claudius is really good at manipulating.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Gertrude – Hamlet’s mother and queen of Denmark. She loved King Hamlet dearly, and according to Hamlet had a very happy relationship with him. She makes Hamlet very angry by remarrying, and we see that she has very few doubts or regrets. She does speak with Hamlet before he sets off to England, where she agrees to keep his secrets, and starts to acknowledge that she may have done something wrong. While she loves Claudius, she kept her son’s secret from him.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Narrative Voice – Since it is a play, the only narrative voice are the directions, which are brief, and to the point. Very impersonal. </span></p>
<p><span style="font-weight: 400;">Style – Point of view is everyone, since it is a play, but we get more asides from Hamlet. Since Hamlet is the main character, we get more info from him, but he is not telling the story. Tone feels more dramatic than other Shakespeare plays. Lots of comparisons and flowy writing. Because many people are royalty, Shakespeare gives them blank verse lines, which are usually iambic pentameter. The tone is also serious, since this is a tragedy. There are lots of symbols. Main ones would be the poison that is used, and the “something is rotten in the state of Denmark”. Another symbol would be the deaths of the people representing the way royalty would be dying and losing power all across Europe. </span></p>
<p> </p>
<p><span style="font-weight: 400;">Quotes: “Something is rotten in the state of Denmark”. Meaning is to show how a supernatural being can influence us all. Also relates to the poison I mentioned earlier, death, corruption, and decay.</span></p>
<p> </p>
<p><span style="font-weight: 400;">“Now cracks a noble heart. Good-night, sweet prince;</span></p>
<p><span style="font-weight: 400;">And flights of angels sing thee to thy rest.” – this is rather interesting because it is being told to someone who ends up causing many different deaths. Despite the number of people that died due to Hamlet’s actions, his best friend Horatio still believes that Hamlet will go to heaven.</span></p>
<p> </p>
<p><span style="font-weight: 400;">In Shakespeare’s Hamlet, we are taught that inaction is the weakest and worst thing that man can do.</span></p>
<p> </p>
<p><span style="font-weight: 400;">Setting – Hamlet is literally a prince. His job is to do things for his people. By neglecting that duty, he dooms Denmark. Plot – Hamlet fails to act on Claudius, preferring to make sure and dilly dally, before making too many bad decisions leading to his death. There is a lot of symbolism (that I saw) with not doing your job (inaction) and something being rotten in the state of Denmark. I can’t think of any imagery that supports this thesis, but the image of Claudius praying while Hamlet doesn’t attack him is fixated in my mind.</span></p>
<p> </p>arhamShakespeare is the author, lived during the 1500’s to 1600’s. He was a famous playwright, and had his own theatre that he wrote his plays for.Response to Course Materials 2/262018-02-26T22:14:23-05:002018-02-26T22:14:23-05:00/uncategorized/2018/02/26/response-to-course-materials-2-26<p><span style="font-weight: 400;">The first thing that we did was our final exam project. First off, I love how open ended these are. I always have way too much fun with creating games, and when it doubles as a school assignment, I can feel productive while having fun. I spent too much time on this game for homework, but it was worth it and a ton of fun in the end. I think that most of the class enjoyed it, and if we have an open ended project for Hamlet, I can’t wait to create “Stabby Hamlet”.</span></p>
<p><span style="font-weight: 400;">I have created Stabby Macbeth, Stabby Boys (this one), and I am working on a more full featured game for a class I am taking at MSU. I always love how there are well written games, that make it more of an interactive literature experience.</span></p>
<p><span style="font-weight: 400;">After that, we covered a bunch of poetry. Hearing my classmates debate and talk about the different viewpoints made me realize that there is rarely one “true” answer for any given piece of text. There are bad answers that may lack evidence, but there are a lot of good ones, influenced by prior readings. </span></p>
<p><span style="font-weight: 400;">We then started a Shakespeare unit, with Elizabethan theatre, before jumping into Hamlet. Hamlet is a good story, albeit with some missing parts almost (I really wish that Shakespeare had written an addition for it that explained more of the plot). He died in his fifties, so it isn’t unreasonable that he may have had a sequel or prequel in mind. We read through the play, which is always nice. Polonius’ lines were a pain. He didn’t have that many, but they were literally tongue twisters.</span></p>
<p><span style="font-weight: 400;">We also did the multiple choice creation exercise. Learning what it is like from the other side does better prepare us for the AP. I had a few learning moments where I realized just how College Board designs questions, and how to exploit that to do better.</span></p>
<p><span style="font-weight: 400;">The poetry projects were very cool. I loved seeing how many people were able to have different opinions on something as short as a poem. Just being forced to sit down and analyze something isn’t something I have done since middle school. Since I have learned a ton since then, it was fun to read something and give myself time to mull it over. </span></p>
<p><span style="font-weight: 400;">My goal score on the AP exam is a 5. I need to work on my writing skills a ton, I think that I have been underperforming recently. I don’t often write out the connections I make in my head, or my thesis morphs as I write. Most of this is just practice. The only way you could help me is by grading extra AP essays that I fill out by myself, and leaving helpful feedback.</span></p>arhamThe first thing that we did was our final exam project. First off, I love how open ended these are. I always have way too much fun with creating games, and when it doubles as a school assignment, I can feel productive while having fun. I spent too much time on this game for homework, but it was worth it and a ton of fun in the end. I think that most of the class enjoyed it, and if we have an open ended project for Hamlet, I can’t wait to create “Stabby Hamlet”.