<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Andrew Dickson</title>
    <description>My website for code, info, and a few projects. 
</description>
    <link>http://example.com/</link>
    <atom:link href="http://example.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 14 Aug 2026 22:18:55 +0000</pubDate>
    <lastBuildDate>Fri, 14 Aug 2026 22:18:55 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>kMC Crystal Simulator</title>
        <description>&lt;h1 id=&quot;i-built-something&quot;&gt;I built something&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;/docs/index.html&quot;&gt;kMC Crystal&lt;/a&gt; is a general simulator for 2D crystal growth, under some basic simplifications. It can grow almost any design, as long as it can be specified in terms of local particle interactions, including non-periodic and off-lattice crystals. For instance, an infinite Sierpinski triangle.&lt;/p&gt;

&lt;div style=&quot;display:flex; gap:16px; align-items:flex-start;&quot;&gt;
  &lt;img src=&quot;/assets/triangle.png&quot; style=&quot;width:48%;&quot; /&gt;
  &lt;img src=&quot;/assets/triangle2.png&quot; style=&quot;width:48%;&quot; /&gt;
&lt;/div&gt;

&lt;h1 id=&quot;motivation&quot;&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Outside of drawing pretty pictures, I built this out of an interest in the general field of self-assembling molecules. In particular, self-assembling proteins and DNA, which are rapidly, and astonishingly, becoming plausibly useful.&lt;/p&gt;

&lt;p&gt;Depending on your perspective, proteins are either massive molecules, or very small nano-machines. And for the first time ever, we’re getting very good at designing them. We’re approaching a future where we can design custom proteins with custom shapes, which associate with each other according to arbitrary rules, and build arbitrary structures on the scale of nanometers.&lt;/p&gt;

&lt;p&gt;If you’re familiar with the nano-machines that already exist in biology, you’ll understand how exciting this is. Examples like microtubules, flagelli motors, or nuclear pore complexes, are coordinated structures of hundreds or thousands of proteins, so old and optimized by evolution that they feel like computer designed cathedrals. The possibility of generating comparable structures artificially is remarkable.&lt;/p&gt;

&lt;p&gt;There’s some existing work on building protein structures. Baker Lab, of course, has designed proteins which can self-assemble into crystal structures, or geometric shapes. And there’s significant comparable work in designing DNA sequences origami, using the extremely selective pairwise interactions between DNA strands and their reverse complements to design nano-molecules that naturally grow into structures.&lt;/p&gt;

&lt;p&gt;However, almost all existing work is focused on engineering a few molecules at the residue or nucleotide level. I think it’s worth jumping ahead a few years, and asking what it looks like when we’re so good at molecule design that we can simply specify molecule shapes and interactions and compile a set of particles. Even with no other capabilities, this is enough to generate complex, fascinating behaviors.&lt;/p&gt;

&lt;p&gt;This simulator is a statistical sampler which produces the crystal structures that would result from particle interactions. It can simulate the platonic solids or lattice grids already being produced, but it can also simulate the much larger, hypothetical, structures that could be built in a few years time. In a plausible future, something like this would be an essential nano-engineering tool, but for now it’s mostly just fun.&lt;/p&gt;

&lt;h1 id=&quot;simulation-formalism&quot;&gt;Simulation Formalism&lt;/h1&gt;
&lt;p&gt;There are many particle simulators, but this one is mine. It is designed to simulate the statistics of crystal growth under arbitrary particle interaction profiles, under the assumption of a “mostly” static crystal structure.&lt;/p&gt;

&lt;p&gt;While a truly correct simulation would run something like a Langevin dynamics simulation of interacting particles in a solute e.g. HOOMD, this is prohibitively expensive for space and time scales of large-structure crystal growth (in-browser). At the same time, Wang tile based simulations, such as kTAM or aTAM, are too restrictive for the geometries I want.&lt;/p&gt;

&lt;p&gt;I simulate crystal growth using a kinetic Monte Carlo scheme in which the attachment rates of new particles to the crystal structure, and detachment rates of existing particles, are tracked approximately, and then sampled from according to transition rates proportional to \(\exp(\frac{-\Delta G}{K_B T})\), where \(\Delta G\) is total binding energy of the particle to its new neighbors. This means the simulation only runs computations for events which modify the crystal structure, with no computation spent on simulating particles outside of the structure in between attachment or detachment events.&lt;/p&gt;

&lt;p&gt;Attachment events are sampled hierarchically, with the simulation first selecting a root particle for the attachment, and then sampling from potential positions and orientations of an adjacent new particle according to their estimated \(\Delta G\). For efficiency, I assume the addition of a new particle will not significantly change the crystal structure while estimating \(\Delta G\), which may significantly bias sampling statistics for flexible structures. Fully accurate sampling from the hierarchical scheme would also require implementing rejection sampling to avoid biasing by inaccuracies in the root particle attachment rate, and to avoid double-counting particle attachment events that could be rooted in multiple particles simultaneously. I leave this out, both to keep simulation speed as high as possible and because it’s too much work for a side-project. If you’re interested in using this simulator for a scenario in which this would be important, please contact me.&lt;/p&gt;

&lt;h1 id=&quot;patchy-particles&quot;&gt;Patchy-Particles&lt;/h1&gt;

&lt;p&gt;Each simulated particle carries a set of directional binding sites called patches. A patch is defined by its angular position in the particle’s body frame and a named type. When the particle rotates, all of its patches rotate with it, so the geometry of a particle is fully determined by its orientation angle together with the fixed body-frame positions of its patches.&lt;/p&gt;

&lt;h2 id=&quot;interaction-potential&quot;&gt;Interaction Potential&lt;/h2&gt;

&lt;p&gt;Two nearby particles interact through every pair of patches they carry. For a given patch pair, the energy has the form&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;U = ε · bump(r) · g(θ_i) · g(θ_j)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The radial factor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bump(r)&lt;/code&gt; is a smooth, compactly supported function that equals 1 at contact distance and falls to zero at a configurable cutoff radius. It is strictly zero outside that support, so patch interactions are short-ranged. The angular factors &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g(θ) = exp(-(1 - cos θ) / σ²)&lt;/code&gt; measure how well each patch points toward the other particle: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;θ_i&lt;/code&gt; is the angle between patch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;’s direction and the inter-particle axis, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;σ&lt;/code&gt; is a per-side angular half-width. When both patches point directly at each other, both angular factors equal 1 and the full well depth &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ε&lt;/code&gt; is realized. Misalignment suppresses the energy exponentially, so a patch pair only contributes meaningfully when both particles are simultaneously well-oriented.&lt;/p&gt;

&lt;p&gt;Backbone repulsion is always present independently of patches, handled by a purely repulsive hinge potential. Patch interactions add an orientation-dependent attractive correction on top of that floor.&lt;/p&gt;

&lt;h2 id=&quot;implications-for-self-assembly&quot;&gt;Implications for Self-Assembly&lt;/h2&gt;

&lt;p&gt;Because attraction only occurs when patches on opposite particles are co-aligned, the geometry of patches on each particle type directly encodes the binding rules of the intended crystal. A particle with four patches at 0°, 90°, 180°, and 270° will preferentially bind along four perpendicular directions, generating a square lattice. More complex arrangements such as staggered, chevron, or spiral, follow directly from the patch geometry and the interaction table, without any additional logic in the simulator itself. The entire design space of patchy-particle crystals is therefore expressed through configuration.&lt;/p&gt;

&lt;h1 id=&quot;using-the-interface&quot;&gt;Using the Interface&lt;/h1&gt;

&lt;h2 id=&quot;editor&quot;&gt;Editor&lt;/h2&gt;

&lt;p&gt;The editor (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;editor.html&lt;/code&gt;) is a three-panel design tool for building particle configurations without hand-editing JSON.&lt;/p&gt;

&lt;p&gt;The left panel manages patch types and their interactions. Each patch type has a name and a color. The interaction table below the palette shows every pair of patch types; clicking a cell lets you set the well depth ε, the per-side angular half-widths, and the radial cutoff for that pair. Only enabled pairs participate in the energy.&lt;/p&gt;

&lt;p&gt;The center panel is the particle type designer. Each particle type appears as a card with a circular canvas preview showing the particle body and its patches as colored dots. Dragging a patch token from the left panel onto a card adds that patch type to the particle; dragging an existing patch on the card rotates it. A snap toggle constrains angles to multiples of 15°. Each card also has fields for the particle radius, display color, and chemical potential μ, which controls how strongly that species is driven to attach.&lt;/p&gt;

&lt;p&gt;The right panel is a live physics preview. Particle type cards from the center panel can be dragged and dropped onto the preview canvas to place instances. The WASM engine runs a continuous force-and-torque relaxation so placed particles immediately settle into their lowest-energy orientations. This lets you verify that a proposed patch layout produces the binding geometry you intend before committing it to a full simulation.&lt;/p&gt;

&lt;p&gt;When the design is ready, the Export button downloads the configuration as a JSON file. The Simulate button pushes the current configuration directly to an open simulator tab via a broadcast channel and reopens that tab, so no file round-trip is needed.&lt;/p&gt;

&lt;h2 id=&quot;simulator&quot;&gt;Simulator&lt;/h2&gt;

&lt;p&gt;The simulator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;) runs the KMC engine and renders the growing crystal in real time. A sidebar provides runtime controls. The steps-per-frame slider sets how many KMC events are processed between renders, trading visual smoothness for throughput. The temperature slider adjusts kT live and takes effect on the next event. Per-species chemical potential sliders appear automatically based on the loaded configuration and let you drive or suppress individual species independently.&lt;/p&gt;

&lt;p&gt;A stats readout shows the current particle count, the accumulated KMC time, and the render frame rate. The Pause and Step buttons stop or advance the simulation one batch at a time. Reset reinitialises the simulation from the current configuration without reloading the page.&lt;/p&gt;

&lt;p&gt;The config panel at the bottom of the sidebar lets you switch between built-in preset configurations or paste and apply arbitrary JSON directly in the browser.&lt;/p&gt;
</description>
        <pubDate>Fri, 10 Apr 2026 22:04:06 +0000</pubDate>
        <link>http://example.com/blog/crystals/</link>
        <guid isPermaLink="true">http://example.com/blog/crystals/</guid>
        
        
        <category>jekyll</category>
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>Skill Leverage in Game Design</title>
        <description>&lt;p&gt;Every time you play a game, part of the fun is getting better. But, the definition of “better” is trickier than I first thought, and there are some interesting implications for game design.&lt;/p&gt;

&lt;p&gt;How do you measure your skill in a game? The obvious answer is to just measure your success. If you can beat skyrim in 2 hours, get to level 30 in tetris, or beat competitive chess players, you know you’re good. However, that’s not all there is to the story. In some sense, you can get significantly more skilled at a game, before feeling the corresponding success. I’d argue there’s value in drawing an explicit distinction between your &lt;em&gt;skill&lt;/em&gt; and your &lt;em&gt;success&lt;/em&gt;. The correlation between these is the degree to which your game &lt;em&gt;rewards&lt;/em&gt; skill, or what I’d call &lt;strong&gt;skill leverage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So if skill isn’t just success, what’s the obvious alternative? Well, you can always break the game down and measure success in small chunks of the game. For instance, in Super Mario, you could track your progress in making precise jumps. Or in tetris, you could see how long you can keep a combo going. Usually these correlate well with actual results, but not always. In the worst case, you could imagine a game in which the only task is to press a spacebar after ten seconds with microsecond precision. On success, you’re rewarded, on failure, you’re simply told you failed, with no other feedback.&lt;/p&gt;

&lt;p&gt;Obviously a pretty bad game, partially because you’d never know how good you were. There’d be no in-game reward for working your way all the way up from pressing the spacebar at 9.5 seconds to pressing it at 9.99 seconds, and in fact, no actual way to tell that you were better at all. It’s hard to imagine a more frustrating experience. Once you can see the clock, and know how close you are to winning though, it’s actually a pretty fun bar game. Real humans have payed money to try it.&lt;/p&gt;

&lt;p&gt;Once you start thinking in terms of the distinction between skill and results, there are all sorts of game machinery that suddenly make logical sense. One that used to confuse me is mechanics that reward players for already being good, like the doors in Dead Cells that only give you extra loot if you can make it through a level without ever taking damage. When I first saw them, I thought “why help players win even more?”. You’d expect it to be more useful to help struggling players, than to help ones already good enough at the game to get through a level unhit.&lt;/p&gt;

&lt;p&gt;But, loot with a win-condition actually just makes the skill -&amp;gt; performance slope a little steeper. A reasonable improvement in skill (dodging well enough to no-hit a level) gives you a direct bonus in the game. The effect is that when you get a little better, you do a lot better, which feels pretty good. And the alternative, in which you get bonuses to help you when you’re bad, actually goes very wrong. You end up in situations where improving actually has a bit of a negative impact on your result, which is obviously frustrating.&lt;/p&gt;
</description>
        <pubDate>Fri, 28 Oct 2022 22:04:06 +0000</pubDate>
        <link>http://example.com/blog/skill-leverage/</link>
        <guid isPermaLink="true">http://example.com/blog/skill-leverage/</guid>
        
        
        <category>jekyll</category>
        
        <category>update</category>
        
      </item>
    
      <item>
        <title>First Post</title>
        <description>&lt;p&gt;Just a little test post. I’ll take this down when I have something else to copy as a template.&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Mar 2022 22:04:06 +0000</pubDate>
        <link>http://example.com/blog/first-post/</link>
        <guid isPermaLink="true">http://example.com/blog/first-post/</guid>
        
        
        <category>jekyll</category>
        
        <category>update</category>
        
      </item>
    
  </channel>
</rss>
