<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Somē&apos;s Blog</title><description/><link>https://blog.soch.cc/</link><language>en-us</language><item><title>Atomic Counters in OpenFrameworks</title><link>https://blog.soch.cc/atomic-counters-in-openframeworks/</link><guid isPermaLink="true">https://blog.soch.cc/atomic-counters-in-openframeworks/</guid><pubDate>Sat, 27 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since OpenGL 4.2, &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Atomic_Counter&quot;&gt;atomic
counters&lt;/a&gt; have been a core
feature. They help solve a variety of problems, like counting the number of red
pixels in a fragment shader or acting as a form of shared memory that can be
altered by shaders.&lt;/p&gt;
&lt;p&gt;The use case I’m interested in is to create a particle system using compute
shaders. This means that I need to keep track of the number of particles that
are alive or dead at any given time. To do that in the GPU, I can use atomic
counters.&lt;/p&gt;
&lt;p&gt;Looking online, there were only a handful of tutorials that taught how to use
atomic counters in OpenFrameworks like &lt;a href=&quot;https://cvl-robot.hateblo.jp/entry/2017/05/19/214241&quot;&gt;this
one&lt;/a&gt;. But it required the
use of the verbose OpenGL api. &lt;a href=&quot;https://note.com/omakazu/n/n1a264820972c&quot;&gt;This
writer&lt;/a&gt; even says that OpenFrameworks
doesn’t have an API for it. But as I found out, there is! This article is just
a short and sweet walk through on how to set that up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/atomic.gif&quot; alt=&quot;A GIF of a slime mode simulation, also known as physarum simulation. It is rainbow colored, depiciting the direction in which the particles are moving.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Setting up the buffers&lt;/h2&gt;
&lt;p&gt;There is only one data type used with atomic counters and that is &lt;code&gt;atomic_uint&lt;/code&gt; . The data type that maps to this is &lt;code&gt;GLuint&lt;/code&gt;. So we need to create a &lt;code&gt;GLuint&lt;/code&gt; vector of size 1.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vector&amp;lt;GLuint&amp;gt; counter;  
counter.resize(1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason we’re using a vector is because OpenFrameworks’ &lt;code&gt;ofBufferObject&lt;/code&gt; class allows us to allocate a buffer with the data itself. So let’s create an &lt;code&gt;ofBufferObject&lt;/code&gt; and allocate it. After allocating it, we bind it, telling OpenGL that it is an atomic counter buffer at the binding point 0.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ofBufferObject counterBuffer;  
counterBuffer.allocate(counter, GL_DYNAMIC_DRAW);  
counterBuffer.bindBase(GL_ATOMIC_COUNTER_BUFFER, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Setting up the shader&lt;/h2&gt;
&lt;p&gt;I’m going to make a particle system, so I will be using compute shaders. The setup looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ofShader compute;  
compute.setupShaderFromFile(GL_COMPUTE_SHADER, &quot;shader.comp&quot;);  
compute.linkProgram();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this article, I’m making a fake particle system with 69 particles.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;compute.begin();  
compute.dispatchCompute(69,1,1);  
compute.end();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Writing the shader&lt;/h2&gt;
&lt;p&gt;Since we bound the atomic counter buffer to the binding point 0 earlier, we can access it in the shader. To access data at binding points, we can use the &lt;code&gt;layout&lt;/code&gt; qualifier in GLSL. One thing to note is that the atomic counter type is opaque. This means that we need to declare it as a uniform in the shader.&lt;/p&gt;
&lt;p&gt;After doing this, we increment the counter. We can only use special functions for the atomic counter. A full list is found &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Atomic_Counter&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// fragment shader

layout(binding = 0) uniform atomic_uint counter;  
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main(){  
    atomicCounterIncrement(counter);  
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is our fake particle.&lt;/p&gt;
&lt;h2&gt;Reading the data from ofBufferObject&lt;/h2&gt;
&lt;p&gt;For debugging, we might want to check the counter in our host application. Since we created 69 particles, we expect that the counter reads 69 since we incremented it 69 times.&lt;/p&gt;
&lt;p&gt;There are many ways of reading data from an &lt;code&gt;ofBufferObject&lt;/code&gt;, but the easiest way I’ve found is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GLuint result[1];  
glGetNamedBufferSubData(counterBuffer.getId(), 0, sizeof(GLuint), result);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method &lt;code&gt;glGetNamedBufferSubData&lt;/code&gt; loads the data from the GPU into RAM. Since we only have a buffer with a single unsigned integer (4 bytes), this shouldn’t affect our performance. But if you’re reading a lot of data, this can bottleneck your application!&lt;/p&gt;
&lt;p&gt;Anyways, the method gives you a pointer to the data. I’ve found that initializing the output data as an array instead of pointer works best. After calling the method, we can read the data with a simple &lt;code&gt;cout &amp;lt;&amp;lt; result[0] &amp;lt;&amp;lt; endl;&lt;/code&gt;. The output should be 69.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;That’s it for atomic counters! This is a simple way to get atomic counters
working without resorting to the verbose OpenGL API. I hope this has helped!&lt;/p&gt;
</content:encoded></item><item><title>The Best String Compression in JavaScript</title><link>https://blog.soch.cc/best-string-compression-in-javascript/</link><guid isPermaLink="true">https://blog.soch.cc/best-string-compression-in-javascript/</guid><description>A mini comparison of some string compression algorithms</description><pubDate>Mon, 22 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;edit: 23.05.22 - add benchmarks of gzip, zlib and deflate via wasm-flate&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lately, I needed to find a decent algorithm to compress strings in JavaScript
so that I could encode it as a URL param. Since the strings to be encoded where
&lt;a href=&quot;https://p5js.org/&quot;&gt;p5&lt;/a&gt; sketches that ran in the &lt;a href=&quot;https://p5cljs-editor.onrender.com/&quot;&gt;p5.cljs web editor&lt;/a&gt;, it was important that the
compressed string was short enough to enable longer sketches to be written.&lt;/p&gt;
&lt;h2&gt;The algorithms&lt;/h2&gt;
&lt;p&gt;After some searching on the web, I found some algorithms that I could use for this purpose. These are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.google.com/archive/p/u-lzss/&quot;&gt;u-lzss&lt;/a&gt; - an algorithm created by Google&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pieroxy/lz-string&quot;&gt;lz-string&lt;/a&gt; - an LZ-compression based algorithm&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/revolunet/843889&quot;&gt;lzw&lt;/a&gt; - another algorithm also based on LZ-compression&lt;/li&gt;
&lt;li&gt;gzip - this, and the next two algorithms, are a part of &lt;a href=&quot;https://drbh.github.io/wasm-flate/&quot;&gt;wasm-flate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;zlib&lt;/li&gt;
&lt;li&gt;deflate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&apos;t know what LZ compression is, well, I confess ... neither do I.
What I noticed was that LZ-based compression is rather ubiquitous.&lt;/p&gt;
&lt;h3&gt;Hiccups&lt;/h3&gt;
&lt;p&gt;There were many more compression algorithms that I could find for JavaScript.
However, many of them were either only for Node.js, using the &lt;code&gt;Buffer&lt;/code&gt; api, or
just didn&apos;t work. For algorithms using the &lt;code&gt;Buffer&lt;/code&gt; api, wasm implementations
are a godsend.&lt;/p&gt;
&lt;h2&gt;The benchmark&lt;/h2&gt;
&lt;p&gt;Since the only thing that mattered to me was the resulting base64 string at the
end of the compression process, the benchmarks I ran only measured the length
of the resulting string. The compression process looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;compress the string using the algorithm&lt;/li&gt;
&lt;li&gt;encode the resulting string as a &lt;code&gt;Uint8Array&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;convert this the &lt;code&gt;Uint8Array&lt;/code&gt; to base64&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I the following string sources to test the algorithms.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sketch&lt;/code&gt; - an example sketch for the p5.cljs web editor. Lines: 42.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;simplexNoise&lt;/code&gt; - a GLSL snippet for 4D simplex noise. Lines: 92.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;quil&lt;/code&gt; - a part of the &lt;a href=&quot;https://github.com/quil/quil&quot;&gt;Quil&lt;/a&gt; source code. Lines: 191.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;triangle&lt;/code&gt; - a JS wrapper for the C triangulation library &lt;a href=&quot;https://www.cs.cmu.edu/~quake/triangle.html&quot;&gt;triangle&lt;/a&gt;. Lines: 391.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;modele&lt;/code&gt; - a part of &lt;a href=&quot;https://github.com/openmusic-project/openmusic&quot;&gt;Open Music&apos;s&lt;/a&gt; source code. Lines: 588.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since &lt;code&gt;triangle&lt;/code&gt; contained template literals, I had to modify the source to escape the backticks and &lt;code&gt;$&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The results&lt;/h2&gt;
&lt;h3&gt;character count&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;sketch&lt;/th&gt;
&lt;th&gt;simplex noise&lt;/th&gt;
&lt;th&gt;quil&lt;/th&gt;
&lt;th&gt;triangle&lt;/th&gt;
&lt;th&gt;modele&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;no compression&lt;/td&gt;
&lt;td&gt;1320&lt;/td&gt;
&lt;td&gt;2936&lt;/td&gt;
&lt;td&gt;7745&lt;/td&gt;
&lt;td&gt;9112&lt;/td&gt;
&lt;td&gt;23766&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base64&lt;/td&gt;
&lt;td&gt;1760&lt;/td&gt;
&lt;td&gt;3916&lt;/td&gt;
&lt;td&gt;10328&lt;/td&gt;
&lt;td&gt;12152&lt;/td&gt;
&lt;td&gt;31688&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u-lzss&lt;/td&gt;
&lt;td&gt;1276&lt;/td&gt;
&lt;td&gt;2768&lt;/td&gt;
&lt;td&gt;5572&lt;/td&gt;
&lt;td&gt;5224&lt;/td&gt;
&lt;td&gt;15980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lz-string&lt;/td&gt;
&lt;td&gt;1100&lt;/td&gt;
&lt;td&gt;2244&lt;/td&gt;
&lt;td&gt;4444&lt;/td&gt;
&lt;td&gt;4876&lt;/td&gt;
&lt;td&gt;12292&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LZW&lt;/td&gt;
&lt;td&gt;1404&lt;/td&gt;
&lt;td&gt;2968&lt;/td&gt;
&lt;td&gt;6096&lt;/td&gt;
&lt;td&gt;6936&lt;/td&gt;
&lt;td&gt;19100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GZIP&lt;/td&gt;
&lt;td&gt;864&lt;/td&gt;
&lt;td&gt;1708&lt;/td&gt;
&lt;td&gt;3196&lt;/td&gt;
&lt;td&gt;2808&lt;/td&gt;
&lt;td&gt;7776&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZLIB&lt;/td&gt;
&lt;td&gt;848&lt;/td&gt;
&lt;td&gt;1692&lt;/td&gt;
&lt;td&gt;3180&lt;/td&gt;
&lt;td&gt;2792&lt;/td&gt;
&lt;td&gt;7760&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEFLATE&lt;/td&gt;
&lt;td&gt;840&lt;/td&gt;
&lt;td&gt;1684&lt;/td&gt;
&lt;td&gt;3172&lt;/td&gt;
&lt;td&gt;2784&lt;/td&gt;
&lt;td&gt;7752&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;../images/compression.png&quot; alt=&quot;Bar chart of the compression results by character count&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;ratio&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;sketch&lt;/th&gt;
&lt;th&gt;simplex noise&lt;/th&gt;
&lt;th&gt;quil&lt;/th&gt;
&lt;th&gt;triangle&lt;/th&gt;
&lt;th&gt;modele&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;no compression&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base64&lt;/td&gt;
&lt;td&gt;133.33%&lt;/td&gt;
&lt;td&gt;133.37%&lt;/td&gt;
&lt;td&gt;133.35%&lt;/td&gt;
&lt;td&gt;133.33%&lt;/td&gt;
&lt;td&gt;133.33%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u-lzss&lt;/td&gt;
&lt;td&gt;96.66%&lt;/td&gt;
&lt;td&gt;94.27%&lt;/td&gt;
&lt;td&gt;71.94%&lt;/td&gt;
&lt;td&gt;57.33%&lt;/td&gt;
&lt;td&gt;67.23%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lz-string&lt;/td&gt;
&lt;td&gt;83.33%&lt;/td&gt;
&lt;td&gt;76.43%&lt;/td&gt;
&lt;td&gt;57.37%&lt;/td&gt;
&lt;td&gt;53.51%&lt;/td&gt;
&lt;td&gt;51.72%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LZW&lt;/td&gt;
&lt;td&gt;106.36%&lt;/td&gt;
&lt;td&gt;101.08%&lt;/td&gt;
&lt;td&gt;78.70%&lt;/td&gt;
&lt;td&gt;76.11%&lt;/td&gt;
&lt;td&gt;80.36%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GZIP&lt;/td&gt;
&lt;td&gt;65.45%&lt;/td&gt;
&lt;td&gt;58.17%&lt;/td&gt;
&lt;td&gt;41.26%&lt;/td&gt;
&lt;td&gt;30.81%&lt;/td&gt;
&lt;td&gt;32.71%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZLIB&lt;/td&gt;
&lt;td&gt;64.24%&lt;/td&gt;
&lt;td&gt;57.62%&lt;/td&gt;
&lt;td&gt;41.05%&lt;/td&gt;
&lt;td&gt;30.64%&lt;/td&gt;
&lt;td&gt;32.65%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEFLATE&lt;/td&gt;
&lt;td&gt;63.63%&lt;/td&gt;
&lt;td&gt;57.35%&lt;/td&gt;
&lt;td&gt;40.95%&lt;/td&gt;
&lt;td&gt;30.55%&lt;/td&gt;
&lt;td&gt;32.61%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;../images/compression-ratio.png&quot; alt=&quot;compression ratio increases as file size gets larger&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The base 64 conversion of the string always expanded the length. This is just how base64 works.&lt;/p&gt;
&lt;p&gt;From this mini-benchmark, we see that the deflate algorithm performed the
best, reaching almost a compression ratio of around 30% for the largest source
text. That said, gzip, zlib and deflate have almost the same results.&lt;/p&gt;
&lt;p&gt;For all three algorithms that were tested, the compression ratio improved as
the length of the source string increased. But beyond a certain size, it seems
to go back down.&lt;/p&gt;
&lt;h2&gt;Reversibility&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;sketch&lt;/th&gt;
&lt;th&gt;simplex noise&lt;/th&gt;
&lt;th&gt;quil&lt;/th&gt;
&lt;th&gt;triangle&lt;/th&gt;
&lt;th&gt;modele&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base64&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u-lzss&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lz-string&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LZW&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GZIP&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZLIB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEFLATE&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Testing the reversibility of the algorithms, I was surprised to see that
Google&apos;s u-lzss wasn&apos;t 100% reversible. For longer source strings, the
decompressed string contained corrupted and malformed strings towards the end
of the file.&lt;/p&gt;
&lt;h3&gt;Snippet of original string&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class TriangulateIO {
    static get LENGTH() { return 23; }
    constructor(props = {}) {
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Malformed output of u-lzss&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lass TangleuleMoIO
cstr ic         etTy LENGTH({
eturn nu23; }   const tctor =(prop= pt{}{
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Compression is probably better done elsewhere than a browser environment. But
sometimes you just need to have a handy compression tool, like in the p5.cljs
editor, which shares sketches by storing them as a url param instead of in a
server somewhere. In this situation, I&apos;ll be choosing deflate for my use
case.&lt;/p&gt;
&lt;p&gt;If you want to checkout the benchmark, it&apos;s &lt;a href=&quot;https://github.com/somecho/js-string-compression-benchmarks&quot;&gt;on
github&lt;/a&gt;. Also, I
don&apos;t have a deep understanding of compression and maybe missing out on
something. If you know better, do reach out on my socials or email!&lt;/p&gt;
</content:encoded></item><item><title>How to brick (and prevent bricking) an STM8</title><link>https://blog.soch.cc/bricking-stm8/</link><guid isPermaLink="true">https://blog.soch.cc/bricking-stm8/</guid><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There&apos;s an option bit on the STM8S001J3 that, when enabled without precaution,
bricks the chip.&lt;/p&gt;
&lt;h2&gt;The Device&lt;/h2&gt;
&lt;p&gt;The STM8S001J3 is one of the cheapest 8-bit microcontroller currently on the
market. I bought a reel of 10 for 0.60€ a piece. It&apos;s a serious ATTINY85
alternative, but you&apos;ll need a SOP-8 adapter.&lt;/p&gt;
&lt;p&gt;The STM8S has a few disadvantages. It has 5 usable pins instead of the usual 6
for 8-pinned chips. It doesn&apos;t have GCC support.  It has no external reset pin.
It uses the SWIM protocol.&lt;/p&gt;
&lt;p&gt;It&apos;s also easy to brick an STM8S if you&apos;re careless.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/stm8s001j3_pinout.png&quot; alt=&quot;stm8s001j3 pinout diagram&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The Brick - Enabling the TIM2_CH1 on STM8S&lt;/h2&gt;
&lt;p&gt;For one reason or another, you may need a timer. Let&apos;s say you picked
&lt;code&gt;TIM2_CH1&lt;/code&gt;. &lt;code&gt;TIM2_CH1&lt;/code&gt; is the alternate function of pin &lt;code&gt;PC5&lt;/code&gt;. To enable
&lt;code&gt;TIM2_CH1&lt;/code&gt;, the &lt;code&gt;AFR0&lt;/code&gt; bit must be set.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/stm8s_option.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the problem. Setting the option bit &lt;code&gt;AFR0&lt;/code&gt; also remaps pin &lt;code&gt;PC6&lt;/code&gt; to its
alternate function. The pin &lt;code&gt;PC6&lt;/code&gt; is physically the same as &lt;code&gt;SWIM&lt;/code&gt;, the pin used
to program the chip. With the &lt;code&gt;SWIM&lt;/code&gt; pin now overriden, the chip is no longer
programmable. Since the STM8S does not have an external reset pin, this change
is permanent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/stm8_afr.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this state, flashing the STM8S returns &lt;code&gt;SWIM error 0x04&lt;/code&gt;. This means the chip
is bricked.&lt;/p&gt;
&lt;h2&gt;The Method&lt;/h2&gt;
&lt;p&gt;The official recommendation to prevent this from happening is surprisingly
straightforward. The application note suggests adding a 5 second delay at the
beginning of the firmware before assigning other functions to the SWIM pin.[^1]&lt;/p&gt;
&lt;p&gt;[^1]:&lt;a href=&quot;https://www.st.com/resource/en/application_note/an5047-getting-started-with-the-stm8s001j3-microcontroller-stmicroelectronics.pdf&quot;&gt;Application note AN5047&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Creative Coding with Circles</title><link>https://blog.soch.cc/creative-coding-with-circles/</link><guid isPermaLink="true">https://blog.soch.cc/creative-coding-with-circles/</guid><pubDate>Fri, 30 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This article was originally published
&lt;a href=&quot;https://uxdesign.cc/creative-coding-with-circles-fb4356b7f12c&quot;&gt;here.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/1.gif&quot; alt=&quot;A trail of elongating circles in orange and blue.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Right around the beginning of May, in the last few days of April, I began
learning &lt;a href=&quot;https://openrndr.org/&quot;&gt;OpenRNDR&lt;/a&gt;. As a way to learn the framework,
the first things I tried to code were circles. And lots of them! I coded
circles in such profundity as I’ve never had in my life.&lt;/p&gt;
&lt;p&gt;With that impulse, I found myself coding every single day and most of the work
I produced involved circles. I would like to share what I found with you!&lt;/p&gt;
&lt;h2&gt;Trails&lt;/h2&gt;
&lt;p&gt;By varying the size and the position of the circle, then drawing it as it
moves, taking care not to erase its path, the outcome is something that
resembles pulled candy. The animation at the beginning of the article was
created using this technique.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/2.gif&quot; alt=&quot;A pulsating trail of circle that looks squiggly. The color is changing all the time.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can always store previous positions in an array and then continue to vary
the size and color as new circles get drawn. You end up with something funkier!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/3.gif&quot; alt=&quot;a grid of circular tubes which are pulsating as if swallowing something. They all have different colors.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another technique is to have multiple trails being drawn at the same time. In
the animation above, it looks like the camera is panning, following the pulsing
circles. But it actually isn’t!&lt;/p&gt;
&lt;p&gt;The circles calculates its size based on the size of the circle above it. In
other words, the radii of the circles are propagated down the circle stack.
It’s a technique that artist Zach Lieberman — who I absolutely revere — uses.
By doing so, you don’t end up with a gigantic array of circles and only have to
deal with a finite number of circles.&lt;/p&gt;
&lt;h2&gt;Soddy Circles&lt;/h2&gt;
&lt;p&gt;Please don’t ask me who coined the term Soddy Circles. But let me explain it
anyways. Given three mutually tangent circles — that is, three circles touching
each other — a soddy circle is a circle that is tangent to &lt;strong&gt;all three
circles&lt;/strong&gt; at the same time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/4.gif&quot; alt=&quot;A moving diagram showing the relationship between soddy circles&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The construction of soddy circles begins with the construction of three
mutually tangent circles! Given three arbitrary points, you can always
construct three circles such that they are mutually tangent and where they
don’t contain one another.&lt;/p&gt;
&lt;p&gt;There are a algebraic methods to solve this problem, but since my math is bad,
I opted for the geometric method. The blue lines you see above are the
intermediate construction geometries that you need to first draw in order to
find the solution.&lt;/p&gt;
&lt;p&gt;Working on this problem felt like I was back in primary school, with a compass,
ruler and a piece of paper, working out what I could and couldn’t draw.&lt;/p&gt;
&lt;p&gt;With that problem solved, it’s now time to find the soddy circles! You could
read up about it before hand, but in trying to solve this, I discovered a
property about soddy circles that freaked me out. It turns out, given the
constraints, you could always find an inner soddy circle. In fact, the previous
geometric constructions help you find it!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/5.gif&quot; alt=&quot;A moving chart showing three mutually tangent circles and their inner and outer soddy circles&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, there isn’t a clear formula for finding the outer soddy circle.
Logically, I thought that if there could always be an inner circle, then there
should always be an outer circle! But this assumption is wrong.&lt;/p&gt;
&lt;p&gt;While there can always be a larger circle mutually tangent to all three, there
are times where it overlaps and thus negates the solution. Finding this out was
pretty neat but it wasn’t really artistic…yet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/6.gif&quot; alt=&quot;Mutually touching circles moving around leaving colorful trails behind&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It starts to look better with a little color and a trailing effect! The
trailing effect is the very same technique as the one mentioned in this
article.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/7.gif&quot; alt=&quot;Mutually touching circles moving around leaving colorful trails behind&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Adding some transparency and shortening the trails as new ones are drawn gives
a more interesting result.
&lt;img src=&quot;../images/circles/8.gif&quot; alt=&quot;Mutually touching circles moving around leaving colorful trails behind. It is interacted upon by a cursor.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And lastly, I added some interaction. If you’d like to play with it, &lt;a href=&quot;http://hic.art/52096&quot;&gt;click
here&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Trees&lt;/h2&gt;
&lt;p&gt;One technique I explored with circles was the idea of trees. I formulated the
problem this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start out with a circle in the center&lt;/li&gt;
&lt;li&gt;Spawn a circle that doesn’t intersect or contain another existing circle&lt;/li&gt;
&lt;li&gt;Repeat until the screen is filled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/9.gif&quot; alt=&quot;A black square is progressively filled with circles, branching out from a center one.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I animated this to solve one step of the problem per frame and coded up my own tree structure to store the relationships. Every time a circle is spawned, it stores a reference to the circle it is tangent to and vice versa.
&lt;img src=&quot;../images/circles/10.gif&quot; alt=&quot;A black square progressively filled with branching circles, but only lines between root and leaf circles are drawn, creating branch like image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Varying the size gives you patterns with different qualities. Drawing the lines between the circles shows you a root like structure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/11.gif&quot; alt=&quot;an image of a glowing, pulsating light traversing a tree, leaving a trail behind&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Again, combining this circle tree technique, I can now draw a circle trail to show the relationship between the circles!&lt;/p&gt;
&lt;p&gt;I wrote some code to traverse my circle tree so that I could draw a pulsing trail.&lt;/p&gt;
&lt;p&gt;It’s not obvious in this highly compressed gif, but I added some post processing — from within OpenRNDR itself — to lend some pizazz to the final output. Some bloom goes a long way!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/12.gif&quot; alt=&quot;traversing the circle tree but with poisson filling&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Doing Poisson fill on it yields satisfyingly smooth gradients. Click the image to see the original one on Instagram as GIF compression kills it!&lt;/p&gt;
&lt;h2&gt;Other Techniques&lt;/h2&gt;
&lt;p&gt;Here are some other circular ideas I tried out.&lt;/p&gt;
&lt;h3&gt;Spirograph&lt;/h3&gt;
&lt;p&gt;Another idea that took me back to my primary school days was the spirograph. One of those mathematical drawing kits where you traced the path of a smaller circle rolling within a larger circle.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/13.gif&quot; alt=&quot;A moving image of a wonky spirograph-like apparatus leaving pulsating trails&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Fortunately for creative coders, we don’t have to simulate everything so that it represents physical reality! You can tweak how fast the inner circle rotates around its own center as well as how fast it rotates around the larger circle. Varying this yields different patterns. In fact, I made &lt;a href=&quot;http://hic.art/59749&quot;&gt;an interactive version&lt;/a&gt; where you can do exactly just that!&lt;/p&gt;
&lt;h3&gt;Physics&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/14.gif&quot; alt=&quot;a mouse cursor herding white circles on a black background&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I have never really explored physics in creative coding before so I decided to try my hand at it!&lt;/p&gt;
&lt;p&gt;What you see at your left is a bunch of circles whose radii vary over time. They are all trying to chase the moving dot but they get pushed around by each other as they do.&lt;/p&gt;
&lt;p&gt;Doing this simulation, I noticed something. The circles naturally form some kind of local optimum for packing themselves, with the smaller circles always somewhat in the interior.&lt;/p&gt;
&lt;p&gt;As usual, I always try to see if I can iterate an idea and combine techniques I’ve already developed. An interactive version of these can be &lt;a href=&quot;http://hic.art/65103&quot;&gt;found here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&quot;Always be iterating.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Zach Lieberman’s 2017 Motto&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/15.gif&quot; alt=&quot;a mouse cursor grabs a circle and throws it around, deforming it like a pizza in the air&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another physics technique was to use spring forces!&lt;/p&gt;
&lt;p&gt;You can manually create a circle mesh and then connecting all the vertices with a spring.&lt;/p&gt;
&lt;p&gt;By varying the spring’s stiffness and damping, you alter the behavior of the circle significantly.&lt;/p&gt;
&lt;p&gt;For the interactive version, &lt;a href=&quot;http://hic.art/102576&quot;&gt;click here.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Vertex Transformations&lt;/h3&gt;
&lt;p&gt;This technique is a bit niche, but it’s based on the way the graphics card draws things on the screen — specifically, the way OpenGL draws things on the screen.&lt;/p&gt;
&lt;p&gt;Without, getting too much into detail, you can feed OpenGL a bunch of vertices, tell it to draw a circle and it will draw a circle. But why do this manually when you can just &lt;code&gt;circle()&lt;/code&gt; in any good creative coding framework?&lt;/p&gt;
&lt;p&gt;The answer: &lt;strong&gt;vertex attributes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Vertices aren’t just point on a screen. They contain data such as normals, texture coordinates and color. By manually feeding the data, you can get OpenGL to do stuff for you that normally would be too tedious to do yourself.&lt;/p&gt;
&lt;p&gt;One such thing would be gradients!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/16.gif&quot; alt=&quot;an interface showing a circle with rainbow gradients. A vertical and horizontal slider on the left and bottom moves around as the gradients change&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The interactive version can be found &lt;a href=&quot;http://hic.art/76467&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Webbing&lt;/h3&gt;
&lt;p&gt;Another reason to think in vertices is webbing!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/17.gif&quot; alt=&quot;Relationships between moving circles are drawn by connecting nearest points between them&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By defining my circles as a set of vertices, I have access to these vertices. I can do maths over them. I can iterate over each vertice and check how close they are to the vertices of other circles. If they are near enough, I draw line.&lt;/p&gt;
&lt;h3&gt;Dubin Paths&lt;/h3&gt;
&lt;p&gt;Dubin paths are a specific kind of curve. Long story short, they can be defined with circles.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/circles/18.gif&quot; alt=&quot;a trail is being drawn in dubin curves&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The algorithm for this is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trace along the circumference of a circle&lt;/li&gt;
&lt;li&gt;At an arbitrary point, spawn a circle that is tangent to the current one&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Thanks for reading this article all the way until the end! I hope some of these
techniques inspired you somewhat. I encourage you to give it a go and try it
out yourself. If you create something you feel worth sharing, reach out to me
on Instagram or Twitter!&lt;/p&gt;
&lt;p&gt;It can’t be said enough how important it is to iterate and look for
combinations. There are quite a few artists doing daily art—&lt;a href=&quot;https://www.instagram.com/mactuitui/&quot;&gt;Alexis
Andre&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/zach.lieberman/&quot;&gt;Zach
Lieberman&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/sasj_nl/&quot;&gt;Saskia
Freeke&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/ericaofanderson/&quot;&gt;Erika
Anderson&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/mactuitui/&quot;&gt;Jason
Ting&lt;/a&gt;, just to name a few — and they all
do just that: iterate.&lt;/p&gt;
&lt;h2&gt;Recommended reading/watching&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ics.uci.edu/~eppstein/junkyard/tangencies/three-circles.html&quot;&gt;Constructing tangent circles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codeproject.com/Articles/808880/Soddy-Crescent-Construction&quot;&gt;Soddy Crescent Construction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/31934617-the-book-of-circles&quot;&gt;The Book of Circles&lt;/a&gt; by Manuel Lima&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Dubins_path&quot;&gt;Dubins Paths&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0p9VxImr7Y0&amp;amp;t=646s&quot;&gt;Vertex Buffers and Drawing a Triangle in OpenGL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LPzyNOHY3A4&quot;&gt;Programming Balls #1 Circle Vs Circle Collisions C++&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Rr-5HiXquhw&amp;amp;t=1411s&quot;&gt;Coding Challenge #160: Spring Forces&lt;/a&gt; on Coding Train&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Creative Coding with Generative Colors in 3 Levels</title><link>https://blog.soch.cc/creative-coding-with-generative-colors/</link><guid isPermaLink="true">https://blog.soch.cc/creative-coding-with-generative-colors/</guid><pubDate>Fri, 07 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This article was first published in &lt;a href=&quot;https://sam-tsao.medium.com/3-levels-of-generative-colors-b43bd0d6d609&quot;&gt;a medium article&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Prelude&lt;/h2&gt;
&lt;p&gt;Alright, I’ll be honest, I’m not an expert. But I’m no spring chicken either. Still,
having started creative coding in 2019, my understanding of color hasn’t grown
much.&lt;/p&gt;
&lt;p&gt;With so many generative techniques out there like
&lt;a href=&quot;https://www.instagram.com/p/CLhO6M2gZ83/&quot;&gt;jumpflooding&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/p/CMPRF8agueR/&quot;&gt;cellular
automata&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/p/CMl_T-tgHlI/&quot;&gt;river
erosion&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/p/CM4IrUFgBny/&quot;&gt;mold slime
simulation&lt;/a&gt; and so on, it’s easy
getting caught up in the challenges of implementing them, getting blown away by
the super ultra mega cool visuals that they create and forgetting about color,
if not outright avoiding it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/1.gif&quot; alt=&quot;A GIF with two panels. The top shows black sand in a river pattern. The bottom shows a growing river in rainbow colors, showing the direction the sand is moving.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In Tim Rodenbröker’s short blog post ‘&lt;a href=&quot;https://timrodenbroeker.de/thoughts-about-color/&quot;&gt;Thoughts about
Color&lt;/a&gt;’, he admits to using
his comfort palette of black and white because he feels that the color systems
he created were cold and mechanical.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;I have thought a lot about the subject of color during my work as a designer
and also during the development of my courses, although I have always
intuitively avoided any kind of colorfulness.&quot;&lt;/p&gt;
&lt;p&gt;Tim Rodenbröker&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I share his views too. At least until I came across Quayola’s
&lt;a href=&quot;https://vimeo.com/457234035&quot;&gt;Transient&lt;/a&gt;, a work whose stunning visual effects
isn’t just attributed to its crazy algorithms, but to beautiful interplay of
colors. Since then, I’ve thought about the work everyday, especially those
colors, which have much in common with impressionistic paintings.&lt;/p&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;This article is somewhat of a documentation of the journey I’ve made thus far,
but also a brief overview of the generative color techniques. I’ve split these
techniques up into 3 levels. The word ‘level’ has other connotations too. So to
clear things up, I don’t mean that the next level is always better than the
last. I draw on the analogy of buildings, where the next level is built on top
of the previous.&lt;/p&gt;
&lt;h3&gt;Level 1: Randomness&lt;/h3&gt;
&lt;p&gt;Early on in my creative code journey, I learnt that colors on the computer
screen are represented by three values: red, green and blue. The next thing I
learnt about colors is to randomize these values!&lt;/p&gt;
&lt;p&gt;It’s the easiest way to come up with an assortment of colors. You can call a
random color function literally a thousand times and you will get a thousand
colors. But these thousand colors don’t have anything to do with each other,
both visually and conceptually. They’re just … random.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/2.gif&quot; alt=&quot;Colorful moving waves with random RGB values&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Random RGB does have its merits though. Owing to its ease of use, it’s the
technique I most often fallback to when debugging. When multiple objects have
wildly contrasting colors, their form is more obvious and has an in-your-face
vibe to it. When my school teachers said ‘Watch the colorful language!’ (in the
context of me swearing in primary school), THIS is the colorful they are
talking about.&lt;/p&gt;
&lt;p&gt;It’s not that difficult to add some context to random colors though. Instead of
using the RGB color space, we can use the HSB colorspace. By restricting the
ranges of the random values we plug into the hue, saturation and brightness
channels, the colors will be closer to each other in visual proximity. You can
get different shades of the same hue by only varying saturation and brightness,
or get analogous colors by only varying the hue a moderate amount.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/3.gif&quot; alt=&quot;A 2D GIF showing rolling waves in different layers. Each layer is a different layer of blue.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The advantage of using random colors is the ease and variation that it creates.
However, the disadvantage, which I think outweighs its advantage, is the lack
of control over the values. You’re at the mercy of the RNG gods. You could
mitigate this by choosing a Gaussian distribution (bell curve) so that you know
which values are more likely to appear. But in the end, there’s no way to map a
random value to something like time or position in space.&lt;/p&gt;
&lt;h3&gt;Level 2: Procedural&lt;/h3&gt;
&lt;h4&gt;Phase Shifting&lt;/h4&gt;
&lt;p&gt;After a while of using black/white and random color schemes, I came across this
article by &lt;a href=&quot;https://www.iquilezles.org/www/articles/palettes/palettes.htm&quot;&gt;Inigo
Quilez&lt;/a&gt;. In it,
he describes a technique to procedurally generate colors. He didn’t name the
technique, but I believe it’s called phase shifting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/4.gif&quot; alt=&quot;A GIF of a 3D blob rotating and pulsing over a black background. The blob is colorful.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The colors generated by this technique are limited, depending on the
parameters. You can generate the rainbow, but you can also generate palettes of
complementary colors or monochromatic colors by tweaking the parameters.&lt;/p&gt;
&lt;p&gt;When I want to reliably produce a color scheme that is coherent and
aesthetically pleasing, I use this technique.&lt;/p&gt;
&lt;h4&gt;Other techniques: Sampling and Displacement&lt;/h4&gt;
&lt;p&gt;This isn’t a technique to generate colors per se, but it’s a delightful way of
mixing up a visually attractive blend of colors. Below is an example where
feedback displacement of the RGB channels in a fragment shader creates crazy
colors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/5.gif&quot; alt=&quot;An abstract GIF of a sea of colors. It is very cyberpunk and shiny, turbulent and chaotic.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another way to do this is to use a source video and have the underlying video
data displace the colors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/6.gif&quot; alt=&quot;A GIF of a close up image of a tree branch melting into a watercolor moving painting.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The colors stay in the same palette as the source video, so much of the result
depends on the original composition of the video. Practicing this technique has
led to me practicing other endeavors like videography and video editing.&lt;/p&gt;
&lt;h4&gt;Other techniques: Mapping&lt;/h4&gt;
&lt;p&gt;One more way of procedurally generating colors is to use mapping. For example,
a trick I love to use, for example when I’m working with particles, is to map
the angle of movement to the hue. The result is vibrant and funky:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/7.gif&quot; alt=&quot;A GIF of a slime mode simulation, also known as physarum simulation. It is rainbow colored, depiciting the direction in which the particles are moving.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Mapping is a powerful technique with the right combination of parameters and
clever usage of color. My choice for using angles of movement and a hue mapping
makes the result resemble a normal map. With imagination, these colors will be
visually coherent and attractive and still preserve some internal logic.&lt;/p&gt;
&lt;p&gt;Procedurally generating colors has saved me the footwork for doing the critical
thinking and reflection. At the same time, it has helped me create easily
beautiful color compositions with the help of a few parameters. Except for one
thing.&lt;/p&gt;
&lt;p&gt;I still don’t know how colors work together.&lt;/p&gt;
&lt;h3&gt;Level 3: Color Theory&lt;/h3&gt;
&lt;p&gt;I’ve come so far with procedurally generating colors, but it still hasn’t
gotten me closer to understanding why Quayola’s Transient looks amazing
(seriously, go see it if you haven’t by now).&lt;/p&gt;
&lt;p&gt;I decided to revisit art class in primary school. I read up and watched videos
on color theory. One concept is central to this: color harmonies. The idea that
different colors can form different color harmonies based on their position on
the color wheel wasn’t new to me, but it never occurred to me explore these
combinations.&lt;/p&gt;
&lt;p&gt;Until (there is always an until) I came across an addon for OpenFrameworks (the
framework I use) called
&lt;a href=&quot;https://github.com/aspeteRakete/ofxColorPalette&quot;&gt;ofxColorPalette&lt;/a&gt;, which
enabled me to explore these theoretical combinations. I wrote my own addon
&lt;a href=&quot;https://github.com/chosamuel/ofxSwatch&quot;&gt;ofxSwatch&lt;/a&gt; to create palettes (it can
create gradients too). With my addon, I created color tables, with multiple
rows of different color harmonies to get a better grasp of color relationships.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/8.png&quot; alt=&quot;A grid of related colors acting like a color theory chart&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It was such a simple idea! Just like there were multiplication tables to aid
understanding the relationship of numbers, I could now make my own color tables
to aid my understanding of colors. I was really pleased at this, because when I
saw a color combination I liked, I could use that color to generate another
color table. Now with two color tables, I could mix and match or even
interpolate for a wide variety of tonally matching colors.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/colors/9.gif&quot; alt=&quot;A moving image with two panels, both showing virtual brush strokes painting the canvas in wave like patterns. The left is rosy and the right is turquoise.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;While it was still algorithmic and generative, I know had the choice to pick my
own colors and not rely on the math. This basis of color selection was, to me,
a more artistically satisfying choice.&lt;/p&gt;
&lt;h2&gt;What’s the next level ?&lt;/h2&gt;
&lt;p&gt;So far I’ve only written about how to generate this colors as general
approaches. They’re all only techniques. To add to that, these techniques
aren’t mutually exclusive. I could easily generate a color palette with color
theory but then still resort to using randomness to select them.&lt;/p&gt;
&lt;p&gt;There has to be some sort of overarching hierarchy.&lt;/p&gt;
&lt;p&gt;One really good rule to follow (it comes from the world of design), is the
60–30–10 rule, where you use your primary colors 60% of the time, secondary
colors 30% and accent colors 10%. The terms primary, secondary and accent
refers to the color scheme, not color theory.&lt;/p&gt;
&lt;p&gt;There are countless rules out there, I’m sure, but they all aim at balance. A
proper balance of colors, in their usage and meaning, can lead to a more
pleasing composition.&lt;/p&gt;
&lt;p&gt;Color is subjective. We are all built different and grew up different. The way
we see colors are different. For this reason alone, it’s safe to say that there
are no right or wrong ways to use colors. It depends on the context.&lt;/p&gt;
&lt;h3&gt;The How/Why dilemma&lt;/h3&gt;
&lt;p&gt;Then there’s also the why. These techniques are only the how. It’s still
important to think about the why. There is no way to avoid it. One way or
another, we will have to face that colors mean different things in different
cultures too. &lt;strong&gt;&lt;em&gt;Color is cultural.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For example, red is such an auspicious color and black such an inauspicious one
in Chinese culture, that I was forbidden to wear black clothes as a kid during
auspicious events such as Chinese New Year or my friend’s granny’s 90th
birthday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Color is political too.&lt;/em&gt;&lt;/strong&gt; In Myanmar, where a coup took place in February
earlier this year, green is a hated color as it represents the military. Red,
again, represents the NLD (National League for Democracy) party of Myanmar.
Since the coup, the meaning of these colors have been heightened. It would be
wrong to use the color green in a pro-democracy demonstration in Myanmar.&lt;/p&gt;
&lt;p&gt;But these are not reasons to shy away from thinking about colors. Knowing not
only the how, but also the why we use colors will bring more depth to our
artistry and craft. The things we make will not be cool empty graphics, but
will also be creations which mean something to ourselves and others.&lt;/p&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;I hope you could get something out of this article! Much of it is personal, but
many of the things I’ve learnt has also been personal musing of other artists I
respect and admire.&lt;/p&gt;
</content:encoded></item><item><title>Free Continuous Deployment for Reagent Apps with Glitch</title><link>https://blog.soch.cc/deploy-reagent-app/</link><guid isPermaLink="true">https://blog.soch.cc/deploy-reagent-app/</guid><pubDate>Sun, 18 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Edit (18.09.2025): There are now many ways to setup continuous deployment for
some of the more niche stacks out there with the most widely used platforms like
Netlify or Vercel. The information in this article is outdated.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So you have a &lt;a href=&quot;https://github.com/reagent-project/reagent&quot;&gt;Reagent&lt;/a&gt; application
you want to deploy somewhere. Probably, you have experience deploying frontend
applications on platforms like &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;,
&lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; or &lt;a href=&quot;https://render.com/&quot;&gt;Render&lt;/a&gt;. You try
this with your ClojureScript application and quickly find out that none of
these platforms support building ClojureScript applications, &lt;em&gt;because the build
environment lacks Java&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This article walks you through creating a simple continuous deployment setup
using Github Actions and &lt;a href=&quot;https://glitch.com/&quot;&gt;Glitch&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setting up a Glitch project&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;../images/glitch-hello-website.PNG&quot; alt=&quot;screenshot of the new project dialog on glitch&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Create or login to your Glitch account, then create a new
&apos;glitch-hello-website&apos; project. You should now have a simple static site
project with an &lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;script.js&lt;/code&gt; and &lt;code&gt;style.css&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, open up the Glitch project terminal and run this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config receive.denyCurrentBranch updateInstead
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;../images/glitch-terminal.PNG&quot; alt=&quot;screenshot of the glitch&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This allows you to later push updates to your Glitch project. You only need to
run this command once.&lt;/p&gt;
&lt;h3&gt;The Glitch Git repository&lt;/h3&gt;
&lt;p&gt;Your Glitch project has a Git repository which you can access. To find the URL
of the repository, click &apos;tools&apos; then &apos;Import and Export&apos;. You should now see
the URL. Take note of this URL for now. You will need this later when writing
the Github Action.&lt;/p&gt;
&lt;h2&gt;Setting up Github Actions&lt;/h2&gt;
&lt;h3&gt;Secrets&lt;/h3&gt;
&lt;p&gt;Since the Glitch Git URL is a private URL, you should create a repository
secret. To set this up, navigate &apos;settings&apos; -&amp;gt; &apos;Secrets and Variables&apos; -&amp;gt;
&apos;Actions&apos; -&amp;gt; &apos;New repository secret&apos;.&lt;/p&gt;
&lt;h3&gt;Action&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;main.yml&lt;/code&gt; file in a directory &lt;code&gt;.github/workflow/&lt;/code&gt; in your project
directory. In &lt;code&gt;main.yml&lt;/code&gt;, use the following code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# main.yml

# the name of the action
name: deploy

# when this action should be run
on: [push, workflow_dispatch]

# the jobs this action contains
jobs:

  # the name of the job as a key
  deploy:

    # the name of the job as a string
    name: Deploy to Glitch

    # the image that is used to run this job
    runs-on: ubuntu-latest

    # the instructions this job will execute
    steps:

        # checks out of the main branch and cd into the directory
      - name: Checkout Repo
        uses: actions/checkout@v3

        # sets up Java since Clojure/Script requires Java
      - name: Setup Java
        uses: actions/setup-java@v3
        with:
          distribution: &apos;adopt&apos;
          java-version: 17

        # sets up Node since Shadow CLJS requires node
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: lts/hydrogen
          cache: &apos;npm&apos;

        # install node dependencies
      - name: Install Dependencies
        run: npm install

        # Now all my project dependencies are installed,
        # it is time to build the project. You should customize
        # these steps to suit your project

        # Build project
      - name: Build project
        run: npx shadow-cljs release app

        # Here you clone the Glitch repo and reference the
        # URL you saved as a repository secret
      - name: Clone Glitch Repo
        run: git clone ${{ secrets.GLITCH_REPO }}

        # Now you need to copy your assets, replacing
        # the files in the Glitch repo. Make sure to copy
        # all the assets that you need for a working application.
        # Once done, commit and push your changes
      - name: Update and push changes
        run: |
          cd project 
          git config user.email &quot;YOUR@EMAIL&quot;
          git config user.name &quot;YOUR NAME&quot;
          git pull --rebase
          cp ../public/index.html .
          cp ../public/main.js .
          cp ../public/style.css .
          git add .
          git commit -m &quot;build and deploy&quot;
          git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Take Away&lt;/h2&gt;
&lt;p&gt;Now whenever you push changes to your repository, this action will run, build
your project and push the public assets to your Glitch repository! I use this
setup for my project &lt;a href=&quot;https://github.com/somecho/mayfly&quot;&gt;Mayfly&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://help.glitch.com/kb/article/85-pushing-local-code-to-a-project/&quot;&gt;Pushing local code to a Glitch Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/actions/security-guides/encrypted-secrets&quot;&gt;Github Secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nektos/act&quot;&gt;Running Github Actions locally&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Emulating I2S on STM32F103</title><link>https://blog.soch.cc/emulate-i2s-stm32/</link><guid isPermaLink="true">https://blog.soch.cc/emulate-i2s-stm32/</guid><pubDate>Sat, 08 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I like building audio and music related things.  Recently, I&apos;ve acquired an
&lt;a href=&quot;https://invensense.tdk.com/wp-content/uploads/2015/02/INMP441.pdf&quot;&gt;INMP441&lt;/a&gt; as an addition to my slowly growing rack of
electrical and digital components. The INMP441 is cheap digital microphone that
uses transmits data via &lt;a href=&quot;https://www.nxp.com/docs/en/user-manual/UM11732.pdf&quot;&gt;I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;S&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With this and an &lt;a href=&quot;https://www.st.com/en/microcontrollers-microprocessors/stm32f103c8.html&quot;&gt;STM32F103C8&lt;/a&gt;, I set out to make an LED react
to audio as a &lt;em&gt;&quot;Hello, World!&quot;&lt;/em&gt; of sorts.&lt;/p&gt;
&lt;h2&gt;Method&lt;/h2&gt;
&lt;p&gt;I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;S is not available on low and medium density STM32 devices[^1], but
is easily emulated, as &lt;a href=&quot;https://www.st.com/resource/en/application_note/an5086-i2s-protocol-emulation-on-stm32l0-series-microcontrollers-using-a-standard-spi-peripheral-stmicroelectronics.pdf&quot;&gt;AN5086&lt;/a&gt; demonstrates. With the INMP411, which
transmits a 32-bit dataframe comprising 24-bits of audio data, the challenge is
aligning the data as it comes.&lt;/p&gt;
&lt;p&gt;Here&apos;s the approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure SPI in receive-only master mode&lt;/li&gt;
&lt;li&gt;Configure the DMA to continuously read the SPI Data Register&lt;/li&gt;
&lt;li&gt;Use SCK to trigger a timer&lt;/li&gt;
&lt;li&gt;Use another channel on the same timer to generate the WS signal&lt;/li&gt;
&lt;li&gt;Process the DMA buffer in halves&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Step 1 - Configure SPI in receive-only master mode&lt;/h2&gt;
&lt;p&gt;In receive-only master mode (RXONLY = 1, MSTR = 1), SCK generation begins as
soon as SPE=1[^2]. The SPI transmitter is also disabled. This is handy as there
is 1 GPIO pin less to configure and trasmission doesn&apos;t have to be handled.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SPI2-&amp;gt;CR1 = SPI_CR1_MSTR |              /* Master mode */
            SPI_CR1_SSM | SPI_CR1_SSI | /* Force NSS high */
            SPI_CR1_DFF |               /* 16-bit Dataframe */
            SPI_CR1_RXONLY |            /* Receive-only mode */
            (4 &amp;lt;&amp;lt; SPI_CR1_BR_Pos);      /* SPI SCK Prescaler: 32 */
SPI2-&amp;gt;CR2 = SPI_CR2_RXDMAEN;            /* enable DMA for RX */
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SCK Frequency&lt;/h3&gt;
&lt;p&gt;I&apos;ve chosen SPI2 here because the pins are physically closer to where the
microphone is on the breadboard. SPI2 uses the APB1 clock, which has been
configured to 36MHz.  The prescaler is 32, giving an &lt;strong&gt;SCK frequency of
1.125Mhz&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Step 2 - Configuring the DMA&lt;/h2&gt;
&lt;p&gt;The DMA is configured in circular mode to continuously transfer data from
&lt;code&gt;SPI_DR&lt;/code&gt; to &lt;code&gt;rx_buf&lt;/code&gt;, the buffer that is used later for processing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;volatile uint16_t rx_buf[RX_BUF_LEN] = {0};
// ...
DMA1_Channel4-&amp;gt;CPAR = (uint32_t)&amp;amp;SPI2-&amp;gt;DR; /* read from SPI_DR */
DMA1_Channel4-&amp;gt;CMAR = (uint32_t)rx_buf;    /* write to rx_buf */
DMA1_Channel4-&amp;gt;CNDTR = RX_BUF_LEN;
DMA1_Channel4-&amp;gt;CCR = DMA_CCR_MINC |    /* Increment rx_buf index */
                     DMA_CCR_CIRC |    /* Circular mode */
                     DMA_CCR_PL_1 |    /* High priority */
                     DMA_CCR_PSIZE_0 | /* 16-bit Peripheral */
                     DMA_CCR_MSIZE_0 | /* 16-bit Memory */
                     DMA_CCR_TCIE | /* Enable transfer complete interrupt */
                     DMA_CCR_HTIE | /* Enable half transfer interrupt */
                     DMA_CCR_EN;    /* Enable DMA */

NVIC_EnableIRQ(DMA1_Channel4_IRQn); /* Enable IRQ */
NVIC_SetPriority(DMA1_Channel4_IRQn, 1); /* Set IRQ priority */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ISR sets status flags and returns.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void DMA1_Channel4_IRQHandler(void){
  uint32_t full_transfer_complete = DMA1-&amp;gt;ISR &amp;amp; DMA_ISR_TCIF4;
  if(full_transfer_complete){
    DMA1-&amp;gt;IFCR |= DMA_ISR_TCIF4;
    flags |= FLAGS_HALF_A_COMPLETE;
    return;
  }
  uint32_t half_transfer_complete = DMA1-&amp;gt;ISR &amp;amp; DMA_ISR_HTIF4;
  if(half_transfer_complete){
    DMA1-&amp;gt;IFCR |= DMA_ISR_HTIF4;
    flags |= FLAGS_HALF_B_COMPLETE;
    return;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3 - Configuring the timer to generate WS&lt;/h2&gt;
&lt;p&gt;The timer is configured to starts on the falling edge of SCK. The PWM signal
that drives WS is inverted, starting low to send the left channel first.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/i2s_timing.png&quot; alt=&quot;I2S timing chart from NXP&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define WS_PERIOD 64     /*!&amp;lt; 64 SCK cycles for every stereo data-word */
#define WS_PRESCALER 64  /*!&amp;lt; Prescale TIM1 to the same frequency as SPI_SCK */
#define WS_DUTY_CYCLE 32 /*!&amp;lt; 50% duty cycle */
// ...
TIM1-&amp;gt;ARR = WS_PERIOD - 1;
TIM1-&amp;gt;PSC = WS_PRESCALER - 1;
TIM1-&amp;gt;BDTR |= TIM_BDTR_MOE; /* TIM1 is an advance timer, this is needed to
                               enable output */
TIM1-&amp;gt;SMCR |= (6 &amp;lt;&amp;lt; TIM_SMCR_SMS_Pos) | /* Slave mode: Trigger */
              (6 &amp;lt;&amp;lt; TIM_SMCR_TS_Pos);   /* Trigger source: TI2 */
TIM1-&amp;gt;CCMR1 |= TIM_CCMR1_CC2S_0; /* Configure channel 2 as input and map to
                                    TI2 */
TIM1-&amp;gt;CCER |= TIM_CCER_CC2P; /* Triggered by falling edge of input signal */
TIM1-&amp;gt;CCMR2 |= (7 &amp;lt;&amp;lt; TIM_CCMR2_OC3M_Pos) | /* PWM Mode 2 */
               TIM_CCMR2_OC3PE;            /* Enable preload */
TIM1-&amp;gt;CCR3 = WS_DUTY_CYCLE;                /* comparator */
TIM1-&amp;gt;CCER |= TIM_CCER_CC3E;               /* Enable capture/compare */
TIM1-&amp;gt;EGR |= TIM_EGR_UG;                   /* Update registers */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 4 - Aligning and Processing the Data&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void process_buffer(uint16_t start, uint16_t len) {
  static int32_t audio_buffer[AUDIO_BUF_LEN] = {0};
  uint32_t sum_squares = 0;
  for (uint16_t i = 0; i &amp;lt; len / 4; i++) { // 1
    uint16_t i0 = (i * 4) + start;
    uint16_t i1 = i0 + 1;
    uint16_t w0 = rx_buf[i0];
    uint16_t w1 = rx_buf[i1];
    uint32_t s = ((uint32_t)w0 &amp;lt;&amp;lt; 16) | w1; // 2
    s = (s &amp;gt;&amp;gt; 6) &amp;amp; 0xFFFFFF; // 3
    if (s &amp;amp; 0x800000) { // 4
      s |= 0xFF000000;
    }
    audio_buffer[i] = (int32_t)s;
    sum_squares += (uint64_t)(audio_buffer[i] * audio_buffer[i]); // 5
  }

  uint32_t mean = sum_squares / AUDIO_BUF_LEN; // 6 
  uint32_t root_mean = mean &amp;gt;&amp;gt; 1; // 7
  TIM2-&amp;gt;CCR2 = (uint16_t)(root_mean &amp;gt;&amp;gt; 8); // 8
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(1) &lt;code&gt;rx_buf&lt;/code&gt; is iterated over in groups of 4: 2 &lt;code&gt;uint16_t&lt;/code&gt; pairs for the left
channel and 2 for the right. The right channel is ignored.&lt;/p&gt;
&lt;p&gt;(2) The pairs are &lt;code&gt;or&lt;/code&gt;ed together in a &lt;code&gt;uint32_t&lt;/code&gt;. The data is not yet aligned.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/inmp411_reconstructed.png&quot; alt=&quot;Bit structure of the reconstructed data&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Bit 23—the MSB—of the received 24-bit value ends up in bit 29 of the
reconstructed value.&lt;/p&gt;
&lt;p&gt;But wait! Why is the offset 2 when SD is supposed to have an offset of 1 with
respect to WS? This timing chart shows what happens in the emulation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/i2s_sd_timing.png&quot; alt=&quot;Timing chart&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a - When SCK starts, data is sampled on the leading edge. This corresponds to bit 31.&lt;/li&gt;
&lt;li&gt;b - On the trailing edge of SCK, TI2 detects a falling edge. The timer is started and
T1C3 starts generating WS.&lt;/li&gt;
&lt;li&gt;c - INMP411 sends MSB 1 SCK period after WS is driven low. By this time, the
DMA has already moved on to bit 30.&lt;/li&gt;
&lt;li&gt;d - On the leading edge of the 3rd SCK, the MSB gets sampled. This corresponds
to bit 29. This is why there is an extra SCK in the offset.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(3) To align the data, the value gets right shifted by 6 bits.&lt;/p&gt;
&lt;p&gt;(4) The sign bit (bit 23) is checked. If it is 1, the top byte is set to all
1&apos;s to extend the sign and get a 32-bit signed integer.&lt;/p&gt;
&lt;p&gt;(5-7) RMS is calculated.&lt;/p&gt;
&lt;p&gt;(8) The RMS value is converted to a 16-bit value and used as the duty cycle for
the LED&apos;s PWM.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is all that is necessary to emulate I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;S with SPI and a timer.
The setup is straightforward, but aligning the data required careful
consideration of where the MSB actually ended up in the data buffer.&lt;/p&gt;
&lt;p&gt;👉 The full code to this example is in &lt;a href=&quot;https://codeberg.org/some/inmp441_i2s_reactive/src/branch/main/src/main.c&quot;&gt;this Codeberg repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[^1]:Chapter 25.1,
&lt;a href=&quot;https://www.st.com/resource/en/reference_manual/rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#%5B%7B%22num%22%3A1102%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C67%2C558%2Cnull%5D&quot;&gt;RM0008&lt;/a&gt;
[^2]:Chapter 25.3.5,
&lt;a href=&quot;https://www.st.com/resource/en/reference_manual/rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#%5B%7B%22num%22%3A2245%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C124%2C208%2Cnull%5D&quot;&gt;RM0008&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Fastest GPIO Read Speed on STM32F103 (Tight Loops vs DMA)</title><link>https://blog.soch.cc/fastest-gpio-read-speed-stm32f103/</link><guid isPermaLink="true">https://blog.soch.cc/fastest-gpio-read-speed-stm32f103/</guid><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I was building &lt;a href=&quot;https://codeberg.org/some/lookshi&quot;&gt;Lookshi&lt;/a&gt;—a portable
mini logic analyzer—I asked myself: &lt;em&gt;How fast can you read GPIO on the
STM32F1?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I needed to figure out the maximum GPIO read speed on the STM32F103.  I ran four
benchmarks and measured the number of CPU cycles needed to perform 128
consecutive GPIO reads.  This small experiment taught me quite a bit about the
limitations of the STM32F103 but also the ins-and-outs of DMA.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;I used the ARM Cortex-M3 DWT (data watchpoint trigger) cycle counter to measure
how long it took for each method to perform the benchmark. The benchmarks were
compiled with &lt;code&gt;arm-none-eabi-gcc&lt;/code&gt; with &lt;code&gt;-Os&lt;/code&gt;. The STM32F103 was clocked at
72MHz.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CoreDebug-&amp;gt;DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT-&amp;gt;CYCCNT = 0;
DWT-&amp;gt;CTRL |= DWT_CTRL_CYCCNTENA_Msk;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Benchmark 1: Tight-Loop GPIO Reads&lt;/h2&gt;
&lt;p&gt;To begin with, I wanted to know how fast it would be if I just read from GPIO in
a simple for-loop.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;volatile uint32_t dest[128] = {0};
uint32_t t0 = DWT-&amp;gt;CYCCNT;
for(uint8_t i = 0; i &amp;lt; 128; ++i){
  dest[i] = GPIOA-&amp;gt;IDR;
}
uint32_t t1 = DWT-&amp;gt;CYCCNT;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Total number of cycles: &lt;strong&gt;1540&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Cycles/read: &lt;strong&gt;12.03&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Effective read frequency: &lt;strong&gt;5.98 Mhz&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reading GPIO in a tightloop allowed for reading speeds of just under 6 Mhz.
That&apos;s quite low for a logic analyzer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side note&lt;/em&gt;: It&apos;s faster to use an unsigned 32-bit buffer than a 16-bit buffer
because the processor performs an &lt;code&gt;uxth&lt;/code&gt; instruction if we store &lt;code&gt;IDR&lt;/code&gt; as a
16-bit value. This instruction throws away the top 16 bits.&lt;/p&gt;
&lt;h3&gt;Assembly&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;; setup
mov     r1, r4
ldr     r2, [r5, #4]
add.w   r4, r4, #1073741824
add.w   r4, r4, #67584
; read GPIOA-&amp;gt;IDR
ldr     r0, [r4, #8]
; write buffer
str.w   r0, [r3, r1, lsl #2]
; loop exit condition check
adds    r1, #1
cmp     r1, #128
; exit loop
bne.n   8000320 &amp;lt;main+0x44&amp;gt;
; read DWT-&amp;gt;CYCCNT
ldr     r3, [r5, #4]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at the assembly, it&apos;s actually quite a fast operation. 4 instructions to
setup the benchmark (loop and &lt;code&gt;t0&lt;/code&gt; assignment), 2 to read from GPIO and write to
buffer, 2 to check the loop exit condition, 1 to exit the loop and 1 to store
&lt;code&gt;DWT-&amp;gt;CYCCNT&lt;/code&gt; to &lt;code&gt;t1&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;APB2 Peripheral Access Latency&lt;/h3&gt;
&lt;p&gt;There are only 4 instructions per read, yet it takes 12 cycles to complete. I
learned that this is because APB2 peripheral access takes quite a few cycles. To
read from GPIO, the CPU has to cross AHB into APB2 to access the peripheral.
Considering this, we&apos;re at the physical limit of the chip.&lt;/p&gt;
&lt;h2&gt;Benchmark 2: Tight Loop in RAM (RamFunc)&lt;/h2&gt;
&lt;p&gt;This is same loop, but placed in RAM to reduce possible flash wait states.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__attribute__ ((section(&quot;.RamFunc&quot;)))
static inline void read_gpioa(void){
  for(uint8_t i = 0; i &amp;lt; 128; ++i){
    dest[i] = GPIOA-&amp;gt;IDR;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Total number of cycles: &lt;strong&gt;1699&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Cycles/read: &lt;strong&gt;13.27&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Effective read frequency: &lt;strong&gt;5.42 Mhz&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Surprisingly, this method took longer! Only 1.2 cycles more, but that&apos;s half a
megahertz in performance. The assembly was even 1 instruction less. I&apos;m not sure
why this is the case, so if anyone knows, let me know too.&lt;/p&gt;
&lt;h2&gt;Benchmark 3: Timer-Triggered DMA Sampling&lt;/h2&gt;
&lt;p&gt;The previous 2 methods tried to read GPIO as fast as possible but there were 2
obvious problems. The first problem is that there was always 2 instructions of
for-loop overhead. The second problem is that the sampling rate cannot be
controlled.&lt;/p&gt;
&lt;p&gt;A solution to this is to use the DMA triggered by a timer to directly transfer
&lt;code&gt;GPIOA-&amp;gt;IDR&lt;/code&gt; to memory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uint16_t dest[128] = {0};
// enable peripheral clocks
RCC-&amp;gt;AHBENR |= RCC_AHBENR_DMA1EN;
RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC-&amp;gt;APB2ENR |= RCC_APB2ENR_TIM1EN;
// setup timer to trigger a DMA request on an update event
TIM1-&amp;gt;ARR = 9;
TIM1-&amp;gt;DIER |= TIM_DIER_UDE;
// setup DMA
DMA1_Channel5-&amp;gt;CPAR = (uint32_t)&amp;amp;GPIOA-&amp;gt;IDR;
DMA1_Channel5-&amp;gt;CMAR = (uint32_t)dest;
DMA1_Channel5-&amp;gt;CCR |= DMA_CCR_MINC |
                      DMA_CCR_PSIZE_0 |
                      DMA_CCR_MSIZE_0;
DMA1_Channel5-&amp;gt;CNDTR = 128;
DMA1_Channel5-&amp;gt;CCR |= DMA_CCR_EN;
// benchmark
uint32_t t0 = DWT-&amp;gt;CYCCNT;
TIM1-&amp;gt;CR1 |= TIM_CR1_CEN;
while(DMA1_Channel5-&amp;gt;CNDTR &amp;gt; 0){
}
uint32_t t1 = DWT-&amp;gt;CYCCNT;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;With this method, there&apos;s a new variable: the timer period. So this benchmark
was ran with different timer periods.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Timer Frequency (MHz)&lt;/th&gt;
&lt;th&gt;Timer Period&lt;/th&gt;
&lt;th&gt;Total Cycles&lt;/th&gt;
&lt;th&gt;Cycles/Read&lt;/th&gt;
&lt;th&gt;Read Frequency (MHz)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;1569&lt;/td&gt;
&lt;td&gt;12.25&lt;/td&gt;
&lt;td&gt;5.87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.2&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1314&lt;/td&gt;
&lt;td&gt;10.26&lt;/td&gt;
&lt;td&gt;7.07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1309&lt;/td&gt;
&lt;td&gt;10.22&lt;/td&gt;
&lt;td&gt;7.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1306&lt;/td&gt;
&lt;td&gt;10.2&lt;/td&gt;
&lt;td&gt;7.05&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Much faster. With DMA, the peak reading speed is 7.05 MHz. Still low if you&apos;re
looking for an industrial logic analyzer, but I think it is good enough for
hobbyists.&lt;/p&gt;
&lt;p&gt;The DMA cannot be pushed to read faster than 10 cycles/read. Comparing the
results with the previous 2 benchmarks, removing the for-loop overhead brings a
full megahertz of performance.&lt;/p&gt;
&lt;p&gt;Note that actual sampling rate with DMA is a couple of kilohertz behind the
timer frequency. 16-bit/32-bit DMA sizes did not matter here.&lt;/p&gt;
&lt;p&gt;Now the limit of how fast we can read from GPIO on an STM32F103 has &lt;em&gt;really&lt;/em&gt;
been reached.&lt;/p&gt;
&lt;h2&gt;Benchmark 4: DMA with Interrupt on Completion&lt;/h2&gt;
&lt;p&gt;Although unlikely, I wondered if using an interrupt instead of polling &lt;code&gt;CNDTR&lt;/code&gt;
would improve performance. There is atleast 10 cycles or more of latency
associated with interrupts.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Timer Frequency (MHz)&lt;/th&gt;
&lt;th&gt;Timer Period&lt;/th&gt;
&lt;th&gt;Total Cycles&lt;/th&gt;
&lt;th&gt;Cycles/Read&lt;/th&gt;
&lt;th&gt;Read Frequency (MHz)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;1596&lt;/td&gt;
&lt;td&gt;12.46&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.2&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1340&lt;/td&gt;
&lt;td&gt;10.46&lt;/td&gt;
&lt;td&gt;6.88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1336&lt;/td&gt;
&lt;td&gt;10.43&lt;/td&gt;
&lt;td&gt;6.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1333&lt;/td&gt;
&lt;td&gt;10.47&lt;/td&gt;
&lt;td&gt;6.91&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each run took longer exactly as expected, incurring 25~30 extra CPU
cycles.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;As a beginner to embedded microcontroller programming, it&apos;s not easy to go from
datasheet and reference manual stats &lt;em&gt;(&quot;This chip runs at 72MHZ!&quot;, &quot;The formula
for DMA service time is Ts = Ta + Trd + Twr!&quot;, ...)&lt;/em&gt; to a definitive conclusion
of how fast things can &lt;em&gt;really&lt;/em&gt; go. So I encourage those like me who do not yet
have practical field experience to carry out these little benchmarks and
experiments to test your hypotheses.&lt;/p&gt;
&lt;p&gt;👉 The code for these benchmarks can be found in &lt;a href=&quot;https://codeberg.org/some/stm32f1-gpio-read-benchmark&quot;&gt;this Codeberg
repository&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Learning ClojureScript with P5.js</title><link>https://blog.soch.cc/learning-clojurescript-with-p5js/</link><guid isPermaLink="true">https://blog.soch.cc/learning-clojurescript-with-p5js/</guid><pubDate>Sun, 25 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s December and you know what this means. T&apos;is the season to learn new
languages! Lately, I have been learning ClojureScript and have been having a
blast creative coding with it. I think you should give it a try.&lt;/p&gt;
&lt;h2&gt;Who this tutorial is for&lt;/h2&gt;
&lt;p&gt;In order to follow this tutorial, you should be familiar with coding in P5
already. If you are, great! It will be a breeze to follow along. Let&apos;s get
started setting up.&lt;/p&gt;
&lt;p&gt;Please note that I am not an expert in ClojureScript. Still, I&apos;d like to share
this with you.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;I&apos;ve created a &lt;a href=&quot;https://github.com/somecho/p5cljs-template&quot;&gt;P5-CLJS template&lt;/a&gt;.
Go ahead and clone the repo to your machine and follow the instructions on the
&lt;code&gt;README.md&lt;/code&gt;. Once the terminal shows &lt;code&gt;Build completed&lt;/code&gt;, you can open
&lt;code&gt;localhost:3000&lt;/code&gt;. You should see a red screen. I recommend using VSCode with the
plugin Calva. With this, we&apos;re now ready to code.&lt;/p&gt;
&lt;h2&gt;First steps&lt;/h2&gt;
&lt;p&gt;Our sketch is located in the file &lt;code&gt;src/sketch.cljs&lt;/code&gt;. It looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(ns sketch
  (:require [goog.object :as g]
            [p5 :as p5]))

(defn setup[]
  (js/createCanvas js/window.innerWidth js/window.innerHeight))

(defn draw[]
  (js/background 255 0 0 ))

(doto js/window
  (g/set &quot;setup&quot; setup)
  (g/set &quot;draw&quot; draw))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks a lot like our familiar P5 sketch, with the setup and draw functions.
Go ahead and try to change the background of the sketch! You can do so with
this line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(js/background 100 180 180)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Syntax&lt;/h2&gt;
&lt;p&gt;This line looks a lot different than Javascript. ClojureScript is a dialect of
Lisp. You can compile it to Javascript so that you can run it in the browser.&lt;/p&gt;
&lt;p&gt;Being a Lisp, everything is an expression and every expression is surrounded by
parentheses.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(+ 420 69)&lt;/code&gt; is a valid expression but &lt;code&gt;+ (420 69)&lt;/code&gt; is not.&lt;/p&gt;
&lt;p&gt;By now you would have noticed, or you may already even know, that the first
item within the parentheses is a function. This syntax is called prefix
notation. The operation or function which we want to use comes first in an
expression. The syntax we are used to, like 1+1 is called infix notation.&lt;/p&gt;
&lt;h2&gt;JS Interop&lt;/h2&gt;
&lt;p&gt;You can call global Javascript functions from within Clojure by prefixing
functions with &lt;code&gt;js&lt;/code&gt; followed by a forward slash. For example: &lt;code&gt;(js/console.log &quot;Oi!&quot;)&lt;/code&gt; calls &lt;code&gt;console.log(&quot;Oi!&quot;)&lt;/code&gt;. You&apos;ll find that you don&apos;t need the console
logs, because &lt;code&gt;(println &quot;Oi!&quot;)&lt;/code&gt; does the same in ClojureScript.&lt;/p&gt;
&lt;p&gt;Here&apos;s an &lt;a href=&quot;https://lwhorton.github.io/2018/10/20/clojurescript-interop-with-javascript.html&quot;&gt;article about JS
Interop&lt;/a&gt;
to find out more about how to call and use JS from within ClojureScript. For
us, this would be enough to continue coding.&lt;/p&gt;
&lt;h2&gt;Rotating Rectangle&lt;/h2&gt;
&lt;p&gt;I&apos;ve changed my sketch to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(defn setup []
  (js/createCanvas js/window.innerWidth js/window.innerHeight)
  (js/rectMode js/CENTER))

(defn draw []
  (js/background 0)
  (let [x (/ js/width 2)
        y (/ js/height 2)]
    (js/translate x y)
    (js/rotate (* js/frameCount 0.01))
    (js/fill 250 0 0)
    (js/rect 0 0 100 100)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a rotating square. Notice that whenever you change and save your
code, the sketch updates immediately! It&apos;s not immediately apparent, but the
state of your program remains. For example, &lt;code&gt;frameCount&lt;/code&gt; continues
incrementing, even when you update your code. Go ahead and print it to the
console to see for yourself. This is a feature about ClojureScript I really
love.&lt;/p&gt;
&lt;p&gt;However, if you make changes to the setup function, you&apos;ll have to refresh the
page manually. For code that executes only absolutely once (like creating the
canvas), put this in setup. For code you&apos;d like to execute once every time you
change the sketch, just put it in global scope.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;let&lt;/code&gt; block&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;let&lt;/code&gt; is a &lt;em&gt;special form&lt;/em&gt;. It&apos;s used when you want to have local variables.
There are three parts to &lt;code&gt;let&lt;/code&gt;. First is the let keyword itself, followed by
&lt;em&gt;bindings&lt;/em&gt;, and then the expressions you want to run.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(let [a 50 
      b 100]
  (+ a b)) 
;;yields 150
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bindings are put in square brackets and are variable-value pairs.
Basically, in the example above, &lt;code&gt;a&lt;/code&gt; takes the value of 50 and &lt;code&gt;b&lt;/code&gt; 100. Once
the bindings have been defined, you can use the variables in your expressions.
Here is &lt;a href=&quot;https://clojuredocs.org/clojure.core/let&quot;&gt;the official documentation for ClojureScript&apos;s
let&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Tubes&lt;/h2&gt;
&lt;p&gt;Let&apos;s try this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(defn draw []
  (js/background 0)
  (js/noStroke)
  (doseq [i (range (/ js/width 4))]
    (let [x (* i 4)
          y ( js/height 2)
          time (* js/frameCount 0.05)
          theta (+ (* i 0.05) time)
          r (* (+ (* (Math/sin theta) 0.5) 0.5) 255)]
      (js/fill r)
      (js/circle x y (+ r 50)))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a row of red circles growing towards the left. This is an example on how to use loops in ClojureScript&lt;/p&gt;
&lt;h3&gt;DoSeq&lt;/h3&gt;
&lt;p&gt;To do loops, we can use the &lt;code&gt;doseq&lt;/code&gt; special block. Like let, it has three
parts. It starts with the &lt;code&gt;doseq&lt;/code&gt; keyword, followed by bindings in square
brackets and then by the expressions to be evaluated/executed. However, the
bindings here are a bit different. The value of the variable-value pairs needs
to be a sequence or a collection. This simply means an array or a list of
items. In the example above, our collection is &lt;code&gt;(range (/ js/width 4))&lt;/code&gt;, which
basically is an array with numbers from 0 until 1/4th of our screen&apos;s width. Go
ahead and print it out in the console to see what it looks like!&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://clojuredocs.org/clojure.core/doseq&quot;&gt;the official docs about
DoSeq&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h3&gt;Too many parentheses!&lt;/h3&gt;
&lt;p&gt;There was a really gnarly line in our last example, when we were calculating
the radii of the circles. Three nested parentheses! Luckily, there&apos;s a feature
in ClojureScript called threading (not to be confused with concurrency). If
you&apos;re familiar with the command line, it&apos;s similar to piping! Let&apos;s take a
look at what that looks like.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(-&amp;gt; theta 
    (Math/sin) 
    (* 0.5) 
    (+ 0.5) 
    (* 255))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expression starts with &lt;code&gt;-&amp;gt;&lt;/code&gt;, followed by our initial value &lt;code&gt;theta&lt;/code&gt;. This
gets plugged into the next expression &lt;code&gt;(Math/sin)&lt;/code&gt;. The output of this gets
plugged into &lt;code&gt;(* 0.5)&lt;/code&gt; and so on. This, to me, is really beautiful, because you
can easily shuffle, insert and append operations if you need to without
worrying about the parentheses or operation order.&lt;/p&gt;
&lt;p&gt;Here&apos;s &lt;a href=&quot;https://clojuredocs.org/clojure.core/-%3E&quot;&gt;the official documentation to ClojureScript&apos;s
-&amp;gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;State&lt;/h2&gt;
&lt;p&gt;At this point, we&apos;ve been doing everything statelessly. We don&apos;t have any
global variable which stores some data that we update. This is because
ClojureScript is by default immutable. Every expression returns a new value and
never modifies the values or variables you plug into it. This makes it really
easy to reason about the logic of our programs.&lt;/p&gt;
&lt;p&gt;However, this doesn&apos;t mean that ClojureScript cannot handle state. It does so
with something called atoms. Here&apos;s a good article about &lt;a href=&quot;http://langintro.com/cljsbook/atoms.html&quot;&gt;using state and atoms
in ClojureScript&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;About our template&lt;/h2&gt;
&lt;p&gt;We&apos;ve gone this far without really talking about the template that we are
using. The build tool that we are using is &lt;em&gt;ShadowCLJS&lt;/em&gt;. It basically acts like
a code hot reloader. In my opinion, it&apos;s the best build tool to use if you use
a lot of NPM packages.&lt;/p&gt;
&lt;p&gt;In your sketch, you&apos;ll also see that it starts out with &lt;code&gt;ns sketch&lt;/code&gt;, followed
by a couple of requires. This declares the namespace of sketch for the entire
file and pulls in the dependencies that we need: p5 and goog.object.&lt;/p&gt;
&lt;p&gt;Goog.object is a module from Google Closure&apos;s library for working with JS
objects. Here&apos;s the &lt;a href=&quot;https://google.github.io/closure-library/api/goog.object.html&quot;&gt;API for
it&lt;/a&gt;. Google
Closure library is incidentally a great library with many other modules for the
DOM, maths and more.&lt;/p&gt;
&lt;p&gt;At the bottom of our sketch file, you&apos;ll see this code block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(doto js/window
  (g/set &quot;setup&quot; setup)
  (g/set &quot;draw&quot; draw))

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This just sets the global variables of &quot;setup&quot; and &quot;draw&quot; to our setup and draw
functions, which is how P5 works. The syntax may look a bit strange, since it
is using the &lt;code&gt;doto&lt;/code&gt; special form.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(g/set js/window &quot;setup&quot; setup)
(g/set js/window &quot;draw&quot; draw)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the same as above. Since &lt;code&gt;js/window&lt;/code&gt; gets called twice, we use
the &lt;code&gt;doto&lt;/code&gt; special form to reduce this. You may later want to add your
&lt;code&gt;keyPressed&lt;/code&gt;, &lt;code&gt;mouseClicked&lt;/code&gt;, &lt;code&gt;windowResized&lt;/code&gt; functions. Using the &lt;code&gt;doto&lt;/code&gt;
special form means you don&apos;t have to type &lt;code&gt;js/window&lt;/code&gt; each time.&lt;/p&gt;
&lt;h2&gt;What next?&lt;/h2&gt;
&lt;p&gt;This is it for this tutorial! It may seem like we covered only a little, but
actually, it&apos;s quite a lot. ClojureScript, being a Lisp, has very simple
syntax. This tutorial covers everything you might need to use for basic
sketches.&lt;/p&gt;
&lt;p&gt;If you enjoyed this tutorial, I recommend going through a more thorough
document about the ins and outs of ClojureScript. This &lt;a href=&quot;https://www.learn-clojurescript.com/&quot;&gt;online
ebook&lt;/a&gt; is a great resource.&lt;/p&gt;
&lt;p&gt;It&apos;s also worth trying out different tutorials using different build tools. For
me though, this can be rather confusing. I stuck to learning &lt;a href=&quot;https://github.com/thheller/shadow-cljs&quot;&gt;about
shadow-cljs&lt;/a&gt; instead, which has been
sufficient for my needs so far.&lt;/p&gt;
&lt;p&gt;With &lt;em&gt;#Genuary2023&lt;/em&gt; just around the corner, I challenge you to follow along in
ClojureScript!&lt;/p&gt;
</content:encoded></item><item><title>One of my favorite texts</title><link>https://blog.soch.cc/one-of-my-favorite-texts/</link><guid isPermaLink="true">https://blog.soch.cc/one-of-my-favorite-texts/</guid><pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;The Trees&lt;/em&gt; by Philip Larkin[^1] was one of my favorite texts when I was doing
A-Levels literature and it remains one of my favorite poems.&lt;/p&gt;
&lt;p&gt;It has straight forward structure: three four-lined stanzas, each with
eight syllables, with one exception. Reading it over and over again, the
repetitive syllabic structure, the &lt;em&gt;verby&lt;/em&gt; lines and sparse use of adjectives
make it memorable.&lt;/p&gt;
&lt;p&gt;In particular, the image of trees as &lt;em&gt;&quot;unresting castles&quot;&lt;/em&gt; is deeply ingrained
in my mind.  When I am in Brunei, Malaysia or China, the thick lush forests
leave a deep impression on me. I look upon the masses of green and feel drawn to
their opaqueness.&lt;/p&gt;
&lt;p&gt;Their leaves form opaque walls, enshadowing the undergrowth. On windy days, they
behave like oceans, their branches undulating. On hot days after rain, mist rise
out of them like a steaming basket. But every day, they are unrevealing,
contradicting Larkin&apos;s trees by remaining green.&lt;/p&gt;
&lt;p&gt;It was only after moving to Germany that I felt the weight of &lt;em&gt;&quot;Their greenness
is a kind of grief&quot;&lt;/em&gt;. Every spring this grief grows on me in a dimension I
cannot explain. It reminds me of some kind of unescapable &lt;em&gt;Samsara&lt;/em&gt;-like cycle,
not mortality, the theme that occupies most interpreters of literature[^2].&lt;/p&gt;
&lt;p&gt;Comparing myself with the trees, I wonder: am I different from last year, or am
I the same? Do I also have &lt;em&gt;&quot;rings of grain&quot;&lt;/em&gt;, proving that I have grown, or am
I doomed to repeat my years until the day I don&apos;t? Even if I had &lt;em&gt;&quot;rings of
grain&quot;&lt;/em&gt;, are they actually signs of growth, or just a &lt;em&gt;&quot;trick of looking new&quot;&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;In my youth, when my life was still anchored in tropical lands, there was no
&lt;em&gt;&quot;yearly trick of looking new&quot;&lt;/em&gt;, just perpetual &lt;em&gt;&quot;fullgrown thickness&quot;&lt;/em&gt;. I read
the poem in a more optimistic light back then. I saw the trees as a perpetual
unstoppable growth. If one fell, another would take its place, rising up to the
opening in the sky it had just created.&lt;/p&gt;
&lt;p&gt;Having arrived back in Germany after a holiday, I notice that the trees in the
parking lot are &quot;coming into leaf&quot;, where they were still bald only 3 weeks ago.
There&apos;s this small feeling of betrayal, as if they went on with life without
waiting for me. When I look at them, I can&apos;t help feeling trapped. Like them I am
&lt;em&gt;unresting&lt;/em&gt;, but I am not a &lt;em&gt;castle&lt;/em&gt;. My &lt;em&gt;&quot;recent buds relax&quot;&lt;/em&gt;, but do not
&lt;em&gt;spread&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The grief comes not from knowing that one day, as all living things eventually
do, you will die.  It comes from knowing that you will live the same life, year
after year, regardless of underlying cycles, and trying to escape it anyways.&lt;/p&gt;
&lt;p&gt;[^1]: Link to full text: https://poetryarchive.org/poem/trees/
[^2]: &lt;a href=&quot;https://www.researchgate.net/publication/382405196_The_View_of_Death_in_Philip_Larkins_The_Trees_and_The_Building&quot;&gt;The View of Death in Philip Larkins The trees and The Building&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Programming AVR chips with an Arduino</title><link>https://blog.soch.cc/programming-avr-chips-with-arduino/</link><guid isPermaLink="true">https://blog.soch.cc/programming-avr-chips-with-arduino/</guid><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a quick reference on programming any AVR microcontroller using any
Arduino as an ISP.&lt;/p&gt;
&lt;h2&gt;Required Materials&lt;/h2&gt;
&lt;h3&gt;Hardware&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;An Arduino&lt;/li&gt;
&lt;li&gt;An AVR microcontroller&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Software&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Arduino IDE&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avr-gcc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avr-objcopy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avrdude&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1 - Flashing the Arduino&lt;/h2&gt;
&lt;p&gt;This step sets up the Arduino as an ISP. The easiest way to do so is with the
Arduino IDE. Simply load the &quot;ArduinoISP&quot; sketch and upload it to the board.
This can be found in the examples. Otherwise you can simply copy and paste it
from this &lt;a href=&quot;https://gist.github.com/srmq/0217a2844a9d82a6913ff989472a98dd&quot;&gt;gist&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Step 2 - Wire AVR microcontroller to the Arduino&lt;/h2&gt;
&lt;p&gt;Here&apos;s a table of the connections. Refer to the pinouts from the respective datasheets to findout which pins correspond.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arduino&lt;/th&gt;
&lt;th&gt;AVR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCK&lt;/td&gt;
&lt;td&gt;SCK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CIPO&lt;/td&gt;
&lt;td&gt;CIPO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPI&lt;/td&gt;
&lt;td&gt;COPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SS (Pin10 on UNO)&lt;/td&gt;
&lt;td&gt;RESET&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Step 3 - Compile&lt;/h2&gt;
&lt;p&gt;Below is an example command to compile a C source file into an ELF to the
attiny85.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;avr-gcc -Os\
    -ffunction-sections -fdata-sections\
    -Wl,--gc-sections\
    -DF_CPU=1000000UL\
    -mmcu=attiny85\
    main.c -o main.elf 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explanations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ffunction-sections -fdata-sections&lt;/code&gt; tells the compiler to split up functions
and data into their own distinct sections.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-Wl,--gc-sections&lt;/code&gt; is a passed to the linker to tell it to remove unused
sections. Because of the previous &lt;code&gt;-f&lt;/code&gt; flags, the linker can tell what to
remove.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-mmcu&lt;/code&gt; tells &lt;code&gt;avr-gcc&lt;/code&gt; which AVR instruction set to use. A complete list of
possible arguments can be found on the &lt;a href=&quot;https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html#index-mmcu&quot;&gt;gnu
website&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 4 - Convert ELF to HEX&lt;/h2&gt;
&lt;p&gt;The command below transforms the compiled ELF into an intel hex binary format,
removing the &lt;code&gt;.eeprom&lt;/code&gt; section in the process. This is the format required by
AVR chips.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;avr-objcopy -O ihex -R .eeprom main.elf main.hex
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5 - Flash&lt;/h2&gt;
&lt;p&gt;This is the last step. The command below uses &lt;code&gt;avrdude&lt;/code&gt; to flash the hex file onto the microcontroller.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;avrdude -c avrisp -p t85 -P /dev/ttyACM0 -b 19200 -U flash:w:main.hex:i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explanations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-c avrisp&lt;/code&gt; - the programmer. &lt;code&gt;avrisp&lt;/code&gt; and &lt;code&gt;stk500v1&lt;/code&gt; are both acceptable. See
this &lt;a href=&quot;https://www.nongnu.org/avrdude/user-manual/avrdude_11.html#Programmer-Definitions&quot;&gt;list of
programmers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p t85&lt;/code&gt; - the part number. Differs slightly from &lt;code&gt;-mmcu&lt;/code&gt;. See this &lt;a href=&quot;https://www.nongnu.org/avrdude/user-manual/avrdude_3.html&quot;&gt;list of part numbers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-P /dev/ttyACM0&lt;/code&gt; - the port where the Arduino is located. On Linux, this is
usually &lt;code&gt;/dev/ttyXXXX&lt;/code&gt;. This can differ from system to system, even within
Linux.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b 19200&lt;/code&gt; - the baud rate. This differs from programmer to programmer. When
none is given, it will default to the one listed in the configuration file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; depending on your system, you may have to specify an &lt;code&gt;avrdude.conf&lt;/code&gt;
file with the &lt;code&gt;-C&lt;/code&gt; flag. On my system, installing &lt;code&gt;avrdude&lt;/code&gt; created an
&lt;code&gt;avrdude.conf&lt;/code&gt; in &lt;code&gt;/etc&lt;/code&gt;. Installing the Arduino ide created one in
&lt;code&gt;/usr/lib/arduino/hardware/tools/avr/etc/avrdude.conf&lt;/code&gt;. If you&apos;re on a UNIX
based system, you can use &lt;code&gt;find / -iwholename &quot;**/**/avrdude.conf&quot;&lt;/code&gt; to find
this file. This file is essentially a mapping of programmers, part numbers,
baud rates and so on.&lt;/p&gt;
&lt;p&gt;It is also in this step where you can set the fuse bits to change the clock
divider of the AVR chip&apos;s CPU.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This &lt;a href=&quot;https://codeberg.org/some/avr-cmake-project-template&quot;&gt;AVR project
template&lt;/a&gt; uses CMake to
make these steps easier.&lt;/li&gt;
&lt;li&gt;This &lt;a href=&quot;https://youtu.be/N591sLGYWnM&quot;&gt;youtube video&lt;/a&gt; covers these steps and
contain more detailed information not covered in this article. It also covers
how to set fusebits. The video creator uses Mac so it could be useful for Mac users.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Remapping a third-party Switch Controller</title><link>https://blog.soch.cc/remapping-switch-controller/</link><guid isPermaLink="true">https://blog.soch.cc/remapping-switch-controller/</guid><pubDate>Sat, 20 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I love rhythm games. I grew up on Guitar Hero. A while back, I bought a
third-party taiko controller for my partner so that we could play &lt;a href=&quot;https://en.wikipedia.org/wiki/Taiko_no_Tatsujin&quot;&gt;Taiko no
Tatsujin&lt;/a&gt; together on the Nintendo Switch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/taiko.png&quot; alt=&quot;IINE Taiko Switch Controller&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the product listing photos, the controller looked like it was wireless.
Spoiler: it wasn&apos;t.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../images/taiko-pic.jpg&quot; alt=&quot;my Taiko controller&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You had to connect the controller via the USB cable to the Switch &lt;em&gt;and&lt;/em&gt; via a
wifi connection. This inevitably led to a poor Taiko experience and the novelty
quickly wore off. Despite this, my partner was still able to adjust her timing
to the latency and beat me many, many times.&lt;/p&gt;
&lt;h2&gt;Osu!&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;../images/osu-screenshot.png&quot; alt=&quot;Screenshot of Osu! Taiko gameplay&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Around the same time I came across &lt;a href=&quot;https://osu.ppy.sh/&quot;&gt;Osu!&lt;/a&gt;. It has a mode that resembles Taiko no
Tatsujin. PC players usually use the keyboard, typing &lt;code&gt;f&lt;/code&gt; or &lt;code&gt;j&lt;/code&gt; for
the drums and &lt;code&gt;d&lt;/code&gt; or &lt;code&gt;k&lt;/code&gt; for the rims.&lt;/p&gt;
&lt;p&gt;I wanted to play with the Taiko controller. The first thing I tried, quite
predictably, did no work.&lt;/p&gt;
&lt;p&gt;While Osu! seems to have settings for detecting and playing with controllers,
it didn&apos;t quite work out of the box like playing Steam games with Xbox or
Nintendo controllers.&lt;/p&gt;
&lt;h2&gt;Remapping&lt;/h2&gt;
&lt;p&gt;Using &lt;a href=&quot;https://github.com/jordansissel/xdotool&quot;&gt;&lt;code&gt;libxdo&lt;/code&gt;&lt;/a&gt;, I quickly hacked together a remapper, mapping the
event codes the controller emits to keystrokes &lt;code&gt;libxdo&lt;/code&gt; sends.&lt;/p&gt;
&lt;p&gt;If you&apos;re on X11, have a third party controller lying around and want to play
Osu! (like me), check out the &lt;a href=&quot;https://codeberg.org/some/iine-taiko-remapper&quot;&gt;remapper repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is quite a simple solution as it delegates the sending of keystrokes to
&lt;code&gt;libxdo&lt;/code&gt;. In the process of hacking up the remapper, I learned that &lt;a href=&quot;https://wiki.archlinux.org/title/Keyboard_input&quot;&gt;quite a few
layers&lt;/a&gt; sit in between the physical action of pressing a
key and the keystroke happening in a Linux computer.&lt;/p&gt;
&lt;p&gt;I don&apos;t fully understand it yet, but atleast I now have a highly responsive
controller to play Taiko with! 😋&lt;/p&gt;
</content:encoded></item><item><title>Testing the p5.cljs editor</title><link>https://blog.soch.cc/testing-p5-cljs-editor/</link><guid isPermaLink="true">https://blog.soch.cc/testing-p5-cljs-editor/</guid><description>Getting Jest to play well with Vite, React and Markdown</description><pubDate>Thu, 25 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There was never a time where I the JavaScript ecosystem more than when I tried
to write tests for the &lt;a href=&quot;https://github.com/somecho/p5-cljs-web-editor&quot;&gt;p5.cljs web
editor&lt;/a&gt;. Things broken left and
right, missing plugins, babel, transpiling, modules, &lt;em&gt;npm&lt;/em&gt;. &lt;strong&gt;Disclaimer&lt;/strong&gt;: I
don&apos;t hate the JS ecosystem in earnest. Just ... &lt;em&gt;sometimes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This article is about why I chose Jest as the test runner for the p5.cljs
editor and the steps necessary to get it working with third-party components I
was using.&lt;/p&gt;
&lt;h2&gt;Why not Vitest?&lt;/h2&gt;
&lt;p&gt;Because of this &lt;a href=&quot;https://github.com/uiwjs/react-codemirror/issues/506&quot;&gt;issue&lt;/a&gt;.
I love Vite. With a previous project that used Vite&apos;s Env Variables
(&lt;code&gt;import.meta.env.VARIABLE&lt;/code&gt;), the only way to set up tests without a headache
was to use Vitest. Needless to say, if you can use Vitest to test your
Vite-React project, you should. It was made for that.&lt;/p&gt;
&lt;p&gt;But now and then you hit a deadend, like the issue mentioned above with
CodeMirror. From what I understand, the issue is due to how Vitest bundles
dependencies. I&apos;m not sure how, but if you have a clue, do reach out!&lt;/p&gt;
&lt;p&gt;And this is why I switched to Jest.&lt;/p&gt;
&lt;h2&gt;Setting up Jest&lt;/h2&gt;
&lt;p&gt;My first distaste with Jest started with just how many dependencies you needed
to install for Jest to work.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also needed &lt;code&gt;@testing-library/react&lt;/code&gt; and &lt;code&gt;jest-environment-jsdom&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the installation out of the way, I got started writing my first test. The
first test rendered the base app, just to check if I got everything setup correctly.&lt;/p&gt;
&lt;h2&gt;The First Trial - ES Modules&lt;/h2&gt;
&lt;p&gt;Let&apos;s go &lt;code&gt;yarn test&lt;/code&gt; and ...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Jest encountered an unexpected token

Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some more details:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Details:

/re/dac/ted/p5-cljs-web-editor/node_modules/react-markdown/index.js:6
export {uriTransformer} from &apos;./lib/uri-transformer.js&apos;
^^^^^^

SyntaxError: Unexpected token &apos;export&apos;

&amp;gt; 1 | import ReactMarkdown from &apos;react-markdown&apos;
    | ^
  2 | import hljs from &quot;highlight.js&quot;
  3 | import { useEffect } from &apos;react&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After much researching, I learned that &lt;strong&gt;&lt;a href=&quot;https://jestjs.io/docs/ecmascript-modules&quot;&gt;Jest has limited support for ES
Modules&lt;/a&gt;&lt;/strong&gt;. Many encountered this
issue and the solution was to prevent Jest from transforming the
&lt;code&gt;react-markdown&lt;/code&gt; module. I went a head and created a &lt;code&gt;jest.config.cjs&lt;/code&gt; which
contained this configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/** @type {import(&apos;jest&apos;).Config} */
const config = {
	&quot;transform&quot;: {},
	&quot;transformIgnorePatterns&quot;: [],
};

module.exports = config;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Second Trial - JSX&lt;/h2&gt;
&lt;p&gt;Let&apos;s go &lt;code&gt;yarn test&lt;/code&gt; round 2 and ..&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Details:

/re/dac/ted/p5-cljs-web-editor/src/App.integration.test.jsx:5
import { render, cleanup, screen } from &apos;@testing-library/react&apos;
^^^^^^

SyntaxError: Cannot use import statement outside a module
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The previous error was now gone and I was treated to another error to solve.
After some researching (again), the solution was to transform &lt;code&gt;.jsx&lt;/code&gt; files so that
Jest could consume it. My &lt;code&gt;jest.config.cjs&lt;/code&gt; now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/** @type {import(&apos;jest&apos;).Config} */
const config = {
	&quot;transform&quot;: {
		&quot;^.+\\.jsx?$&quot;: &quot;babel-jest&quot;,
	},
	&quot;transformIgnorePatterns&quot;: [],
};

module.exports = config;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admittedly, this is a rookie mistake. Because if you knew how Jest worked (in
Node), you would know that &lt;code&gt;.jsx&lt;/code&gt; files needed some special treatment. But hey,
we are all learning.&lt;/p&gt;
&lt;h2&gt;The Third Trial - Markdown&lt;/h2&gt;
&lt;p&gt;Let&apos;s go &lt;code&gt;yarn test&lt;/code&gt; round 3 and ...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Details:

/re/dac/ted/p5-cljs-web-editor/src/pages/about.md:1
({&quot;Object.&amp;lt;anonymous&amp;gt;&quot;:function(module,exports,require,__dirname,__filename,jest){# About
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another &lt;code&gt;unexpected token&lt;/code&gt; error. This time for &lt;code&gt;.md&lt;/code&gt; files. Learning from my
previous mistake, I now know that you need to also transform &lt;code&gt;.md&lt;/code&gt; files for
Jest. Jest must be some special kid, huh? &lt;strong&gt;Just kidding.&lt;/strong&gt; You generally need &lt;em&gt;some&lt;/em&gt;
configuration to get markdown loading properly in a JS project.&lt;/p&gt;
&lt;p&gt;A quick search allowed the
&lt;a href=&quot;https://github.com/bitttttten/jest-transformer-mdx&quot;&gt;jest-transformer-mdx&lt;/a&gt; to
reveal itself to me. Last commit: May 26, 2021. &lt;em&gt;Yikes&lt;/em&gt;. Two years ago. Jest
went through 2 major versions since then. Jest
&lt;a href=&quot;https://github.com/jestjs/jest/releases/tag/v27.0.0&quot;&gt;27.0.0&lt;/a&gt; was released May
25, 2021. I&apos;m on Jest 29.5.0.&lt;/p&gt;
&lt;p&gt;I went and &lt;code&gt;yarn add --dev jest-transformer-mdx&lt;/code&gt; and updated my &lt;code&gt;jest.config.cjs&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/** @type {import(&apos;jest&apos;).Config} */
const config = {
	&quot;transform&quot;: {
		&quot;^.+\\.jsx?$&quot;: &quot;babel-jest&quot;,
		&quot;^.+\\.(md|mdx)$&quot;: &quot;jest-transformer-mdx&quot;,
	},
	&quot;transformIgnorePatterns&quot;: [],
};

module.exports = config; 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Fourth Trial - Transformation&lt;/h2&gt;
&lt;p&gt;Let&apos;s go &lt;code&gt;yarn test&lt;/code&gt; round 4 and ...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error: Invalid synchronous transformer module:
  &quot;/re/dac/ted/p5-cljs-web-editor/node_modules/jest-transformer-mdx/index.js&quot; specified in the &quot;transform&quot; object of Jest configuration
  must export a `process` function.
  Code Transformation Documentation:
  https://jestjs.io/docs/code-transformation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why did I expect this to work on the first go? Okay, it&apos;s not so bad though.
Jest has made it really explicit here that it&apos;s a problem with the transformer.
I was not ready to, after spending a few hours on setting Jest up, to go down
the rabbit hole of figuring out how transforming files for Jest. &lt;em&gt;Not yet&lt;/em&gt;.
This is a &lt;a href=&quot;https://github.com/bitttttten/jest-transformer-mdx/issues/25&quot;&gt;known
issue&lt;/a&gt; with the transformer.&lt;/p&gt;
&lt;p&gt;The author of the transformer
&lt;a href=&quot;https://github.com/bitttttten/jest-transformer-mdx/issues/25#issuecomment-1307501813&quot;&gt;suggested&lt;/a&gt;
rolling back to &lt;code&gt;@3.0.0-beta.0&lt;/code&gt;. I rolled back and ran the test again.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error: Invalid return value:
  `process()` or/and `processAsync()` method of code transformer found at
  &quot;/re/dac/ted/p5-cljs-web-editor/node_modules/jest-transformer-mdx/index.js&quot;
  should return an object or a Promise resolving to an object. The object
  must have `code` property with a string of processed code.
  This error may be caused by a breaking change in Jest 28:
  https://jestjs.io/docs/28.x/upgrading-to-jest28#transformer
  Code Transformation Documentation:
  https://jestjs.io/docs/code-transformation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A new error, not even more concrete. I went into
&lt;code&gt;jest-transformer-mdx/index.js&lt;/code&gt; and found this function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function createTransformer(src, filename, config) {
	const withFrontMatter = parseFrontMatter(src, config.transformerConfig)
	const jsx = mdx.sync(withFrontMatter)
	const toTransform = `import {mdx} from &apos;@mdx-js/react&apos;;${jsx}`

	return process(toTransform, filename, config).code
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It was not returning and object with a &lt;code&gt;code&lt;/code&gt; property. I changed the return value to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return process(toTransform, filename, config)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I ran &lt;code&gt;yarn test&lt;/code&gt; again.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ModuleNotFoundError: Cannot find module &apos;@mdx-js/react&apos; from &apos;src/pages/about.md&apos;

Require stack:
  src/pages/about.md
  src/App.jsx
  src/App.integration.test.jsx

    at Resolver._throwModNotFoundError (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-resolve/build/resolver.js:427:11)
    at Resolver.resolveModule (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-resolve/build/resolver.js:358:10)
    at Resolver._getVirtualMockPath (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-resolve/build/resolver.js:619:14)
    at Resolver._getAbsolutePath (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-resolve/build/resolver.js:587:14)
    at Resolver.getModuleID (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-resolve/build/resolver.js:530:31)
    at Runtime._shouldMockCjs (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:1699:37)
    at Runtime.requireModuleOrMock (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:1036:16)
    at Object.&amp;lt;anonymous&amp;gt; (/re/dac/ted/p5-cljs-web-editor/src/pages/about.md:8:14)
    at Runtime._execModule (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:1429:24)
    at Runtime._loadModule (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:1013:12)
    at Runtime.requireModule (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:873:12)
    at Runtime.requireModuleOrMock (/re/dac/ted/p5-cljs-web-editor/node_modules/jest-runtime/build/index.js:1039:21)
    at require (/re/dac/ted/p5-cljs-web-editor/src/App.jsx:12:2) {
  code: &apos;MODULE_NOT_FOUND&apos;,
  hint: &apos;&apos;,
  requireStack: [
    &apos;/re/dac/ted/p5-cljs-web-editor/src/pages/about.md&apos;,
    &apos;/re/dac/ted/p5-cljs-web-editor/src/App.jsx&apos;,
    &apos;/re/dac/ted/p5-cljs-web-editor/src/App.integration.test.jsx&apos;
  ],
  siblingWithSimilarExtensionFound: false,
  moduleName: &apos;@mdx-js/react&apos;,
  _originalMessage: &quot;Cannot find module &apos;@mdx-js/react&apos; from &apos;src/pages/about.md&apos;&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A big stack for a small error. &lt;code&gt;@mdx-js/react&lt;/code&gt;, which &lt;code&gt;jest-transformer-mdx&lt;/code&gt;
depends on, was not found. I added it as a dev dependency and ran &lt;code&gt;yarn test&lt;/code&gt; once more.&lt;/p&gt;
&lt;h2&gt;Success&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;PASS  src/App.integration.test.jsx
      renders the app (669 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.249 s
Ran all test suites.
Done in 4.24s.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally! Now I can get down to business and write some &lt;strong&gt;real&lt;/strong&gt; tests. Among which is to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mock the cljs compiler function, since it&apos;s made available globally by a
script and cannot be tested in a component&lt;/li&gt;
&lt;li&gt;mock the fetches properly for the markdown files (which I already did with
dummy valuesfor this test)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Testing is a destructive act. Well, according to the book &lt;em&gt;The Art of Software
Testing&lt;/em&gt; anyways. Through the bare act of trying to setup testing, I already
found issues with my application that needed my attention.&lt;/p&gt;
&lt;p&gt;I&apos;m not a pro at testing. &lt;em&gt;Not yet&lt;/em&gt;. But I encourage all, especially frontend
developers, to try testing their applications if they haven&apos;t already. If
you&apos;re like me, coming from a background of creative coding, you might not have
found the need to test your programs. If it looks good, it must be working.
Frontend developers might suffer this too. The act of trying to figure out
&lt;em&gt;what does it mean to have a working frontend&lt;/em&gt; or even to &lt;em&gt;actively try to
break the frontend&lt;/em&gt; will make you a more robust developer.&lt;/p&gt;
</content:encoded></item></channel></rss>