<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Seena Burns</title>
    <description>Devlog of Seena Burns
</description>
    <link>http://seenaburns.com/</link>
    <atom:link href="http://seenaburns.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 20 Jun 2019 10:03:25 -0700</pubDate>
    <lastBuildDate>Thu, 20 Jun 2019 10:03:25 -0700</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>Recreating the OFFF by Night opening shot in Houdini</title>
        <description>&lt;p&gt;Dev log time. I’ve been back in Houdini again, mesmerized by the &lt;a href=&quot;https://vimeo.com/184360631&quot;&gt;OFFF By Night
2016&lt;/a&gt; title sequence.&lt;/p&gt;

&lt;iframe src=&quot;https://player.vimeo.com/video/184360631&quot; width=&quot;550&quot; height=&quot;300&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; fullscreen&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;There are a ton of amazing shots in here, but today I’m just looking at the opening shot.
Director &lt;a href=&quot;http://www.williamarnold.tv/#/offf-by-night/&quot;&gt;William Arnold&lt;/a&gt; and artist
&lt;a href=&quot;https://www.instagram.com/eparizi/&quot;&gt;eparizi&lt;/a&gt; have some behind the scenes experiments, but no
indication of to how (other than “sop solver”)&lt;/p&gt;

&lt;p&gt;After a few experiments, here’s what I’ve gotten to:&lt;/p&gt;

&lt;video autoplay=&quot;&quot; loop=&quot;&quot; controls=&quot;&quot; poster=&quot;/assets/2019-06-20-flat-surfaces-expand.png&quot;&gt;
  &lt;source src=&quot;/assets/2019-06-20-flat-surfaces-expand.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  Your browser appears to not support HTML5 video mp4.
&lt;/video&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-06-20-flat-surfaces-expand.png&quot; alt=&quot;offf by night 2016 mill recreation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m decently happy with this recreation, but would love to hear if anyone (esp the artists on it!)
can break down the effect better.&lt;/p&gt;

&lt;h2 id=&quot;the-breakdown&quot;&gt;The Breakdown&lt;/h2&gt;

&lt;p&gt;The setup is not that complicated: a bunch of thin grid geo repeated and passed into a SOP solver.
A solver gives you the previous frame as input, and you modify how the next frame should look, the
basis of simulations.&lt;/p&gt;

&lt;p&gt;After the solver, the color and vertical position is applied. There is no shading here, separating
the layers vertically is just to fix the z-fighting as they overlap.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2019-06-20-houdini-node-graph-top.png&quot;&gt;&lt;img src=&quot;/assets/2019-06-20-houdini-node-graph-top.png&quot; alt=&quot;offf by night 2016 mill sop node graph&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;SOP level node graph&lt;/p&gt;

&lt;p&gt;Well the meat of what’s happening is in the solver itself.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Every frame, we’re sampling some random activating points (from the &lt;code class=&quot;highlighter-rouge&quot;&gt;scatter&lt;/code&gt; node above, passed in as an input).&lt;/li&gt;
  &lt;li&gt;For each point in the stripes, we see if they are within some &lt;code class=&quot;highlighter-rouge&quot;&gt;RADIUS&lt;/code&gt; of activating points. These
points are now activated.&lt;/li&gt;
  &lt;li&gt;Activated are given a velocity proportional to their distance to the activating point. There’s a
lot of extra logic to make sure the points don’t move back, and only the top line of the stripe
moves, the bottom is static. Additionally, only affect points in the hemisphere above the
activating point, so points aren’t pushed backwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With some drag this gives a neat bursting growth, and as long as the radius of affected points is
roughly greater than the max distance travelled by each point, the layers give the allusion of not
overlapping.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2019-06-20-sovler-node-graph.png&quot;&gt;&lt;img src=&quot;/assets/2019-06-20-sovler-node-graph.png&quot; alt=&quot;offf by night 2016 mill solver node graph&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Solver node graph&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-06-20-debug-vel.png&quot; alt=&quot;debug velocities&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Debug viz of velocities&lt;/p&gt;

&lt;p&gt;And roughly the &lt;code class=&quot;highlighter-rouge&quot;&gt;VEX&lt;/code&gt; involved:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// find_active_and_dir&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pchandle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;P&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radius&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;max_points&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_active&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcnumfound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pchandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group_active&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcfilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pchandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;P&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// avg(centers);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// set_vel (only applied to active group)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to_active_center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_active_center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist_scaling&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;../find_active_and_dir/radius&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;vel_scale&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist_scaling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;video autoplay=&quot;&quot; loop=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/2019-06-20-motion-experiment.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  Your browser appears to not support HTML5 video mp4.
&lt;/video&gt;

&lt;p class=&quot;caption&quot;&gt;Motion test&lt;/p&gt;

&lt;p&gt;Currently this overly uses above (greater z), like to determine the hemisphere. This breaks down if
you rotate the points as well. This should be turned into forward, say with a dot product of an
initial forward vector&lt;/p&gt;

&lt;h2 id=&quot;what-could-be-better&quot;&gt;What could be better&lt;/h2&gt;

&lt;p&gt;Unfortunately when you spend your time recreating something, it becomes more about copying exactly
than just making something good in its own way.&lt;/p&gt;

&lt;p&gt;I’m not 100% happy with the motion, and I think it’s still simpler than what happens in the real
sequence. For example, I’m fundamentally lacking these interactions:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-06-20-delayed-effect.png&quot; alt=&quot;offf by night the mill delayed effect&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Delay: the bottom yellow stripe grows after the initial movement, but is at the center of impact&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2019-06-20-expand-effect.png&quot; alt=&quot;offf by night the mill expand effect&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Expand: the red stripe grows outward after the inital burst, making it more growth-like and not like
a rounded lump&lt;/p&gt;

&lt;p&gt;I’m not convinced this is a &lt;a href=&quot;https://forums.odforce.net/topic/25534-differential-curve-growth/&quot;&gt;differential curve
growth&lt;/a&gt; like where I first saw
shots from the title sequence, but the motion is definitely more entrancing than what I have.&lt;/p&gt;
</description>
        <pubDate>Thu, 20 Jun 2019 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2019/06/20/recreating-offf-opening-shot/</link>
        <guid isPermaLink="true">http://seenaburns.com/2019/06/20/recreating-offf-opening-shot/</guid>
        
        
      </item>
    
      <item>
        <title>Wrote some ARM</title>
        <description>&lt;p&gt;Over the weekend I wrote some ARM! Specifically a program to read two numbers from &lt;code class=&quot;highlighter-rouge&quot;&gt;argv&lt;/code&gt;, parse,
add, convert back to a string, and write back to stdout with a syscall.&lt;/p&gt;

&lt;p&gt;I thought it would be peaceful in zen sort of way (it was not) and maybe be a little educational (it
was).&lt;/p&gt;

&lt;p&gt;This turned out to be a good exercise, going over reading and writing arrays to the stack, looping,
function calls and syscalls.&lt;/p&gt;

&lt;p&gt;Quick takeaways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A program is really just a giant mutable array of bytes in memory and some mutable registers. A
debugger is pretty useful here, and separately, it’s interesting to code is just data being
executed&lt;/li&gt;
  &lt;li&gt;Compiling and running ARM on x86 (with qemu) demystified some questions I had about emulation and
VMs&lt;/li&gt;
  &lt;li&gt;I overwrote all my stack frames trying to write an array because I got the stack direction wrong.
Not recommended&lt;/li&gt;
  &lt;li&gt;But the stack seems stupid cheap (just add a register and copy, though not sure how expensive the
copy is), vs running a whole bunch of allocator code + maybe a syscall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full &lt;a href=&quot;https://gist.github.com/seenaburns/70019f3217ab6392c3c245976e336978#file-strtoi-add-argv-s&quot;&gt;gist&lt;/a&gt; (quite brittle at points)&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/seenaburns/70019f3217ab6392c3c245976e336978.js&quot;&gt;&lt;/script&gt;

</description>
        <pubDate>Wed, 17 Apr 2019 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2019/04/17/wrote-some-arm/</link>
        <guid isPermaLink="true">http://seenaburns.com/2019/04/17/wrote-some-arm/</guid>
        
        
      </item>
    
      <item>
        <title>Thumbnailing in Isolate: Background workers in Electron</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/seenaburns/isolate&quot;&gt;Isolate&lt;/a&gt; is a tool I built for myself to view and organize
art reference/inspiration. It’s a power user tool; it does a few things very fast and well.&lt;/p&gt;

&lt;p&gt;While scrolling quickly, images sometimes pop in after a delay, rather than being visible
immediately as the page scrolls.&lt;/p&gt;

&lt;video autoplay=&quot;&quot; loop=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/2019-01-02-thumbnailing-demo-small.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  Your browser appears to not support HTML5 video mp4.
&lt;/video&gt;

&lt;p&gt;Is this worth fixing? Is it worth a third rewrite? Probably not. Talking to users, nobody has ever
mentioned it and I maybe was better off spending my vacation drawing or doing something other
than coding for 12 hours straight cause that’s what I was supposed to get a break from.&lt;/p&gt;

&lt;p&gt;But here were are, and fundamentally the problem is Isolate is loading full sized images to display
as thumbnails. Even reading locally from disk is not instant for a dozen images at say ~1mb each.
With some judicious css to ensure the position of visible elements is not affected by offscreen
images with unknown dimensions size means Chrome can intelligently lazily load these images, but the
problem is still visible in these few milliseconds when scrolling.&lt;/p&gt;

&lt;p&gt;Similar software will thumbnail the images, saving a resized small version of the image. But this is
computationally expensive, and not something I’d want to do on the same thread handling UI
interactions (thumbnailing my dir of 500 images takes ~20 seconds), else this goes from a problem of
barely perceived rendering lag to this-app-is-slow-hold-on-I-have-a-rant-about-Electron.&lt;/p&gt;

&lt;h1 id=&quot;problem-statement&quot;&gt;Problem statement&lt;/h1&gt;

&lt;p&gt;To step back, I’ve so far built Isolate in Electron. I want it to be cross-platform because I
draw/sculpt in Windows, and everything else in OSX. I wish this was not the case; even
cross-platform frameworks require lots of testing/platform specific work.&lt;/p&gt;

&lt;p&gt;I’d like images thumbnailed in a background thread upon opening a directory, and a small store
with metadata like hash, dimensions and thumbnail for fast querying.&lt;/p&gt;

&lt;p&gt;The open questions for me were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ve heard Javascript is slow, is this too expensive for Javascript?&lt;/li&gt;
  &lt;li&gt;How to communicate with the background thread/process&lt;/li&gt;
  &lt;li&gt;How to package and manage the application lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I prototyped some different approaches. I learned a lot, much more than just reading about them. For
the most part, are all workable. This is just my reasoning and experience with the different
directions I tried:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C++ prototype (QT, then SDL with intent of imgui)&lt;/li&gt;
  &lt;li&gt;Electron with a compiled daemon communicating locally via http&lt;/li&gt;
  &lt;li&gt;background threads in Electron: service/web workers, Node cluster/worker, second renderer process&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;cross-platform-fully-c-prototype-qtsdl&quot;&gt;Cross platform fully C++ prototype (QT/SDL)&lt;/h1&gt;

&lt;p&gt;A task like the above problem statement is straightforward and routine in a reasonably performant
language with concurrency, and I could just reimplement the whole app in such a language like C/C++.&lt;/p&gt;

&lt;p&gt;I started off with a version in QT as a common recommendation and backbone of heavy apps like
Houdini. Then, because Isolate is fairly simple, I tried SDL to build my own abstractions rather
than diving too deep into how make QT do what I want.&lt;/p&gt;

&lt;p&gt;Thoughts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;While the background worker is easier, I’d have to reimplement a lot of what I take for granted.
E.g. layout, swapping thumbnails with full-sized images based on display size, application
packaging, even establishing my own logging because I don’t have console.log. This means I have
much more control over the behavior, but also much more code to write, test across platforms and
maintain.&lt;/li&gt;
  &lt;li&gt;I value not having many abstractions/runtimes in between me and the OS API: it’s nice to be able
to just spawn a thread like every other application rather than learn how JS does it&lt;/li&gt;
  &lt;li&gt;I am not a productive C++ developer yet. Segfaults are a poor feedback loop. I’m scared of how
much of C++ is unchecked statically.&lt;/li&gt;
  &lt;li&gt;Isolate is a relatively simple application. Unlike massive 3d applications, it has a few one off
components with custom interactions and design. I’m not sure I see the value of another
mental model over the underlying OS Api. Instead I could write an OSX and Windows version with
maybe as much work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like the idea of SDL or equivalent IMGUI. I’d like to try it for a project; It’s slower to
develop in, but I find it really enjoyable to work from the ground up and make all my own
abstractions.&lt;/p&gt;

&lt;p&gt;I stopped after an initial exploration. I don’t want to spend my time building Isolate I just want
it to exist, and realistically I’m not going to invest in the development effort involved with this
approach.&lt;/p&gt;

&lt;p&gt;Even I recognized I’d be better off just living with a few images popping and actually spend time
art’ing.&lt;/p&gt;

&lt;h1 id=&quot;background-daemon-communicating-over-http&quot;&gt;Background daemon, communicating over http&lt;/h1&gt;

&lt;p&gt;Instead of rebuilding the ui, I tried leaving the ui in Javascript and deferring computational work
to a daemon process (which could be any language).&lt;/p&gt;

&lt;p&gt;In this prototype, the application execs another binary on startup. The ui communicates with the
background daemon locally over http, looking much like a typical client/server webapp.&lt;/p&gt;

&lt;p&gt;Thoughts:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Very quick to prototype, and easy to extend info a functional application. Once I was in Go, all
the concurrency, thumbnailing and database interaction was a breeze. But here I get to keep the
ease of development everywhere else.&lt;/li&gt;
  &lt;li&gt;Managing the lifecycle of an extra process was tricky. I had to take care with startup ordering,
and handling multiple instances running/daemon terminations. Originally the ui was nonfunctional
without the daemon, but I made it optional to make the app more reliable.&lt;/li&gt;
  &lt;li&gt;Security: previously user interaction is the only interface, but now an unauthenticated http
server is listening to requests locally. I’ve just been careful to limit the interface to the
daemon listing files, not taking action.&lt;/li&gt;
  &lt;li&gt;Packaging: with osx/app images the package is just a directory and can include more than one
binary. On Windows, I’d need the user to move a folder with both binaries. I overvalued the
simplicity of install, but eventually decided this is okay.&lt;/li&gt;
  &lt;li&gt;I tried Go because of ease of cross compilation, ease of concurrency and my experience from work.
After forking the daemon I saw roughly 2-4ms of overhead on requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;background-workers-in-electron&quot;&gt;Background workers in Electron&lt;/h1&gt;

&lt;p&gt;It would have been prudent to check Javascript is too slow before assuming it. As I understand it, a
common pattern in interpreted languages like Python, Ruby, Javascript (Node), is to load a compiled
dynamic library and expose it so it can be called like any other module in the host language. In
this way, packages exist for Javascript to interact with sqlite and libvips’s
compiled C/C++ code, which turns out to &lt;a href=&quot;https://twitter.com/seenaburns/status/1075458389147144193&quot;&gt;as fast as the compiled
daemon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I tried:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service/web workers, the way of having background threads in webapps. Neither of which have access
to Node/Electron’s APIs making reading the original images not possible?&lt;/li&gt;
  &lt;li&gt;Node’s cluster module to fork a duplicate of the Electron renderer process, but it crashed on
startup because the mismatch between Node and Electron’s flags.&lt;/li&gt;
  &lt;li&gt;Node workers, but these are still experimental and not available unless Node is explicitly
compiled with them.&lt;/li&gt;
  &lt;li&gt;There’s a pattern in Electron to start a second renderer process with a hidden window and use IPC
as a way of getting a background process. Doing this worked, but the app was obviously sluggish
when thumbnailing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thoughts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Even if I could get it working, communication with workers was challenging to get right. I’m not
sure I understand the lifecycle with service workers.&lt;/li&gt;
  &lt;li&gt;I thought packaging would be easier in all JS, but native modules need to be compiled for the
version of Node Electron is using. This complicates the dev and production build. I’m using a lot
of tools that work like magic (webpack, electron-builder), and I’d like to avoid having to support
an abnormal usage like I did with &lt;a href=&quot;https://github.com/seenaburns/isolate/issues/19&quot;&gt;bs-platform&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;back-to-the-daemon&quot;&gt;Back to the daemon&lt;/h1&gt;

&lt;p&gt;It’s fair to say all these approaches are workable, and perhaps I signed some off too early.
Ultimately, I’d really like Isolate to be as low maintenance as possible, and the daemon seemed to
suit that well.&lt;/p&gt;

&lt;p&gt;I still really like Electron; some tasks a lot faster to do with an interactive gui, and it’s really
powerful to quickly build tools for myself. As much vitriol as there is around it, I’d love to hear
more/look into about how people construct large apps that run well like VSCode and Discord.&lt;/p&gt;
</description>
        <pubDate>Wed, 02 Jan 2019 00:00:00 -0800</pubDate>
        <link>http://seenaburns.com/2019/01/02/thumbnailing-in-isolate/</link>
        <guid isPermaLink="true">http://seenaburns.com/2019/01/02/thumbnailing-in-isolate/</guid>
        
        
      </item>
    
      <item>
        <title>Lessons on exec from 4:40pm on a friday</title>
        <description>&lt;p&gt;A few Fridays back I got a message from a friend:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;also puzzler for you:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls -la /path/to/my/executable
-r-xr-xr-x 1 root root 6559032 Jan 1 1970 /path/to/my/executable

$ /path/to/my/executable
-bash: /path/to/my/executable: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a few sad guesses, I asked my coworker and got:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Oh, it’s a dynamically linked file missing the loader&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;What&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;When you execute a (dynamically linked) executable, it’s not actually executing the file, the
kernel runs the loader. It’s not in the right path so it errors. Here&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, but first, context! &lt;a href=&quot;/building-c-programs/&quot;&gt;Before, when we talked about building C programs&lt;/a&gt;, we saw when compiling a binary, you can package all object code together
in one file (static linked) or distribute libraries/modules independently in shared object archives
that are installed separately and loaded at runtime (dynamic linking). When a dynamically executed
file is started, it relies on a linker/loaded to load all the shared object files into memory before
executing.&lt;/p&gt;

&lt;p&gt;Now you’re on the same page as I was: something’s going wrong with loading, but why file not found?
It’s the kernel doing all this? How does this all happen?&lt;/p&gt;

&lt;p&gt;The demo went something like:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker run -it --rm ubuntu

$ ldd /bin/mv
  ...
	/lib64/ld-linux-x86-64.so.2
  ...

$ apt-get update
$ apt-get install file
$ file /bin/mv
/bin/mv: ELF 64-bit LSB shared object
  x86-64, version 1 (SYSV)
  dynamically linked
  interpreter /lib64/ld-linux-x86-64.so.2
  for GNU/Linux 3.2.0
  ...

# Or with readelf
$ apt-get install binutils
$ readelf /bin/mv -l
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  ...
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/bin/mv&lt;/code&gt; is dynamically linked with &lt;code class=&quot;highlighter-rouge&quot;&gt;/lib64/ld-linux-x86-64.so.2&lt;/code&gt; as the program interpreter. You
can run programs with it directly too!&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ /lib64/ld-linux-x86-64.so.2 /bin/ls -al /bin/ls
-rwxr-xr-x 1 root root 133792 Jan 18  2018 /bin/ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But when it’s not located on the right path, the kernel errors trying to load it.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mv /lib64/ld-linux-x86-64.so.2 /tmp/

$ ls
bash: /bin/ls: No such file or directory

$ mv /tmp/ld-linux-x86-64.so.2 /lib64/
bash: /bin/mv: No such file or directory

# uh oh

$ /tmp/ld-linux-x86-64.so.2 /bin/mv /tmp/ld-linux-x86-64.so.2 /lib64/
# Things are okay now
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve reproduced our problem! And so ended the workweek.&lt;/p&gt;

&lt;h2 id=&quot;ld-linux&quot;&gt;ld-linux&lt;/h2&gt;

&lt;p&gt;And thus began weekend reading! To start with, &lt;a href=&quot;https://linux.die.net/man/8/ld-linux&quot;&gt;ld-linux(8)&lt;/a&gt;
points out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;dynamic linker can be run either indirectly by running some dynamically linked program or library
(e.g. ELF binaries in the &lt;code class=&quot;highlighter-rouge&quot;&gt;PT_INTERP&lt;/code&gt; program header) or directly by running: &lt;code class=&quot;highlighter-rouge&quot;&gt;/lib/ld-linux.so.*
[OPTIONS] [PROGRAM [ARGUMENTS]]&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;I think being able to run directly is for convenience, but hey we saw that.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Also&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;ld.so and ld-linux.so* find and load the shared libraries needed by a program, prepare the
program to run, and then run it.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;This hints at how this is happening, namely that &lt;code class=&quot;highlighter-rouge&quot;&gt;ld-linux&lt;/code&gt; is actually running the program in
addition to loading shared libraries&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-programs-get-run&quot;&gt;How programs get run&lt;/h2&gt;

&lt;p&gt;lwn.net has some pretty good articles on what happens when a program is exec’d:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://lwn.net/Articles/630727/&quot;&gt;How programs get run&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lwn.net/Articles/631631/&quot;&gt;How programs get run: ELF binaries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Namely, a number of executable formats are defined as instances of &lt;code class=&quot;highlighter-rouge&quot;&gt;struct linux_binfmt&lt;/code&gt;, the most
interesting to me being:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://elixir.bootlin.com/linux/v4.18.16/source/fs/binfmt_script.c&quot;&gt;binfmt_script&lt;/a&gt;: files that start with &lt;code class=&quot;highlighter-rouge&quot;&gt;#!&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://elixir.bootlin.com/linux/v4.18.16/source/fs/binfmt_misc.c&quot;&gt;binfmt_misc&lt;/a&gt;: dynamically defined formats in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/sys/fs/binfmt_misc&lt;/code&gt;, which &lt;a href=&quot;https://blog.jessfraz.com/post/nerd-sniped-by-binfmt_misc/&quot;&gt;Jessie Frazelle dove into to script go&lt;/a&gt;, and Java uses to exec &lt;code class=&quot;highlighter-rouge&quot;&gt;.jar&lt;/code&gt; files&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://elixir.bootlin.com/linux/v4.18.16/source/fs/binfmt_elf.c&quot;&gt;binfmt_elf&lt;/a&gt;: ELF binaries, standard Linux binary format&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some initialization, the kernel iterates over each format, having each attempt to load the
binary file, and returning the first that successfully recognizes it without returning &lt;code class=&quot;highlighter-rouge&quot;&gt;ENOEXEC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, &lt;code class=&quot;highlighter-rouge&quot;&gt;binfmt_script&lt;/code&gt; matches on the &lt;code class=&quot;highlighter-rouge&quot;&gt;#!&lt;/code&gt; at the start, parses the interpreter in the first
line, and recursively execs on it (which then matches to another binfmt). The original file is
passed in as argv[0] like:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat args.sh
#! /bin/bash
echo &quot;args: $*&quot;

$ cat test
#! args.sh

$ ./test
args: ./test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could write a much more interesting interpreter like say, python, that actually uses the given
file.&lt;/p&gt;

&lt;p&gt;Additionally, fun facts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Because &lt;code class=&quot;highlighter-rouge&quot;&gt;BINPRM_BUF_SIZE&lt;/code&gt; is set to 128, the &lt;code class=&quot;highlighter-rouge&quot;&gt;#! interpreter&lt;/code&gt; will fail to match an interpreter
path longer than 128 bytes (minus like 3 or 4 bytes)&lt;/li&gt;
  &lt;li&gt;We just recursed into another &lt;code class=&quot;highlighter-rouge&quot;&gt;binfmt_script&lt;/code&gt; executable. You can’t do this forever, &lt;code class=&quot;highlighter-rouge&quot;&gt;exec&lt;/code&gt;
returns &lt;code class=&quot;highlighter-rouge&quot;&gt;ELOOP&lt;/code&gt; after 4 levels: &lt;code class=&quot;highlighter-rouge&quot;&gt;-bash: ./5: 4: bad interpreter: Too many levels of symbolic
links&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But from our demo, we were dealing with &lt;code class=&quot;highlighter-rouge&quot;&gt;binfmt_elf&lt;/code&gt; which does a lot more than &lt;code class=&quot;highlighter-rouge&quot;&gt;binfmt_script&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The lwn article covers all the heavy lifting to load and initialize the program and kernel state,
but here I’ll focus on the dynamic loading.&lt;/p&gt;

&lt;p&gt;If the &lt;code class=&quot;highlighter-rouge&quot;&gt;PT_INTERP&lt;/code&gt; program header is present, the interpreter is itself loaded into memory as well.
The entry point is replaced with the interpreter’s, and execution begins with the interpreter (the
linker here) loading shared libraries and resolving symbols in userspace. After which, the
interpreter jumps to the original entry point, and actual execution begins.&lt;/p&gt;

&lt;h2 id=&quot;why-did-this-all-happen&quot;&gt;Why did this all happen&lt;/h2&gt;

&lt;p&gt;My friend, being the Nix enthusiast that he is, has all sorts of interesting puzzlers. Nix wants to
load it’s own Glibc, and programs must be either compiled with a correct ELF interpreter or patched
afterwards with &lt;a href=&quot;https://nixos.org/patchelf.html&quot;&gt;patchelf&lt;/a&gt;, which rewrites the interpreter header.&lt;/p&gt;

&lt;p&gt;I believe this was a program compiled with Nix, installed into a regular, Docker-built container (so
only the regular &lt;code class=&quot;highlighter-rouge&quot;&gt;/lib/ld-linux&lt;/code&gt; is present) and boom, thus arises a fun problem most developers
won’t ever have to deal with.&lt;/p&gt;
</description>
        <pubDate>Sat, 20 Oct 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/10/20/lessons-on-exec-from-440-on-a-friday/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/10/20/lessons-on-exec-from-440-on-a-friday/</guid>
        
        
      </item>
    
      <item>
        <title>Migrating Isolate to ReasonML + React</title>
        <description>&lt;p&gt;I’ve just finished moving &lt;a href=&quot;https://github.com/seenaburns/isolate&quot;&gt;Isolate&lt;/a&gt; to
&lt;a href=&quot;https://reasonml.github.io/&quot;&gt;ReasonML&lt;/a&gt; and &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt;. Today’s post is about that.&lt;/p&gt;

&lt;p&gt;Now, I’m no frontend dev. I spend my time at work thinking about systems and infrastructure,
migrating things, building incremental value while working towards some far off vision.&lt;/p&gt;

&lt;p&gt;Then I come home and work on Isolate, an app I’ve made for myself, to managing art reference and
inspiration. Honestly, I just want to have it, not to build it. None of this thinking hard,
following best practices, whatever. I’ve been playing it fast and loose with global variables,
redundant code, unclear interfaces. And what do you know I’ve found myself in the odd position of
having incurred technical debt in a side project.&lt;/p&gt;

&lt;p&gt;One day, when all the problems are solved and there’s nothing left to do, I’ll move this to QT. But
for now, I just wanted to clean things up a bit and move back to adding some features.&lt;/p&gt;

&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;React: Great!&lt;/p&gt;

&lt;p&gt;Reason: Great!&lt;/p&gt;

&lt;h2 id=&quot;react&quot;&gt;React&lt;/h2&gt;
&lt;h3 id=&quot;imposes-code-structure&quot;&gt;Imposes code structure&lt;/h3&gt;

&lt;p&gt;As I worked, I broke up code into logically grouped modules. This is nice for library-esque modules
like &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt;, but did little to organize the application as a whole. Largely it ended up with code
reaching across interfaces and no real abstraction.&lt;/p&gt;

&lt;p&gt;React imposed some much needed code structure. In React, you define components, essentially
functions that define how to construct some HTML. An application is built by composing these
components in a hierarchy: a component can render html, or nest calls to other components.&lt;/p&gt;

&lt;p&gt;React automagically handles re-drawing components when the arguments (“props”) of the components
change, offloading the significant headache of managing how to subscribe to changes, or updating
dependents.&lt;/p&gt;

&lt;p&gt;This is also really nice because it’s a lot easier to reason about components in isolation. All the
dependencies are described as props (and typed with Reason!).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/seenaburns/isolate/blob/master/src/Modal.re&quot;&gt;Modal.re&lt;/a&gt; is a good example, here
it only depends on &lt;code class=&quot;highlighter-rouge&quot;&gt;State.Modal.state&lt;/code&gt; and an action to update the modal state &lt;code class=&quot;highlighter-rouge&quot;&gt;State.Modal.action =&amp;gt;
unit&lt;/code&gt; and then defines how to render the HTML given the current state. This is compared to
&lt;a href=&quot;https://github.com/seenaburns/isolate/blob/47db329009c33d568ee195aa42a2fc85334285cc/src/modal.js&quot;&gt;before&lt;/a&gt;
where the modal module described how to mutate the modal DOM object. You can probably have written a
lot better javascript than that without React, but it really pushes down a good structure.&lt;/p&gt;

&lt;h3 id=&quot;state&quot;&gt;State&lt;/h3&gt;

&lt;p&gt;I haven’t found a good way to have sibling components affect each other (probably good I haven’t),
and so React forced me to accept what is shared state. E.g. if the header contents depends on if there is
an active search, that’s not search-local state, it’s either header-local or global. Because Isolate
has relatively little state, I’ve just been pushing it up to a global state if it’s shared.&lt;/p&gt;

&lt;p&gt;ReasonReact includes a mini Redux model, where a stateful component includes a function &lt;code class=&quot;highlighter-rouge&quot;&gt;(action,
state) =&amp;gt; state&lt;/code&gt; that pattern matches on the possible actions and defines how to update the state.&lt;/p&gt;

&lt;p&gt;Unfortunately I’m still struggling to find a good way of composing the reducer function. ReasonReact
encourages putting a lot into it, but &lt;a href=&quot;https://github.com/seenaburns/isolate/blob/master/src/Main.re#L80&quot;&gt;my main
reducer&lt;/a&gt; is getting a bit
excessive.&lt;/p&gt;

&lt;h3 id=&quot;declarative&quot;&gt;Declarative&lt;/h3&gt;

&lt;p&gt;Previously, the UI would be imperatively toggled, which scaled abysmally. Clicking on an image would
trigger adding &lt;code class=&quot;highlighter-rouge&quot;&gt;display: block&lt;/code&gt; on the modal container, updating a bunch of fields in the process.
New UI elements would each mutate each other’s DOM element, and the DOM became this tangled
implicit, global state.&lt;/p&gt;

&lt;p&gt;In React, you describe what should be rendered given a component’s props, not how to get there. With
JSX (inlining HTML in Javascript), this is incredibly convenient.&lt;/p&gt;

&lt;p&gt;All together, previously searching would hide/show elements in the header like:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;hide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;hide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#search-controls'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#search-controls a'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;hide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#search-controls'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notably, every possible path to updating state would have to update the DOM. Now, instead, the
header describes how to be rendered:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;render: self =&amp;gt; {
  ...
  &amp;lt;header&amp;gt;
    &amp;lt;Search ... /&amp;gt;
    (if (!self.state.search)) {
      &amp;lt;div&amp;gt;
        &amp;lt;h3&amp;gt; (ReasonReact.string(pwd)) &amp;lt;/h3&amp;gt;
        &amp;lt;Directories ... /&amp;gt;
      &amp;lt;/div&amp;gt;;
    } else {
      ReasonReact.null;
    })
  &amp;lt;/header&amp;gt;;
  ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;reasonml&quot;&gt;ReasonML&lt;/h2&gt;

&lt;p&gt;Reason (OCaml) feels like a really tight, consistent language. The docs are great, and focus on
motivating the switching from Javascript.&lt;/p&gt;

&lt;p&gt;Having types is wonderful; adding types for relative and absolute filepaths saved me so many bugs.
Refactoring is much saner when you repeatedly compile and fix errors until the build succeeds.&lt;/p&gt;

&lt;p&gt;Reason does a really good job with Javascript interop. It’s easy to inject raw Javascript, and wrap
it in types. The output of Bucklescript (Reason compiler) is just a (surprisingly readable)
transpiled javascript file, so it’s easy to include in a Javascript file for the other way around.&lt;/p&gt;

&lt;p&gt;Most of Isolate is the UI, so I haven’t gone too deep into Reason. To keep it pragmatic, it says a
lot that it added value without getting in my way. If that’s not convincing, I will say I look
forward to using it again.&lt;/p&gt;

&lt;h3 id=&quot;troubles&quot;&gt;Troubles&lt;/h3&gt;

&lt;p&gt;Briefly, the pains I faced were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ReasonML’s compiler errors are very vague. These two errors comprise almost all the errors I ever
saw:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File &quot;/Users/seena/Developer/isolate/src/Main.re&quot;, line 14, characters 0-1:
Error: 3724: &amp;lt;UNKNOWN SYNTAX ERROR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This has type:
  string
But somewhere wanted:
  Isolate.Path.base (defined as Isolate.Path.base)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Mutating Javascript value from Reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I couldn’t for the life of me find a way to set &lt;code class=&quot;highlighter-rouge&quot;&gt;document.onclick&lt;/code&gt; to a function defined in Reason
from Reason.&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Jul 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/07/02/migrating-isolate-to-reason-react/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/07/02/migrating-isolate-to-reason-react/</guid>
        
        
      </item>
    
      <item>
        <title>Writing To The Framebuffer</title>
        <description>&lt;p&gt;I’ve been thinking about cross platform GUIs, and one question led to the next and eventually I
was asking: how do pixels end up on the monitor? A &lt;a href=&quot;https://electronics.stackexchange.com/questions/102695/how-does-a-gpu-cpu-communicate-with-a-standard-display-output-hdmi-dvi-etc&quot;&gt;pretty neat stack overflow
response&lt;/a&gt; answers:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The image displayed on the monitor is stored in your computer’s video RAM on
the graphics card in a structure called a framebuffer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;The data in the video RAM can be generated by the GPU or by the CPU. The
video RAM is continuously read out by a specialized DMA component on the video
card and sent to the monitor. The signal output to the monitor is either an
analog signal (VGA) where the color components are sent through digital to
analog converters before leaving the card, or a digital signal in the case of
HDMI or DVI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now, you probably realize that for a 1920x1080 display with 4 bytes per
pixel, you only need about 8 MB to store the image, but the video RAM in your
computer is probably many times that size. This is because the video RAM is not
only intended for storing the framebuffer. The video RAM is directly connected
to the GPU, a special purpose processor designed for efficient 3D rendering and
video decoding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is super informative, and also explains why my monitors are directly plugged into my GPU via DVI.
Another answer I found said no modern operating system will let you access the framebuffer directly.
BUT LUCKILY THAT’S WRONG.  With another &lt;a href=&quot;https://unix.stackexchange.com/questions/192206/is-it-possible-to-access-to-the-framebuffer-in-order-to-put-a-pixel-on-the-scree&quot;&gt;stackexchange
tip&lt;/a&gt;,
it’s as easy as:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /dev/urandom &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/fb0
&lt;span class=&quot;nt&quot;&gt;-bash&lt;/span&gt;: /dev/fb0: Permission denied
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo cat&lt;/span&gt; /dev/urandom &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/fb0
&lt;span class=&quot;nt&quot;&gt;-bash&lt;/span&gt;: /dev/fb0: Permission denied
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-al&lt;/span&gt; /dev/fb0
crw-rw---- 1 root video 29, 0 Apr  4 00:21 /dev/fb0
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;adduser seena video
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /dev/urandom &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/fb0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Glossing over why sudo would mysteriously not work, this writes a bunch of noise out to the monitor:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-04-framebuffer-noise.jpg&quot; alt=&quot;framebuffer urandom noise&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Awesome! The &lt;a href=&quot;https://www.kernel.org/doc/Documentation/fb/framebuffer.txt&quot;&gt;kernel framebuffer docs&lt;/a&gt;
have some general info, but the gist is &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/fb0&lt;/code&gt; acts like any other memory device in &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev&lt;/code&gt; and
we can write to it like a file. Since it’s just a memory buffer, any write will overwrite the
existing value, which you can see when the console prompt immeidately overwrites the noise.&lt;/p&gt;

&lt;p&gt;Also, you can even read from the framebuffer, taking a screenshot with &lt;code class=&quot;highlighter-rouge&quot;&gt;cp /dev/fb0
screenshot&lt;/code&gt; which would have been helpful before I took a bunch of pictures with my phone.&lt;/p&gt;

&lt;h2 id=&quot;gradients&quot;&gt;Gradients&lt;/h2&gt;

&lt;p&gt;Alright, so the stackexchange answer also gives a script to write out a red box, but I wanted to
take a shot at a gradient. After permuting the following lines a bunch of times, I settled on: the
monitor is written to one horizontal line at a time, with one byte for Blue,Green,Red,Alpha(?) per
pixel (seems like 24-bit true color).&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;xsize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1920&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ysize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1200&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'data.out'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'wb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ysize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ysize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-04-framebuffer-gradient.jpg&quot; alt=&quot;framebuffer gradient&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;text&quot;&gt;Text&lt;/h2&gt;

&lt;p&gt;I’ve been running this from the linux console (rather than x server or another display server),
which I believe is just rendering text and basic colors to the framebuffer. I should be able to do
that too. I grabbed &lt;a href=&quot;https://github.com/phallus/fonts/blob/master/lemon.bdf&quot;&gt;Lemon&lt;/a&gt;, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Glyph_Bitmap_Distribution_Format&quot;&gt;bitmap
font&lt;/a&gt; I used to use. Bitmap fonts
describe characters as a grid of bits, which can be easily rendered without much thought about the
complexity/horrors of text rendering, aliasing and the likes.&lt;/p&gt;

&lt;p&gt;I started a janky reader for BDF files, and then started to regret this idea and my python script.
Nevertheless, I produced a single “lock” icon from the font, drawn to the framebuffer! This could be
extended to write out a text string, but again, it was really janky.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-04-framebuffer-text.jpg&quot; alt=&quot;framebuffer text&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;images&quot;&gt;Images&lt;/h2&gt;

&lt;p&gt;Okay so at this point, it’s not much of a stretch to render out an image to the framebuffer. We’ve
already got a smooth color gradient and an image is just a file format we have to read pixel data to
and encode/dump to the framebuffer. I exported an image as a TGA file (just an uncompressed format
for easy parsing, also tried BMP), and used &lt;a href=&quot;https://github.com/MircoT/pyTGA/&quot;&gt;pyTGA&lt;/a&gt; to write the
image straight out. What’s neat is you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;seek&lt;/code&gt; to skip over pixels, rather than having offset
write out black pixels.&lt;/p&gt;

&lt;p&gt;Here’s the little TGA renderer python script:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tga&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Usage: tga.py xsize xoff yoff input output&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;g_xsize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g_xoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g_yoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g_input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g_output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tga&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_header&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'wb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_yoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_xsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEEK_CUR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_xoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEEK_CUR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_pixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byteorder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'little'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_xsize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_xoff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEEK_CUR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Running in bash&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; python3 tga.py 1920 750 300 test.tga /dev/fb0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-04-framebuffer-image.jpg&quot; alt=&quot;framebuffer image&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;It works! Image rendered straight to the framebuffer&lt;/p&gt;

&lt;h2 id=&quot;framebuffer-extras&quot;&gt;Framebuffer Extras&lt;/h2&gt;

&lt;p&gt;This has been super useful in connecting a bunch of loose threads I’ve had in my mind. It explains,
for example, why the final step of OpenGL is rendering out to a framebuffer, and why &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Default_Framebuffer#Double_buffering&quot;&gt;double
buffering&lt;/a&gt; (rendering to a
hidden buffer, then swapping) helps (especially given how slow my python program is).&lt;/p&gt;

&lt;p&gt;On the GUI side, From what I’ve found, a lot of custom GUI engines mostly just
&lt;a href=&quot;https://en.wikipedia.org/wiki/Bit_blit&quot;&gt;blit&lt;/a&gt; together bitmaps, and X11, along with &lt;a href=&quot;https://en.wikipedia.org/wiki/Linux_framebuffer&quot;&gt;many
others&lt;/a&gt; (“MPlayer, links2, Netsurf, fbida,[2] and
fim[3] and libraries such as GGI, SDL, GTK+, and Qt”) render out to the framebuffer. Some
&lt;a href=&quot;http://cs107e.github.io/&quot;&gt;university classes&lt;/a&gt; even use Raspberry PIs to mess around with the
framebuffer, and there’s some useful reading there because the architecture is very straightforward.&lt;/p&gt;
</description>
        <pubDate>Wed, 04 Apr 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/04/04/writing-to-the-framebuffer/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/04/04/writing-to-the-framebuffer/</guid>
        
        
      </item>
    
      <item>
        <title>Quad Remapping</title>
        <description>&lt;p&gt;&lt;a href=&quot;/2018/03/26/generating-road-from-curve/&quot;&gt;Last week I finished&lt;/a&gt; with road geometry
in Houdini. This alone is pretty bland; I want to be able to map geometry along the road.
Specifically, I want to take an arbitrary input quad (four points that form a box) with whatever
geometry inside the bounds, and map it to a target quad, preserving the relative positions of the
inner geometry.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-02-quad-remapping-problem.png&quot; alt=&quot;quad remapping problem&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Input shown, target quad outlined&lt;/p&gt;

&lt;p&gt;This really smells like a change of basis to me. If I could express the initial corner positions,
and the target positions as vector spaces, I would just need to find the single transformation
matrix that would take any point from the intial space to the target space. Here’s where I found out
how rusty my linear algebra is.&lt;/p&gt;

&lt;p&gt;I sort of got somewhere by expressing a 2d space, with the basis vectors being along the diagonals
of the quad. Then any point could be expressed as a linear combination of these orthogonal axes.
Remapping is using the same linear combination with the new basis vectors. It worked for corners but
lots of distortion around points off the basis vectors.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-02-quad-remapping-bases-grid.png&quot; alt=&quot;quad remapping bases road&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Remapping a starting grid, intermediate points distorted&lt;/p&gt;

&lt;p&gt;Bummer. This is really haunting me, because I’d really like to believe it’s possible and I’m setting
it up wrong, but I also question if my example transformation - translating a single corner - is
even a linear transformation.&lt;/p&gt;

&lt;p&gt;At some point I threw out that idea and started to think that instead of diagonals I could take two
parallel sides of the initial quad, and express each point as a percentage along each line, and then
a percentage between those two points.&lt;/p&gt;

&lt;p&gt;I got a hunch that maybe, like a &lt;a href=&quot;https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_curves&quot;&gt;bezier
curve&lt;/a&gt;, I could identify a point’s
relative position by computing the percentage along two of the parallel sides of the initial quad,
getting two P0 and P1. Then I could compute the percentage between P0 and P1. Remapping would
involve using the same percentages, but with those parallel sides transformed to the target. &lt;a href=&quot;https://math.stackexchange.com/questions/315828/transformation-between-square-and-a-polygon&quot;&gt;This
stackexchange
answer&lt;/a&gt;
backed up my hunch, but generalized to more complex polygons.&lt;/p&gt;

&lt;p&gt;Then I realized, textures do this everyday! UV mapping takes a 2d input texture (which is just a
quad) and maps it to some target geometry. Here it’s a simpler problem, because my target isn’t some
complex polygon; UV mapping should easily handle texturing a quad. Turns out, it’s the same
technique, but much more formally described: &lt;a href=&quot;https://en.wikipedia.org/wiki/Bilinear_interpolation&quot;&gt;Bilinear
interpolation&lt;/a&gt;! Finally, the y/vertical axis
just needs to be offset along the target quad normal, rather than the world up.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-02-quad-remapping-bilinear-grid.png&quot; alt=&quot;quad remapping bilinear grid&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Bilinear interpolation for grid&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-04-02-quad-remapping-road.png&quot; alt=&quot;quad remapping road&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Original input, repeatedly remapped along a poly strip.&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Apr 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/04/02/quad-remapping/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/04/02/quad-remapping/</guid>
        
        
      </item>
    
      <item>
        <title>Generating road from a curve in Houdini</title>
        <description>&lt;p&gt;Inn the &lt;a href=&quot;/2018/03/02/car-controller/&quot;&gt;car controller post&lt;/a&gt;, you can see I’ve
been living the &lt;a href=&quot;https://kenney.nl/assets&quot;&gt;Kenny asset life&lt;/a&gt;. I’m a huge fan of being able to focus on prototyping the gameplay.&lt;/p&gt;

&lt;p&gt;Moving forward I’d like to be able to make content (race tracks) quickly, so today I’m going to talk
about the system I made to transform an arbitrary curve in space into drivable geometry.&lt;/p&gt;

&lt;h2 id=&quot;process&quot;&gt;Process&lt;/h2&gt;

&lt;p&gt;I started a curve made with Houdini’s builtin curve SOP, which uses given points as anchors for a
Bezier curve (or other curve types). Unfortunately, the curve tool is pretty clunky compared to
others, and it might be easier to define elsewhere and bring in as a series of points.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-1.png&quot; alt=&quot;houdini procedural road input curve&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Input curve&lt;/p&gt;

&lt;p&gt;Next I sample points along the curve. Corners with tight curvature need a much higher resolution to
get smooth geometry, whereas straights can be a rectangle, so I used the facet SOP to consolidate
colinear points. Also I only want the point data, so I chopped out the line geometry.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-2.png&quot; alt=&quot;houdini procedural road resampling&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Resampled curve&lt;/p&gt;

&lt;p&gt;To transform the curve into a surface, I originally had tried sampling lines an using the sweep SOP,
but this twisted the surface uncontrollably when working in all 3 axes. Instead, I now (for each
point:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Get a vector &lt;code class=&quot;highlighter-rouge&quot;&gt;diffV&lt;/code&gt; along the curve. For a given point i, this can be done by averaging the
differences between i-1,i and i,i+1.&lt;/li&gt;
  &lt;li&gt;Generate a direction vector &lt;code class=&quot;highlighter-rouge&quot;&gt;crossDir&lt;/code&gt;, orthogonal to the road. I can get this with &lt;code class=&quot;highlighter-rouge&quot;&gt;diffV&lt;/code&gt; crossed with
the road surface’s normal (world up since the road shouldn’t twist)&lt;/li&gt;
  &lt;li&gt;Place new points at &lt;code class=&quot;highlighter-rouge&quot;&gt;pos(point_i) +/- crossDif * width&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;With the points in place, I can create triangles between every 3 points. I originally did this
manually, but the &lt;code class=&quot;highlighter-rouge&quot;&gt;poly patch&lt;/code&gt; SOP does this for me.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-3.png&quot; alt=&quot;houdini procedural road generation&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Points and lines orthogonal to the road, which form the road geometry when connected&lt;/p&gt;

&lt;p&gt;And it becomes a drivable surface! But I’d like to make this surface form ramps with walls, not just a
paper thin surface. So I follow up by extruding downwards, creating a bottom face. I do another
pass, projecting this bottom face onto the XZ ground plane.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-extrude.png&quot; alt=&quot;houdini procedural road extrude&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Extruding and projecting onto ground&lt;/p&gt;

&lt;p&gt;From here, it’s just some optimizations before exporting. I remove the faces on the ground, and
weld close points (mainly to clean up the extruded walls where the road is on the ground already),
and create smoothing groups.&lt;/p&gt;

&lt;h2 id=&quot;banking&quot;&gt;Banking&lt;/h2&gt;

&lt;p&gt;Unfortunately when I tried driving it in game, I had all sorts of problems. Collision detection
really struggled with the triangulation, but adding a suspension instead of a box collider fixed
most of the jank.&lt;/p&gt;

&lt;p&gt;Still, curves that move in 3 axes at once were problems. The car handles poorly on them, usually
flying off. And, once triangulated, the road would form “walls” because the inner side would rise
more steeply than the outer side (the Y/XZ ratio is greater).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-6.png&quot; alt=&quot;houdini procedural road problems&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Wall artifacts&lt;/p&gt;

&lt;p&gt;One solution to both is to try to bank the turns based on the tightness of the turn. I tried banking
by a function of the Y/XZ ratio (an estimate of curvature), which I smoothed over several points.
Unfortunately, I didn’t have a great way to twist the surface, so it ended up just
lowering/smoothing out the ramps instead of banking. It cleaned the walls but made for a dull road.&lt;/p&gt;

&lt;p&gt;My second approach was limiting the amount of climb, e.g. for every unit of XZ, the road can at most
climb some amount on the Y axis. Critically, I measure this value separately on the inner and outer
edges, so if the outer edge travels further, it can climb more.&lt;/p&gt;

&lt;p&gt;This means each point is a function of the previous point. In Houdini each
attribute wrangle can only access the attributes of others points before execution, so an iteration
like this requires a single pass over the whole object, tracking points in an array manually
iterating.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-banking.png&quot; alt=&quot;houdini procedural road banking&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Banking. Top image shows the difference between the original points and the banked result&lt;/p&gt;

&lt;p&gt;There are still a few artifacts that can be cleaned up, but you can see the surface banks a lot for
the tight turn and the walls have be substantially smoothed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-26-generating-road-from-curve-9.png&quot; alt=&quot;houdini procedural road in game&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;In Game&lt;/p&gt;
</description>
        <pubDate>Mon, 26 Mar 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/03/26/generating-road-from-curve/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/03/26/generating-road-from-curve/</guid>
        
        
      </item>
    
      <item>
        <title>Isolate</title>
        <description>&lt;p&gt;Released &lt;a href=&quot;https://github.com/seenaburns/isolate&quot;&gt;Isolate&lt;/a&gt; today, a snappy image browser I’ve been working on and using for a few months. The &lt;a href=&quot;https://github.com/seenaburns/isolate&quot;&gt;full source and prebuilt binaries is on Github&lt;/a&gt;.&lt;/p&gt;

&lt;video autoplay=&quot;&quot; loop=&quot;&quot; poster=&quot;/assets/2018-03-25-isolate.png&quot;&gt;
  &lt;source src=&quot;/assets/2018-03-25-isolate-demo.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  Your browser appears to not support HTML5 video mp4.
&lt;/video&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2018-03-25-isolate.png&quot; alt=&quot;isolate&quot; /&gt;
&lt;img src=&quot;/assets/2018-03-25-isolate-view.png&quot; alt=&quot;isolate view&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 25 Mar 2018 00:00:00 -0700</pubDate>
        <link>http://seenaburns.com/2018/03/25/isolate/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/03/25/isolate/</guid>
        
        
      </item>
    
      <item>
        <title>FZF.el: emacs + fzf</title>
        <description>&lt;p&gt;&lt;img src=&quot;/assets/2018-03-06-fzf-el.png&quot; alt=&quot;ripgrep fzf and emacs&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Ripgrep and FZF in Emacs&lt;/p&gt;

&lt;p&gt;One of the things I’ve missed in the Vim to Emacs migration
is solid recursive fuzzy file search. I tried &lt;a href=&quot;https://github.com/bling/fzf.el&quot;&gt;bling/fzf.el&lt;/a&gt;, and
ended up spending the weekend working on it.&lt;/p&gt;

&lt;p&gt;Rather than just work with fuzzy file search, I got way too into the possibility of using fzf as a
general completion engine. Fzf.el already had a decent way of invoking fzf (spawn a terminal buffer
and shell out) but the real potential is in the programmability.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/seenaburns/fzf.el/&quot;&gt;My code/fork&lt;/a&gt; exposes fzf by saying given a list and a
function, the user can select an item with FZF and it will be passed to the function. It becomes
trivial to run your own actions:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-lisp&quot;&gt;; Example
(fzf-with-entries (list &quot;a&quot; &quot;b&quot; &quot;c&quot;) 'print)

; Open recent file
(defun fzf-recentf ()
  (interactive)
  (fzf-with-entries recentf-list
    (lambda (f) (when (file-exists-p f) (find-file f))))
)

; Recursive fuzzy file search
; (resolve-directory will use the projectile root if set)
(defun fzf-find-file (&amp;amp;optional directory)
  (interactive)
  (let ((d (fzf/resolve-directory directory)))
    (fzf-base
    (lambda (x)
        (let ((f (expand-file-name x d)))
        (when (file-exists-p f)
            (find-file f))))
    d)))
&lt;/code&gt;&lt;/pre&gt;
</description>
        <pubDate>Tue, 06 Mar 2018 00:00:00 -0800</pubDate>
        <link>http://seenaburns.com/2018/03/06/emacs-fzf/</link>
        <guid isPermaLink="true">http://seenaburns.com/2018/03/06/emacs-fzf/</guid>
        
        
      </item>
    
  </channel>
</rss>
