<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://robocall.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://robocall.github.io//" rel="alternate" type="text/html" /><updated>2026-06-19T22:11:51+00:00</updated><id>https://robocall.github.io//feed.xml</id><title type="html">// commentary — notes on tech</title><subtitle>Welcome to my blog! I&apos;m interested in creative coding, data science, and technical writing.
</subtitle><author><name>jmj</name></author><entry><title type="html">Making sure what your code does you think it does, with Vet</title><link href="https://robocall.github.io//bouncing-balls" rel="alternate" type="text/html" title="Making sure what your code does you think it does, with Vet" /><published>2026-05-21T00:00:00+00:00</published><updated>2026-05-21T00:00:00+00:00</updated><id>https://robocall.github.io//bouncing-balls</id><content type="html" xml:base="https://robocall.github.io//bouncing-balls"><![CDATA[<p>The biggest issue I run into as a software engineer is when my code doesn’t do what I <em>think</em> it does.</p>

<p>I tried using <a href="https://github.com/imbue-ai/vet"><strong>Vet</strong></a> to solve this problem. Vet reviews your git diff plus a commit message, and asks an LLM whether your code change matches what you wanted.</p>

<p>In this walkthrough, I briefly go over how I set up and used vet to debug a deliberately broken p5.js sketch. Overall, I found it easy to use and surprising effective!</p>

<h2 id="the-sketch">The sketch</h2>

<p>I wanted the sketch to show a simple ball that bounces off the edges of the canvas.</p>

<p>On the right is the relevant p5.js code and on the left is the sketch working in the happy case. First, see if you can find the bug!</p>

<div class="bouncing-balls">
  <div id="canvas-wrap-square"></div>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="kd">function</span> <span class="nx">draw</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">background</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="mi">36</span><span class="p">);</span>

  <span class="nx">ball_position_x</span> <span class="o">+=</span> <span class="nx">ball_velocity_x</span><span class="p">;</span>
  <span class="nx">ball_position_y</span> <span class="o">+=</span> <span class="nx">ball_velocity_y</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="nx">ball_position_x</span> <span class="o">+</span> <span class="nx">ball_radius</span> <span class="o">&gt;</span> <span class="nx">width</span>
    <span class="o">||</span> <span class="nx">ball_position_x</span> <span class="o">-</span> <span class="nx">ball_radius</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">ball_velocity_x</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="nx">ball_position_y</span> <span class="o">+</span> <span class="nx">ball_radius</span> <span class="o">&gt;</span> <span class="nx">width</span>
    <span class="o">||</span> <span class="nx">ball_position_y</span> <span class="o">-</span> <span class="nx">ball_radius</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">ball_velocity_y</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">fill</span><span class="p">(</span><span class="mi">249</span><span class="p">,</span> <span class="mi">145</span><span class="p">,</span> <span class="mi">57</span><span class="p">);</span>
  <span class="nx">noStroke</span><span class="p">();</span>
  <span class="nx">circle</span><span class="p">(</span><span class="nx">ball_position_x</span><span class="p">,</span> <span class="nx">ball_position_y</span><span class="p">,</span> <span class="nx">ball_radius</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
</div>

<p>As a hint, here’s the unhappy case — the bug only shows up on a wide, short canvas. I liked this as an example since this is the perfect use case for Vet: code that seems to work but fails on edge cases.</p>

<div id="canvas-wrap-buggy"></div>

<p>If you found the bug in line 12, you’re right! The ball position on the y axis should check against height, not width.</p>

<p>Let’s see if Vet can find the bug.</p>

<h2 id="installing-and-running-vet">Installing and running Vet</h2>

<p>Vet is super easy to install.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>verify-everything
</code></pre></div></div>

<p>Vet also needs a model. I used a <a href="https://ai.google.dev/gemini-api/docs/quickstart">free Gemini key</a>. You can also use an Anthropic or OpenAI API key, or <code class="language-plaintext highlighter-rouge">--agentic</code> to route through Claude Code / Codex / OpenCode.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">GOOGLE_API_KEY</span><span class="o">=</span><span class="s2">"…"</span>

vet <span class="s2">"Bouncing ball that collides and bounces off the walls of the canvas"</span> <span class="se">\</span>
  <span class="nt">--model</span> gemini-2.5-flash
</code></pre></div></div>

<p>The “goal string” is what you’d write in a PR description, and Vet compares it to the diff.</p>

<p>Vet took 90 seconds to run on my M3 MacBook Pro. It returned:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>analyzing /Users/.../imbue (relative to HEAD)

🔴 Vet Issue `logic_error` *severity: 5/5*, *confidence: 1.00*
  bouncing-balls/sketch.js:26
  The vertical collision detection logic incorrectly compares `ball_position_y + ball_radius` against `width` instead of `height` for the bottom edge of the canvas. This causes the ball to pass beyond the visible bottom boundary on non-square canvases. This also means the user's request for a bouncing ball that "bounces off the walls of the canvas" is not fully met with correct functionality.
</code></pre></div></div>
<p>Vet found the bug! Vet itself doesn’t fix bugs, but you can ask your coding agent like Cursor to “fix the issue Vet flagged.” In my case, Cursor was able to fix it.</p>

<p>Vet also returned some other interesting flags. While the root cause of these errors is “mess” from my development process and writing this blog, I still think it’s interesting to look at, as an artifact for thinking about what else Vet is capable of.</p>

<p>Some of my setup files got pulled into my commit. Vet correctly flagged these as not relevant to the commit. Interestingly, “commit message mismatch” seems more about the scope of the PR, whereas the previous “logic error” captures actual bugs in the code.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>🔴 Vet Issue `commit_message_mismatch` *severity: 3/5*, *confidence: 1.00*
  The diff introduces numerous files related to the `vet` skill and its configuration across multiple agent harness directories. These additions are outside the scope of the user's request, which was specifically to implement a "bouncing ball that collides and bounces off the walls of the canvas".
</code></pre></div></div>

<p>I left a comment in my actual code to remind myself what the intended bug was, and Vet flagged it. In larger projects, this would be great as a linter / code hygiene.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>🔴 Vet Issue `user_request_artifacts_left_in_code` *severity: 1/5*, *confidence: 1.00*
  bouncing-balls/sketch.js:27
  The inline comment `// bug: lower edge should compare to height, not width` explicitly points out a known bug rather than describing the correct, intended behavior of the code. This is an artifact of the development or bug-finding process, not a description of the final, correct implementation.
</code></pre></div></div>

<p>I’m curious to see how different models perform with Vet, and testing Vet on a larger (open source?) codebase to see if it uncovers bugs that were previously unknown. Vet seems like a natural extension of fuzz testing, and I can see it finding interesting security/privacy bugs. Stay tuned for a follow-up blog post, and I would love to hear your use cases as well!</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/p5.min.js"></script>

<script src="/assets/posts/bouncing-balls/sketch.js" defer=""></script>]]></content><author><name>jmj</name></author><category term="vet" /><category term="ai" /><category term="debugging" /><category term="p5js" /><category term="interactive" /><summary type="html"><![CDATA[The biggest issue I run into as a software engineer is when my code doesn’t do what I think it does.]]></summary></entry><entry><title type="html">Against Substack and Closed Ecosystems</title><link href="https://robocall.github.io//substack" rel="alternate" type="text/html" title="Against Substack and Closed Ecosystems" /><published>2025-12-08T00:00:00+00:00</published><updated>2025-12-08T00:00:00+00:00</updated><id>https://robocall.github.io//substack</id><content type="html" xml:base="https://robocall.github.io//substack"><![CDATA[<p>In the year 1263 (give or take a historian yelling at me in the comments), there was a manor called Saint Algorithm.</p>

<p>The lord of Saint Algorithm—Lord Stack of Sub—announced with fanfare: any serf with a talent for words could claim a small plot near the village square and read their writings aloud each week. In return, the peasant would give the manor a modest tithe: ten shillings per hundred, plus the usual “don’t commit heresy on the premises” clause.</p>

<p>This was a big deal, because most peasants with a talent for words were previously stuck doing the traditional medieval content strategy: Not writing anything and doing backbreaking manual labor instead.</p>

<p>But Saint Algorithm had <em>distribution</em>.</p>

<p>When you use someone else’s platform, you are renting space on their land. At first, the rent is cheap. Sometimes it’s free. The landlord is nice. They provide everything you need. No startup cost! Free hosting! Free custom domain name! Recommendation graph to help readers discover you!</p>

<p>The landlord knows when enough people move in, they can’t leave. The power of a social graph is that each individual person is trapped by where their connections are. Your audience lives inside their walled garden, and you can’t take them with you. The switching costs are too high.</p>

<p>The landlord has all the power. Substack might let you export your posts and your subscribers now, but they could turn off that feature at any time. Everything they offer you as a “perk”, they also control - they could de-rank you or take down your site. Think back to a time before Twitter (“X”…) demoted tweets with out-links and shadow-banned individual accounts that wronged Elon Musk.</p>

<p>Closed ecosystems are flywheels. Why do people use Substack instead of Patreon? Patreon provides the same monetization model, but without the closed ecosystem. The ethos of Patreon is to support creators who create <em>elsewhere</em>. Creators build their audience and create their material on YouTube or podcasts or whatever; Patreon is just a “link in description.” Since the audience isn’t centralized on Patreon, Patreon is weak at getting creators discovered, which decreases the incentive for creators to join Patreon.</p>

<p>Why do people join closed ecosystems? For creators, it’s obviously convenience. Writers aren’t going to learn how to configure DNS records and Stripe webhooks. What about readers? Substack did a brilliant play by paying big creators like Slate Star Codex to write on their platform. Fans of SSC (yours truly included) of course created accounts to support him. Then the recommendation feed helped us discover other bloggers on Substack… pretty soon every morning became “coffee and Substack”.</p>

<p>At some point Substack can start taking a bigger cut and optimizing its algorithm for engagement vs quality. It might market this as “sustainability.” Later it becomes “industry standard.”</p>

<p>Owning your own site requires a lot of work, but it’s the only way to truly own what you make. Don’t be a Substack serf!</p>]]></content><author><name>jmj</name></author><category term="product" /><category term="fiction" /><summary type="html"><![CDATA[In the year 1263 (give or take a historian yelling at me in the comments), there was a manor called Saint Algorithm.]]></summary></entry><entry><title type="html">Why you can’t use vector calculus for color spaces</title><link href="https://robocall.github.io//colordotproduct" rel="alternate" type="text/html" title="Why you can’t use vector calculus for color spaces" /><published>2025-12-08T00:00:00+00:00</published><updated>2025-12-08T00:00:00+00:00</updated><id>https://robocall.github.io//colordotproduct</id><content type="html" xml:base="https://robocall.github.io//colordotproduct"><![CDATA[<p>I wanted a lazy way to autogenerate thumbnails that with brightly colored text that contrasted against a colored background.</p>

<p>I thought:</p>
<ul>
  <li>Colors are represented as vectors in RGB space</li>
  <li>Vector dot product tells you how different two vectors are</li>
</ul>

<p>I was hyped that vector calculus was actually useful for once. I wrote a script to generate random colors and check them against a dot product threshold. You may have predicted that this doesn’t work, but it’s kind of counterintuitive!</p>

<p>If RGB is a vector that represents color, why wouldn’t dot product, which represents the difference between vectors, work as “difference between colors”?</p>

<p>It’s because RGB values represent the amount of “red/green/blue light”, which is unrelated to “how the eye perceive readability.”</p>

<p>Ignoring blue for now, we’ll look at examples red-green space.
Below are examples:</p>
<ul>
  <li>with small dot products: green text on red background (readable); and dark text on dark background (not readable).</li>
  <li>with large dot products: yellow text on dark background (readable); and two similar reds (not readable)</li>
</ul>

<div style="position: relative; display: inline-block;">

<div style="display:flex;gap:20px">
<canvas id="vectorMap" width="256" height="256"></canvas>
<svg id="vectorSVG" width="256" height="256" style="position: absolute; top: 0; left: 0; pointer-events: none;"></svg>


<div style="margin-bottom: 10px;">
    <button id="example1" style="margin-right: 10px; padding: 5px 10px;font-family:inherit">Example 1: Small dot product, good
      contrast</button>
    <button id="example2" style="margin-right: 10px; padding: 5px 10px;font-family:inherit">Example 2: Small dot product, good
      contrast</button>
    <button id="example3" style="margin-right: 10px; padding: 5px 10px;font-family:inherit">Example 3: Large dot product, poor
      contrast</button>
    <button id="example4" style="margin-right: 10px; padding: 5px 10px;font-family:inherit">Example 4: Small dot product, poor
      contrast</button>
</div>

</div>

  <div id="exampleDisplay" style="margin-top: 10px; padding: 10px; min-height: 50px; background: transparent;"></div>
</div>

<script src="/blogpostscode/color.js"></script>

<p>So RGB vectors aren’t very meaningful by themselves. The correct calculation here would be luminance, which is the perceived brightness of an RGB color, and calculated by (0.2126<em>R + 0.7152</em>G + 0.0722*B). The magic constants are derived from the colors that the human eye is most sensitive to.</p>]]></content><author><name>jmj</name></author><category term="code" /><category term="interactive" /><summary type="html"><![CDATA[I wanted a lazy way to autogenerate thumbnails that with brightly colored text that contrasted against a colored background.]]></summary></entry></feed>